Ignore:
Timestamp:
Nov 14, 2022, 11:52:44 AM (3 years ago)
Author:
caparson <caparson@…>
Branches:
ADT, ast-experimental, master
Children:
7d9598d8
Parents:
b77f0e1 (diff), 19a8c40 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/heap.cfa

    rb77f0e1 r63be3387  
    1010// Created On       : Tue Dec 19 21:58:35 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Oct 13 22:21:52 2022
    13 // Update Count     : 1557
     12// Last Modified On : Sun Oct 30 20:56:20 2022
     13// Update Count     : 1584
    1414//
    1515
     
    4343
    4444#define FASTLOOKUP                                                                              // use O(1) table lookup from allocation size to bucket size
    45 #define RETURNSPIN                                                                              // toggle spinlock / lockfree stack
    4645#define OWNERSHIP                                                                               // return freed memory to owner thread
     46#define RETURNSPIN                                                                              // toggle spinlock / lockfree queue
     47#if ! defined( OWNERSHIP ) && defined( RETURNSPIN )
     48#warning "RETURNSPIN is ignored without OWNERSHIP; suggest commenting out RETURNSPIN"
     49#endif // ! OWNERSHIP && RETURNSPIN
    4750
    4851#define CACHE_ALIGN 64
     
    109112
    110113
    111 //######################### Spin Lock #########################
     114//######################### Helpers #########################
     115
     116
     117// generic Bsearchl does not inline, so substitute with hand-coded binary-search.
     118inline __attribute__((always_inline))
     119static size_t Bsearchl( unsigned int key, const unsigned int vals[], size_t dim ) {
     120        size_t l = 0, m, h = dim;
     121        while ( l < h ) {
     122                m = (l + h) / 2;
     123                if ( (unsigned int &)(vals[m]) < key ) {                // cast away const
     124                        l = m + 1;
     125                } else {
     126                        h = m;
     127                } // if
     128        } // while
     129        return l;
     130} // Bsearchl
    112131
    113132
     
    206225
    207226
    208 #define SPINLOCK 0
    209 #define LOCKFREE 1
    210 #define BUCKETLOCK SPINLOCK
    211 #if BUCKETLOCK == SPINLOCK
    212 #elif BUCKETLOCK == LOCKFREE
    213 #include <stackLockFree.hfa>
    214 #else
    215         #error undefined lock type for bucket lock
    216 #endif // LOCKFREE
    217 
    218227// Recursive definitions: HeapManager needs size of bucket array and bucket area needs sizeof HeapManager storage.
    219228// Break recursion by hardcoding number of buckets and statically checking number is correct after bucket array defined.
     
    232241                                                                void * home;                    // allocated block points back to home locations (must overlay alignment)
    233242                                                                size_t blockSize;               // size for munmap (must overlay alignment)
    234                                                                 #if BUCKETLOCK == SPINLOCK
    235243                                                                Storage * next;                 // freed block points to next freed block of same size
    236                                                                 #endif // SPINLOCK
    237244                                                        };
    238245                                                        size_t size;                            // allocation size in bytes
    239246                                                };
    240                                                 #if BUCKETLOCK == LOCKFREE
    241                                                 Link(Storage) next;                             // freed block points next freed block of same size (double-wide)
    242                                                 #endif // LOCKFREE
    243247                                        };
    244248                                } real; // RealHeader
     
    259263        struct __attribute__(( aligned (8) )) FreeHeader {
    260264                size_t blockSize __attribute__(( aligned(8) )); // size of allocations on this list
    261                 #if BUCKETLOCK == SPINLOCK
    262265                #ifdef OWNERSHIP
    263266                #ifdef RETURNSPIN
     
    266269                Storage * returnList;                                                   // other thread return list
    267270                #endif // OWNERSHIP
     271
    268272                Storage * freeList;                                                             // thread free list
    269                 #else
    270                 StackLF(Storage) freeList;
    271                 #endif // BUCKETLOCK
    272273                Heap * homeManager;                                                             // heap owner (free storage to bucket, from bucket to heap)
    273274        }; // FreeHeader
     
    290291        #endif // __STATISTICS__
    291292}; // Heap
    292 
    293 #if BUCKETLOCK == LOCKFREE
    294 inline __attribute__((always_inline))
    295 static {
    296         Link(Heap.Storage) * ?`next( Heap.Storage * this ) { return &this->header.kind.real.next; }
    297         void ?{}( Heap.FreeHeader & ) {}
    298         void ^?{}( Heap.FreeHeader & ) {}
    299 } // distribution
    300 #endif // LOCKFREE
    301293
    302294
     
    385377
    386378
    387 // generic Bsearchl does not inline, so substitute with hand-coded binary-search.
    388 inline __attribute__((always_inline))
    389 static size_t Bsearchl( unsigned int key, const unsigned int vals[], size_t dim ) {
    390         size_t l = 0, m, h = dim;
    391         while ( l < h ) {
    392                 m = (l + h) / 2;
    393                 if ( (unsigned int &)(vals[m]) < key ) {                // cast away const
    394                         l = m + 1;
    395                 } else {
    396                         h = m;
    397                 } // if
    398         } // while
    399         return l;
    400 } // Bsearchl
    401 
    402 
    403379void heapMasterCtor() with( heapMaster ) {
    404380        // Singleton pattern to initialize heap master
     
    409385        __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC;
    410386
    411         ?{}( extLock );
    412         ?{}( mgrLock );
     387        extLock = 0;
     388        mgrLock = 0;
    413389
    414390        char * end = (char *)sbrk( 0 );
     
    497473                                #ifdef OWNERSHIP
    498474                                #ifdef RETURNSPIN
    499                                 ?{}( freeLists[j].returnLock );
     475                                freeLists[j].returnLock = 0;
     476                                freeLists[j].returnList = 0p;
    500477                                #endif // RETURNSPIN
    501                                 freeLists[j].returnList = 0p;
    502478                                #endif // OWNERSHIP
     479
    503480                                freeLists[j].freeList = 0p;
    504481                                freeLists[j].homeManager = heap;
    505482                                freeLists[j].blockSize = bucketSizes[j];
    506483                        } // for
    507        
     484
    508485                        heapBuffer = 0p;
    509486                        heapReserve = 0;
     
    522499        if ( unlikely( ! heapMasterBootFlag ) ) heapMasterCtor();
    523500
    524         lock( heapMaster.mgrLock );             // protect heapMaster counters
     501        lock( heapMaster.mgrLock );                                                     // protect heapMaster counters
    525502
    526503        // get storage for heap manager
     
    710687        // find the closest bucket size less than or equal to the mmapStart size
    711688        maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search
     689
    712690        verify( maxBucketsUsed < NoBucketSizes );                       // subscript failure ?
    713691        verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?
     
    832810
    833811                size_t increase = ceiling2( size > heapExpand ? size : heapExpand, libAlign() );
    834                 // Do not call abort or strerror( errno ) as they may call malloc.
    835812                if ( unlikely( sbrk( increase ) == (void *)-1 ) ) {     // failed, no memory ?
    836813                        unlock( extLock );
    837                         abort( NO_MEMORY_MSG, size );                           // no memory
     814                        abort( NO_MEMORY_MSG, size );                           // give up
    838815                } // if
    839816
     
    971948                #endif // __STATISTICS__
    972949
    973                 // Spin until the lock is acquired for this particular size of block.
    974 
    975                 #if BUCKETLOCK == SPINLOCK
    976950                block = freeHead->freeList;                                             // remove node from stack
    977                 #else
    978                 block = pop( freeHead->freeList );
    979                 #endif // BUCKETLOCK
    980951                if ( unlikely( block == 0p ) ) {                                // no free block ?
     952                        // Freelist for this size is empty, so check return list (OWNERSHIP), carve it out of the heap, if there
     953                        // is enough left, or get some more heap storage and carve it off.
    981954                        #ifdef OWNERSHIP
    982                         // Freelist for that size is empty, so carve it out of the heap, if there is enough left, or get some more
    983                         // and then carve it off.
    984                         #ifdef RETURNSPIN
    985                         #if BUCKETLOCK == SPINLOCK
    986                         lock( freeHead->returnLock );
    987                         block = freeHead->returnList;
    988                         freeHead->returnList = 0p;
    989                         unlock( freeHead->returnLock );
    990                         #else
    991                         block = __atomic_exchange_n( &freeHead->returnList, nullptr, __ATOMIC_SEQ_CST );
    992                         #endif // RETURNSPIN
    993 
    994                         if ( likely( block == 0p ) ) {                  // return list also empty?
     955                        if ( unlikely( freeHead->returnList ) ) {       // race, get next time if lose race
     956                                #ifdef RETURNSPIN
     957                                lock( freeHead->returnLock );
     958                                block = freeHead->returnList;
     959                                freeHead->returnList = 0p;
     960                                unlock( freeHead->returnLock );
     961                                #else
     962                                block = __atomic_exchange_n( &freeHead->returnList, 0p, __ATOMIC_SEQ_CST );
     963                                #endif // RETURNSPIN
     964
     965                                verify( block );
     966                                #ifdef __STATISTICS__
     967                                stats.return_pulls += 1;
     968                                #endif // __STATISTICS__
     969
     970                                // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
     971
     972                                freeHead->freeList = block->header.kind.real.next; // merge returnList into freeHead
     973                        } else {
    995974                        #endif // OWNERSHIP
    996975                                // Do not leave kernel thread as manager_extend accesses heapManager.
     
    1002981
    1003982                                #ifdef __CFA_DEBUG__
    1004                                 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes.
     983                                // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first SCRUB_SIZE bytes.
    1005984                                memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) );
    1006985                                #endif // __CFA_DEBUG__
    1007                         #endif // BUCKETLOCK
    1008986                        #ifdef OWNERSHIP
    1009                         } else {                                                                        // merge returnList into freeHead
    1010                                 #ifdef __STATISTICS__
    1011                                 stats.return_pulls += 1;
    1012                                 #endif // __STATISTICS__
    1013 
    1014                                 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
    1015 
    1016                                 freeHead->freeList = block->header.kind.real.next;
    1017987                        } // if
    1018988                        #endif // OWNERSHIP
     
    1026996  if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p;
    1027997                tsize = ceiling2( tsize, __page_size );                 // must be multiple of page size
     998
    1028999                #ifdef __STATISTICS__
    10291000                stats.counters[STAT_NAME].alloc += tsize;
     
    10421013                        if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory
    10431014                        // Do not call strerror( errno ) as it may call malloc.
    1044                         abort( "**** Error **** attempt to allocate large object (> %zu) of size %zu bytes and mmap failed with errno %d.", size, heapMaster.mmapStart, errno );
     1015                        abort( "**** Error **** attempt to allocate large object (> %zu) of size %zu bytes and mmap failed with errno %d.",
     1016                                   size, heapMaster.mmapStart, errno );
    10451017                } // if
    10461018                block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap
    10471019
    10481020                #ifdef __CFA_DEBUG__
    1049                 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes.  The rest of
    1050                 // the storage set to 0 by mmap.
     1021                // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first SCRUB_SIZE bytes. The
     1022                // rest of the storage set to 0 by mmap.
    10511023                memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) );
    10521024                #endif // __CFA_DEBUG__
     
    11261098                #endif // __CFA_DEBUG__
    11271099
     1100                #ifdef OWNERSHIP
    11281101                if ( likely( heapManager == freeHead->homeManager ) ) { // belongs to this thread
    11291102                        header->kind.real.next = freeHead->freeList; // push on stack
     
    11321105                        verify( heapManager );
    11331106
    1134                         #ifdef OWNERSHIP
    11351107                        #ifdef RETURNSPIN
    11361108                        lock( freeHead->returnLock );
     
    11411113                        header->kind.real.next = freeHead->returnList; // link new node to top node
    11421114                        // CAS resets header->kind.real.next = freeHead->returnList on failure
    1143                         while ( ! __atomic_compare_exchange_n( &freeHead->returnList, &header->kind.real.next, header,
     1115                        while ( ! __atomic_compare_exchange_n( &freeHead->returnList, &header->kind.real.next, (Heap.Storage *)header,
    11441116                                                                                                   false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) );
    11451117                        #endif // RETURNSPIN
    1146 
    1147                         #else                                                                           // no OWNERSHIP
    1148 
    1149                         freeHead = &heap->freeLists[ClearStickyBits( header->kind.real.home ) - &freeHead->homeManager->freeLists[0]];
    1150                         header->kind.real.next = freeHead->freeList; // push on stack
    1151                         freeHead->freeList = (Heap.Storage *)header;
    1152                         #endif // ! OWNERSHIP
    1153 
    1154                         #ifdef __U_STATISTICS__
    1155                         stats.return_pushes += 1;
    1156                         stats.return_storage_request += rsize;
    1157                         stats.return_storage_alloc += size;
    1158                         #endif // __U_STATISTICS__
    1159 
    1160                         // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
    1161                 } // if
     1118                } // if
     1119
     1120                #else                                                                                   // no OWNERSHIP
     1121
     1122                // kind.real.home is address in owner thread's freeLists, so compute the equivalent position in this thread's freeList.
     1123                freeHead = &freeLists[ClearStickyBits( (Heap.FreeHeader *)(header->kind.real.home) ) - &freeHead->homeManager->freeLists[0]];
     1124                header->kind.real.next = freeHead->freeList;    // push on stack
     1125                freeHead->freeList = (Heap.Storage *)header;
     1126                #endif // ! OWNERSHIP
     1127
     1128                #ifdef __U_STATISTICS__
     1129                stats.return_pushes += 1;
     1130                stats.return_storage_request += rsize;
     1131                stats.return_storage_alloc += size;
     1132                #endif // __U_STATISTICS__
     1133
     1134                // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
    11621135        } // if
    11631136
     
    11861159                #endif // __STATISTICS__
    11871160
    1188                 #if BUCKETLOCK == SPINLOCK
    11891161                for ( Heap.Storage * p = freeLists[i].freeList; p != 0p; p = p->header.kind.real.next ) {
    1190                 #else
    1191                         for(;;) {
    1192 //              for ( Heap.Storage * p = top( freeLists[i].freeList ); p != 0p; p = (p)`next->top ) {
    1193 //              for ( Heap.Storage * p = top( freeLists[i].freeList ); p != 0p; /* p = getNext( p )->top */) {
    1194 //                      Heap.Storage * temp = p->header.kind.real.next.top; // FIX ME: direct assignent fails, initialization works`
    1195 //                      typeof(p) temp = (( p )`next)->top;                     // FIX ME: direct assignent fails, initialization works`
    1196 //                      p = temp;
    1197                 #endif // BUCKETLOCK
    11981162                        total += size;
    11991163                        #ifdef __STATISTICS__
Note: See TracChangeset for help on using the changeset viewer.