Changes in libcfa/src/heap.cfa [0bdfcc3:80fbdc9]
- File:
-
- 1 edited
-
libcfa/src/heap.cfa (modified) (22 diffs)
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/heap.cfa
r0bdfcc3 r80fbdc9 10 10 // Created On : Tue Dec 19 21:58:35 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Oct 30 20:56:20202213 // Update Count : 15 8412 // Last Modified On : Thu Oct 13 22:21:52 2022 13 // Update Count : 1557 14 14 // 15 15 … … 43 43 44 44 #define FASTLOOKUP // use O(1) table lookup from allocation size to bucket size 45 #define RETURNSPIN // toggle spinlock / lockfree stack 45 46 #define OWNERSHIP // return freed memory to owner thread 46 #define RETURNSPIN // toggle spinlock / lockfree queue47 #if ! defined( OWNERSHIP ) && defined( RETURNSPIN )48 #warning "RETURNSPIN is ignored without OWNERSHIP; suggest commenting out RETURNSPIN"49 #endif // ! OWNERSHIP && RETURNSPIN50 47 51 48 #define CACHE_ALIGN 64 … … 112 109 113 110 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 ######################### 131 112 132 113 … … 225 206 226 207 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 227 218 // Recursive definitions: HeapManager needs size of bucket array and bucket area needs sizeof HeapManager storage. 228 219 // Break recursion by hardcoding number of buckets and statically checking number is correct after bucket array defined. … … 241 232 void * home; // allocated block points back to home locations (must overlay alignment) 242 233 size_t blockSize; // size for munmap (must overlay alignment) 234 #if BUCKETLOCK == SPINLOCK 243 235 Storage * next; // freed block points to next freed block of same size 236 #endif // SPINLOCK 244 237 }; 245 238 size_t size; // allocation size in bytes 246 239 }; 240 #if BUCKETLOCK == LOCKFREE 241 Link(Storage) next; // freed block points next freed block of same size (double-wide) 242 #endif // LOCKFREE 247 243 }; 248 244 } real; // RealHeader … … 263 259 struct __attribute__(( aligned (8) )) FreeHeader { 264 260 size_t blockSize __attribute__(( aligned(8) )); // size of allocations on this list 261 #if BUCKETLOCK == SPINLOCK 265 262 #ifdef OWNERSHIP 266 263 #ifdef RETURNSPIN … … 269 266 Storage * returnList; // other thread return list 270 267 #endif // OWNERSHIP 271 272 268 Storage * freeList; // thread free list 269 #else 270 StackLF(Storage) freeList; 271 #endif // BUCKETLOCK 273 272 Heap * homeManager; // heap owner (free storage to bucket, from bucket to heap) 274 273 }; // FreeHeader … … 291 290 #endif // __STATISTICS__ 292 291 }; // 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 293 301 294 302 … … 377 385 378 386 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 379 403 void heapMasterCtor() with( heapMaster ) { 380 404 // Singleton pattern to initialize heap master … … 385 409 __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 386 410 387 extLock = 0;388 mgrLock = 0;411 ?{}( extLock ); 412 ?{}( mgrLock ); 389 413 390 414 char * end = (char *)sbrk( 0 ); … … 473 497 #ifdef OWNERSHIP 474 498 #ifdef RETURNSPIN 475 freeLists[j].returnLock = 0; 499 ?{}( freeLists[j].returnLock ); 500 #endif // RETURNSPIN 476 501 freeLists[j].returnList = 0p; 477 #endif // RETURNSPIN478 502 #endif // OWNERSHIP 479 480 503 freeLists[j].freeList = 0p; 481 504 freeLists[j].homeManager = heap; 482 505 freeLists[j].blockSize = bucketSizes[j]; 483 506 } // for 484 507 485 508 heapBuffer = 0p; 486 509 heapReserve = 0; … … 499 522 if ( unlikely( ! heapMasterBootFlag ) ) heapMasterCtor(); 500 523 501 lock( heapMaster.mgrLock ); // protect heapMaster counters524 lock( heapMaster.mgrLock ); // protect heapMaster counters 502 525 503 526 // get storage for heap manager … … 687 710 // find the closest bucket size less than or equal to the mmapStart size 688 711 maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search 689 690 712 verify( maxBucketsUsed < NoBucketSizes ); // subscript failure ? 691 713 verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ? … … 810 832 811 833 size_t increase = ceiling2( size > heapExpand ? size : heapExpand, libAlign() ); 834 // Do not call abort or strerror( errno ) as they may call malloc. 812 835 if ( unlikely( sbrk( increase ) == (void *)-1 ) ) { // failed, no memory ? 813 836 unlock( extLock ); 814 abort( NO_MEMORY_MSG, size ); // give up837 abort( NO_MEMORY_MSG, size ); // no memory 815 838 } // if 816 839 … … 948 971 #endif // __STATISTICS__ 949 972 973 // Spin until the lock is acquired for this particular size of block. 974 975 #if BUCKETLOCK == SPINLOCK 950 976 block = freeHead->freeList; // remove node from stack 977 #else 978 block = pop( freeHead->freeList ); 979 #endif // BUCKETLOCK 951 980 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 there953 // is enough left, or get some more heap storage and carve it off.954 981 #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? 974 995 #endif // OWNERSHIP 975 996 // Do not leave kernel thread as manager_extend accesses heapManager. … … 981 1002 982 1003 #ifdef __CFA_DEBUG__ 983 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first SCRUB_SIZEbytes.1004 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes. 984 1005 memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) ); 985 1006 #endif // __CFA_DEBUG__ 1007 #endif // BUCKETLOCK 986 1008 #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; 987 1017 } // if 988 1018 #endif // OWNERSHIP … … 996 1026 if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p; 997 1027 tsize = ceiling2( tsize, __page_size ); // must be multiple of page size 998 999 1028 #ifdef __STATISTICS__ 1000 1029 stats.counters[STAT_NAME].alloc += tsize; … … 1013 1042 if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory 1014 1043 // 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 ); 1017 1045 } // if 1018 1046 block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap 1019 1047 1020 1048 #ifdef __CFA_DEBUG__ 1021 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first SCRUB_SIZE bytes. The1022 // rest ofthe 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. 1023 1051 memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) ); 1024 1052 #endif // __CFA_DEBUG__ … … 1098 1126 #endif // __CFA_DEBUG__ 1099 1127 1100 #ifdef OWNERSHIP1101 1128 if ( likely( heapManager == freeHead->homeManager ) ) { // belongs to this thread 1102 1129 header->kind.real.next = freeHead->freeList; // push on stack … … 1105 1132 verify( heapManager ); 1106 1133 1134 #ifdef OWNERSHIP 1107 1135 #ifdef RETURNSPIN 1108 1136 lock( freeHead->returnLock ); … … 1113 1141 header->kind.real.next = freeHead->returnList; // link new node to top node 1114 1142 // 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, 1116 1144 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ); 1117 1145 #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 1135 1162 } // if 1136 1163 … … 1159 1186 #endif // __STATISTICS__ 1160 1187 1188 #if BUCKETLOCK == SPINLOCK 1161 1189 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 1162 1198 total += size; 1163 1199 #ifdef __STATISTICS__
Note:
See TracChangeset
for help on using the changeset viewer.