Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/heap.cfa

    r0bdfcc3 r80fbdc9  
    1010// Created On       : Tue Dec 19 21:58:35 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Oct 30 20:56:20 2022
    13 // Update Count     : 1584
     12// Last Modified On : Thu Oct 13 22:21:52 2022
     13// Update Count     : 1557
    1414//
    1515
     
    4343
    4444#define FASTLOOKUP                                                                              // use O(1) table lookup from allocation size to bucket size
     45#define RETURNSPIN                                                                              // toggle spinlock / lockfree stack
    4546#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
    5047
    5148#define CACHE_ALIGN 64
     
    112109
    113110
    114 //######################### Helpers #########################
    115 
    116 
    117 // generic Bsearchl does not inline, so substitute with hand-coded binary-search.
    118 inline __attribute__((always_inline))
    119 static 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
     111//######################### Spin Lock #########################
    131112
    132113
     
    225206
    226207
     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
    227218// Recursive definitions: HeapManager needs size of bucket array and bucket area needs sizeof HeapManager storage.
    228219// Break recursion by hardcoding number of buckets and statically checking number is correct after bucket array defined.
     
    241232                                                                void * home;                    // allocated block points back to home locations (must overlay alignment)
    242233                                                                size_t blockSize;               // size for munmap (must overlay alignment)
     234                                                                #if BUCKETLOCK == SPINLOCK
    243235                                                                Storage * next;                 // freed block points to next freed block of same size
     236                                                                #endif // SPINLOCK
    244237                                                        };
    245238                                                        size_t size;                            // allocation size in bytes
    246239                                                };
     240                                                #if BUCKETLOCK == LOCKFREE
     241                                                Link(Storage) next;                             // freed block points next freed block of same size (double-wide)
     242                                                #endif // LOCKFREE
    247243                                        };
    248244                                } real; // RealHeader
     
    263259        struct __attribute__(( aligned (8) )) FreeHeader {
    264260                size_t blockSize __attribute__(( aligned(8) )); // size of allocations on this list
     261                #if BUCKETLOCK == SPINLOCK
    265262                #ifdef OWNERSHIP
    266263                #ifdef RETURNSPIN
     
    269266                Storage * returnList;                                                   // other thread return list
    270267                #endif // OWNERSHIP
    271 
    272268                Storage * freeList;                                                             // thread free list
     269                #else
     270                StackLF(Storage) freeList;
     271                #endif // BUCKETLOCK
    273272                Heap * homeManager;                                                             // heap owner (free storage to bucket, from bucket to heap)
    274273        }; // FreeHeader
     
    291290        #endif // __STATISTICS__
    292291}; // Heap
     292
     293#if BUCKETLOCK == LOCKFREE
     294inline __attribute__((always_inline))
     295static {
     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
    293301
    294302
     
    377385
    378386
     387// generic Bsearchl does not inline, so substitute with hand-coded binary-search.
     388inline __attribute__((always_inline))
     389static 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
    379403void heapMasterCtor() with( heapMaster ) {
    380404        // Singleton pattern to initialize heap master
     
    385409        __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC;
    386410
    387         extLock = 0;
    388         mgrLock = 0;
     411        ?{}( extLock );
     412        ?{}( mgrLock );
    389413
    390414        char * end = (char *)sbrk( 0 );
     
    473497                                #ifdef OWNERSHIP
    474498                                #ifdef RETURNSPIN
    475                                 freeLists[j].returnLock = 0;
     499                                ?{}( freeLists[j].returnLock );
     500                                #endif // RETURNSPIN
    476501                                freeLists[j].returnList = 0p;
    477                                 #endif // RETURNSPIN
    478502                                #endif // OWNERSHIP
    479 
    480503                                freeLists[j].freeList = 0p;
    481504                                freeLists[j].homeManager = heap;
    482505                                freeLists[j].blockSize = bucketSizes[j];
    483506                        } // for
    484 
     507       
    485508                        heapBuffer = 0p;
    486509                        heapReserve = 0;
     
    499522        if ( unlikely( ! heapMasterBootFlag ) ) heapMasterCtor();
    500523
    501         lock( heapMaster.mgrLock );                                                     // protect heapMaster counters
     524        lock( heapMaster.mgrLock );             // protect heapMaster counters
    502525
    503526        // get storage for heap manager
     
    687710        // find the closest bucket size less than or equal to the mmapStart size
    688711        maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search
    689 
    690712        verify( maxBucketsUsed < NoBucketSizes );                       // subscript failure ?
    691713        verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?
     
    810832
    811833                size_t increase = ceiling2( size > heapExpand ? size : heapExpand, libAlign() );
     834                // Do not call abort or strerror( errno ) as they may call malloc.
    812835                if ( unlikely( sbrk( increase ) == (void *)-1 ) ) {     // failed, no memory ?
    813836                        unlock( extLock );
    814                         abort( NO_MEMORY_MSG, size );                           // give up
     837                        abort( NO_MEMORY_MSG, size );                           // no memory
    815838                } // if
    816839
     
    948971                #endif // __STATISTICS__
    949972
     973                // Spin until the lock is acquired for this particular size of block.
     974
     975                #if BUCKETLOCK == SPINLOCK
    950976                block = freeHead->freeList;                                             // remove node from stack
     977                #else
     978                block = pop( freeHead->freeList );
     979                #endif // BUCKETLOCK
    951980                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.
    954981                        #ifdef OWNERSHIP
    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 {
     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?
    974995                        #endif // OWNERSHIP
    975996                                // Do not leave kernel thread as manager_extend accesses heapManager.
     
    9811002
    9821003                                #ifdef __CFA_DEBUG__
    983                                 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first SCRUB_SIZE bytes.
     1004                                // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes.
    9841005                                memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) );
    9851006                                #endif // __CFA_DEBUG__
     1007                        #endif // BUCKETLOCK
    9861008                        #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;
    9871017                        } // if
    9881018                        #endif // OWNERSHIP
     
    9961026  if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p;
    9971027                tsize = ceiling2( tsize, __page_size );                 // must be multiple of page size
    998 
    9991028                #ifdef __STATISTICS__
    10001029                stats.counters[STAT_NAME].alloc += tsize;
     
    10131042                        if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory
    10141043                        // Do not call strerror( errno ) as it may call malloc.
    1015                         abort( "**** Error **** attempt to allocate large object (> %zu) of size %zu bytes and mmap failed with errno %d.",
    1016                                    size, heapMaster.mmapStart, errno );
     1044                        abort( "**** Error **** attempt to allocate large object (> %zu) of size %zu bytes and mmap failed with errno %d.", size, heapMaster.mmapStart, errno );
    10171045                } // if
    10181046                block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap
    10191047
    10201048                #ifdef __CFA_DEBUG__
    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.
     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.
    10231051                memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) );
    10241052                #endif // __CFA_DEBUG__
     
    10981126                #endif // __CFA_DEBUG__
    10991127
    1100                 #ifdef OWNERSHIP
    11011128                if ( likely( heapManager == freeHead->homeManager ) ) { // belongs to this thread
    11021129                        header->kind.real.next = freeHead->freeList; // push on stack
     
    11051132                        verify( heapManager );
    11061133
     1134                        #ifdef OWNERSHIP
    11071135                        #ifdef RETURNSPIN
    11081136                        lock( freeHead->returnLock );
     
    11131141                        header->kind.real.next = freeHead->returnList; // link new node to top node
    11141142                        // CAS resets header->kind.real.next = freeHead->returnList on failure
    1115                         while ( ! __atomic_compare_exchange_n( &freeHead->returnList, &header->kind.real.next, (Heap.Storage *)header,
     1143                        while ( ! __atomic_compare_exchange_n( &freeHead->returnList, &header->kind.real.next, header,
    11161144                                                                                                   false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) );
    11171145                        #endif // RETURNSPIN
    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.
     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
    11351162        } // if
    11361163
     
    11591186                #endif // __STATISTICS__
    11601187
     1188                #if BUCKETLOCK == SPINLOCK
    11611189                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
    11621198                        total += size;
    11631199                        #ifdef __STATISTICS__
Note: See TracChangeset for help on using the changeset viewer.