Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/heap.cfa

    r116a2ea r5a076837  
    1010// Created On       : Tue Dec 19 21:58:35 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Tue Oct 11 15:08:33 2022
    13 // Update Count     : 1525
     12// Last Modified On : Fri Apr 29 19:05:03 2022
     13// Update Count     : 1167
    1414//
    1515
    16 #include <stdio.h>
    1716#include <string.h>                                                                             // memset, memcpy
    1817#include <limits.h>                                                                             // ULONG_MAX
     
    2221#include <malloc.h>                                                                             // memalign, malloc_usable_size
    2322#include <sys/mman.h>                                                                   // mmap, munmap
    24 extern "C" {
    2523#include <sys/sysinfo.h>                                                                // get_nprocs
    26 } // extern "C"
    2724
    2825#include "bits/align.hfa"                                                               // libAlign
    2926#include "bits/defs.hfa"                                                                // likely, unlikely
    3027#include "bits/locks.hfa"                                                               // __spinlock_t
    31 #include "concurrency/kernel/fwd.hfa"                                   // __POLL_PREEMPTION
    3228#include "startup.hfa"                                                                  // STARTUP_PRIORITY_MEMORY
    33 #include "math.hfa"                                                                             // ceiling, min
     29#include "math.hfa"                                                                             // min
    3430#include "bitmanip.hfa"                                                                 // is_pow2, ceiling2
    3531
    36 // supported mallopt options
    37 #ifndef M_MMAP_THRESHOLD
    38 #define M_MMAP_THRESHOLD (-1)
    39 #endif // M_MMAP_THRESHOLD
    40 
    41 #ifndef M_TOP_PAD
    42 #define M_TOP_PAD (-2)
    43 #endif // M_TOP_PAD
    44 
    45 #define FASTLOOKUP                                                                              // use O(1) table lookup from allocation size to bucket size
    46 #define RETURNSPIN                                                                              // toggle spinlock / lockfree stack
    47 #define OWNERSHIP                                                                               // return freed memory to owner thread
    48 
    49 #define CACHE_ALIGN 64
    50 #define CALIGN __attribute__(( aligned(CACHE_ALIGN) ))
    51 
    52 #define TLSMODEL __attribute__(( tls_model("initial-exec") ))
    53 
    54 //#define __STATISTICS__
    55 
    56 enum {
    57         // The default extension heap amount in units of bytes. When the current heap reaches the brk address, the brk
    58         // address is extended by the extension amount.
    59         __CFA_DEFAULT_HEAP_EXPANSION__ = 10 * 1024 * 1024,
    60 
    61         // The mmap crossover point during allocation. Allocations less than this amount are allocated from buckets; values
    62         // greater than or equal to this value are mmap from the operating system.
    63         __CFA_DEFAULT_MMAP_START__ = 512 * 1024 + 1,
    64 
    65         // The default unfreed storage amount in units of bytes. When the uC++ program ends it subtracts this amount from
    66         // the malloc/free counter to adjust for storage the program does not free.
    67         __CFA_DEFAULT_HEAP_UNFREED__ = 0
    68 }; // enum
    69 
    70 
    71 //####################### Heap Trace/Print ####################
     32#define FASTLOOKUP
     33#define __STATISTICS__
    7234
    7335
     
    9355static bool prtFree = false;
    9456
    95 bool prtFree() {
     57static bool prtFree() {
    9658        return prtFree;
    9759} // prtFree
    9860
    99 bool prtFreeOn() {
     61static bool prtFreeOn() {
    10062        bool temp = prtFree;
    10163        prtFree = true;
     
    10365} // prtFreeOn
    10466
    105 bool prtFreeOff() {
     67static bool prtFreeOff() {
    10668        bool temp = prtFree;
    10769        prtFree = false;
     
    11072
    11173
    112 //######################### Spin Lock #########################
    113 
    114 
    115 // pause to prevent excess processor bus usage
    116 #if defined( __i386 ) || defined( __x86_64 )
    117         #define Pause() __asm__ __volatile__ ( "pause" : : : )
    118 #elif defined(__ARM_ARCH)
    119         #define Pause() __asm__ __volatile__ ( "YIELD" : : : )
    120 #else
    121         #error unsupported architecture
    122 #endif
    123 
    124 typedef volatile uintptr_t SpinLock_t CALIGN;                   // aligned addressable word-size
    125 
    126 static inline __attribute__((always_inline)) void lock( volatile SpinLock_t & slock ) {
    127         enum { SPIN_START = 4, SPIN_END = 64 * 1024, };
    128         unsigned int spin = SPIN_START;
    129 
    130         for ( unsigned int i = 1;; i += 1 ) {
    131           if ( slock == 0 && __atomic_test_and_set( &slock, __ATOMIC_SEQ_CST ) == 0 ) break; // Fence
    132                 for ( volatile unsigned int s = 0; s < spin; s += 1 ) Pause(); // exponential spin
    133                 spin += spin;                                                                   // powers of 2
    134                 //if ( i % 64 == 0 ) spin += spin;                              // slowly increase by powers of 2
    135                 if ( spin > SPIN_END ) spin = SPIN_END;                 // cap spinning
    136         } // for
    137 } // spin_lock
    138 
    139 static inline __attribute__((always_inline)) void unlock( volatile SpinLock_t & slock ) {
    140         __atomic_clear( &slock, __ATOMIC_SEQ_CST );                     // Fence
    141 } // spin_unlock
     74enum {
     75        // The default extension heap amount in units of bytes. When the current heap reaches the brk address, the brk
     76        // address is extended by the extension amount.
     77        __CFA_DEFAULT_HEAP_EXPANSION__ = 10 * 1024 * 1024,
     78
     79        // The mmap crossover point during allocation. Allocations less than this amount are allocated from buckets; values
     80        // greater than or equal to this value are mmap from the operating system.
     81        __CFA_DEFAULT_MMAP_START__ = 512 * 1024 + 1,
     82
     83        // The default unfreed storage amount in units of bytes. When the uC++ program ends it subtracts this amount from
     84        // the malloc/free counter to adjust for storage the program does not free.
     85        __CFA_DEFAULT_HEAP_UNFREED__ = 0
     86}; // enum
    14287
    14388
     
    175120                unsigned int free_calls, free_null_calls;
    176121                unsigned long long int free_storage_request, free_storage_alloc;
    177                 unsigned int return_pulls, return_pushes;
    178                 unsigned long long int return_storage_request, return_storage_alloc;
     122                unsigned int away_pulls, away_pushes;
     123                unsigned long long int away_storage_request, away_storage_alloc;
    179124                unsigned int mmap_calls, mmap_0_calls;                  // no zero calls
    180125                unsigned long long int mmap_storage_request, mmap_storage_alloc;
     
    186131
    187132static_assert( sizeof(HeapStatistics) == CntTriples * sizeof(StatsOverlay),
    188                            "Heap statistics counter-triplets does not match with array size" );
     133                           "Heap statistics counter-triplets does not match with array size" );
    189134
    190135static void HeapStatisticsCtor( HeapStatistics & stats ) {
     
    258203        static_assert( libAlign() >= sizeof( Storage ), "minimum alignment < sizeof( Storage )" );
    259204
    260         struct __attribute__(( aligned (8) )) FreeHeader {
    261                 size_t blockSize __attribute__(( aligned(8) )); // size of allocations on this list
     205        struct FreeHeader {
     206                size_t blockSize __attribute__(( aligned (8) )); // size of allocations on this list
    262207                #if BUCKETLOCK == SPINLOCK
    263                 #ifdef OWNERSHIP
    264                 #ifdef RETURNSPIN
    265                 SpinLock_t returnLock;
    266                 #endif // RETURNSPIN
    267                 Storage * returnList;                                                   // other thread return list
    268                 #endif // OWNERSHIP
    269                 Storage * freeList;                                                             // thread free list
     208                __spinlock_t lock;
     209                Storage * freeList;
    270210                #else
    271211                StackLF(Storage) freeList;
    272212                #endif // BUCKETLOCK
    273                 Heap * homeManager;                                                             // heap owner (free storage to bucket, from bucket to heap)
    274         }; // FreeHeader
     213        } __attribute__(( aligned (8) )); // FreeHeader
    275214
    276215        FreeHeader freeLists[NoBucketSizes];                            // buckets for different allocation sizes
    277         void * heapBuffer;                                                                      // start of free storage in buffer
    278         size_t heapReserve;                                                                     // amount of remaining free storage in buffer
    279 
    280         #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ )
    281         Heap * nextHeapManager;                                                         // intrusive link of existing heaps; traversed to collect statistics or check unfreed storage
    282         #endif // __STATISTICS__ || __CFA_DEBUG__
    283         Heap * nextFreeHeapManager;                                                     // intrusive link of free heaps from terminated threads; reused by new threads
    284 
    285         #ifdef __CFA_DEBUG__
    286         int64_t allocUnfreed;                                                           // running total of allocations minus frees; can be negative
    287         #endif // __CFA_DEBUG__
    288 
    289         #ifdef __STATISTICS__
    290         HeapStatistics stats;                                                           // local statistic table for this heap
    291         #endif // __STATISTICS__
     216
     217        __spinlock_t extlock;                                                           // protects allocation-buffer extension
     218        void * heapBegin;                                                                       // start of heap
     219        void * heapEnd;                                                                         // logical end of heap
     220        size_t heapRemaining;                                                           // amount of storage not allocated in the current chunk
    292221}; // Heap
    293222
    294223#if BUCKETLOCK == LOCKFREE
    295 inline __attribute__((always_inline))
    296 static {
     224static inline {
    297225        Link(Heap.Storage) * ?`next( Heap.Storage * this ) { return &this->header.kind.real.next; }
    298226        void ?{}( Heap.FreeHeader & ) {}
     
    301229#endif // LOCKFREE
    302230
    303 
    304 struct HeapMaster {
    305         SpinLock_t extLock;                                                                     // protects allocation-buffer extension
    306         SpinLock_t mgrLock;                                                                     // protects freeHeapManagersList, heapManagersList, heapManagersStorage, heapManagersStorageEnd
    307 
    308         void * heapBegin;                                                                       // start of heap
    309         void * heapEnd;                                                                         // logical end of heap
    310         size_t heapRemaining;                                                           // amount of storage not allocated in the current chunk
    311         size_t pageSize;                                                                        // architecture pagesize
    312         size_t heapExpand;                                                                      // sbrk advance
    313         size_t mmapStart;                                                                       // cross over point for mmap
    314         unsigned int maxBucketsUsed;                                            // maximum number of buckets in use
    315 
    316         Heap * heapManagersList;                                                        // heap-list head
    317         Heap * freeHeapManagersList;                                            // free-list head
    318 
    319         // Heap superblocks are not linked; heaps in superblocks are linked via intrusive links.
    320         Heap * heapManagersStorage;                                                     // next heap to use in heap superblock
    321         Heap * heapManagersStorageEnd;                                          // logical heap outside of superblock's end
    322 
    323         #ifdef __STATISTICS__
    324         HeapStatistics stats;                                                           // global stats for thread-local heaps to add there counters when exiting
    325         unsigned long int threads_started, threads_exited;      // counts threads that have started and exited
    326         unsigned long int reused_heap, new_heap;                        // counts reusability of heaps
    327         unsigned int sbrk_calls;
    328         unsigned long long int sbrk_storage;
    329         int stats_fd;
    330         #endif // __STATISTICS__
    331 }; // HeapMaster
     231static inline size_t getKey( const Heap.FreeHeader & freeheader ) { return freeheader.blockSize; }
    332232
    333233
    334234#ifdef FASTLOOKUP
    335 enum { LookupSizes = 65_536 + sizeof(Heap.Storage) };   // number of fast lookup sizes
     235enum { LookupSizes = 65_536 + sizeof(Heap.Storage) }; // number of fast lookup sizes
    336236static unsigned char lookup[LookupSizes];                               // O(1) lookup for small sizes
    337237#endif // FASTLOOKUP
    338238
    339 static volatile bool heapMasterBootFlag = false;                // trigger for first heap
    340 static HeapMaster heapMaster @= {};                                             // program global
    341 
    342 static void heapMasterCtor();
    343 static void heapMasterDtor();
    344 static Heap * getHeap();
     239static const off_t mmapFd = -1;                                                 // fake or actual fd for anonymous file
     240#ifdef __CFA_DEBUG__
     241static bool heapBoot = 0;                                                               // detect recursion during boot
     242#endif // __CFA_DEBUG__
    345243
    346244
     
    370268static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" );
    371269
    372 
    373 // extern visibility, used by runtime kernel
    374 libcfa_public size_t __page_size;                                               // architecture pagesize
    375 libcfa_public int __map_prot;                                                   // common mmap/mprotect protection
    376 
    377 
    378 // Thread-local storage is allocated lazily when the storage is accessed.
    379 static __thread size_t PAD1 CALIGN TLSMODEL __attribute__(( unused )); // protect false sharing
    380 static __thread Heap * volatile heapManager CALIGN TLSMODEL;
    381 static __thread size_t PAD2 CALIGN TLSMODEL __attribute__(( unused )); // protect further false sharing
    382 
    383 
    384 // declare helper functions for HeapMaster
    385 void noMemory();                                                                                // forward, called by "builtin_new" when malloc returns 0
    386 
    387 
    388 // generic Bsearchl does not inline, so substitute with hand-coded binary-search.
    389 inline __attribute__((always_inline))
    390 static size_t Bsearchl( unsigned int key, const unsigned int vals[], size_t dim ) {
    391         size_t l = 0, m, h = dim;
    392         while ( l < h ) {
    393                 m = (l + h) / 2;
    394                 if ( (unsigned int &)(vals[m]) < key ) {                // cast away const
    395                         l = m + 1;
    396                 } else {
    397                         h = m;
    398                 } // if
    399         } // while
    400         return l;
    401 } // Bsearchl
    402 
    403 
    404 void heapMasterCtor() with( heapMaster ) {
    405         // Singleton pattern to initialize heap master
    406 
    407         verify( bucketSizes[0] == (16 + sizeof(Heap.Storage)) );
    408 
    409         __page_size = sysconf( _SC_PAGESIZE );
    410         __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC;
    411 
    412         ?{}( extLock );
    413         ?{}( mgrLock );
    414 
    415         char * end = (char *)sbrk( 0 );
    416         heapBegin = heapEnd = sbrk( (char *)ceiling2( (long unsigned int)end, libAlign() ) - end ); // move start of heap to multiple of alignment
    417         heapRemaining = 0;
    418         heapExpand = malloc_expansion();
    419         mmapStart = malloc_mmap_start();
    420 
    421         // find the closest bucket size less than or equal to the mmapStart size
    422         maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search
    423 
    424         verify( (mmapStart >= pageSize) && (bucketSizes[NoBucketSizes - 1] >= mmapStart) );
    425         verify( maxBucketsUsed < NoBucketSizes );                       // subscript failure ?
    426         verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?
    427 
    428         heapManagersList = 0p;
    429         freeHeapManagersList = 0p;
    430 
    431         heapManagersStorage = 0p;
    432         heapManagersStorageEnd = 0p;
    433 
    434         #ifdef __STATISTICS__
    435         HeapStatisticsCtor( stats );                                            // clear statistic counters
    436         threads_started = threads_exited = 0;
    437         reused_heap = new_heap = 0;
    438         sbrk_calls = sbrk_storage = 0;
    439         stats_fd = STDERR_FILENO;
    440         #endif // __STATISTICS__
    441 
    442         #ifdef FASTLOOKUP
    443         for ( unsigned int i = 0, idx = 0; i < LookupSizes; i += 1 ) {
    444                 if ( i > bucketSizes[idx] ) idx += 1;
    445                 lookup[i] = idx;
    446                 verify( i <= bucketSizes[idx] );
    447                 verify( (i <= 32 && idx == 0) || (i > bucketSizes[idx - 1]) );
    448         } // for
    449         #endif // FASTLOOKUP
    450 
    451         heapMasterBootFlag = true;
    452 } // heapMasterCtor
    453 
    454 
    455 #define NO_MEMORY_MSG "insufficient heap memory available to allocate %zd new bytes."
    456 
    457 Heap * getHeap() with( heapMaster ) {
    458         Heap * heap;
    459         if ( freeHeapManagersList ) {                                           // free heap for reused ?
    460                 heap = freeHeapManagersList;
    461                 freeHeapManagersList = heap->nextFreeHeapManager;
    462 
    463                 #ifdef __STATISTICS__
    464                 reused_heap += 1;
    465                 #endif // __STATISTICS__
    466         } else {                                                                                        // free heap not found, create new
    467                 // Heap size is about 12K, FreeHeader (128 bytes because of cache alignment) * NoBucketSizes (91) => 128 heaps *
    468                 // 12K ~= 120K byte superblock.  Where 128-heap superblock handles a medium sized multi-processor server.
    469                 size_t remaining = heapManagersStorageEnd - heapManagersStorage; // remaining free heaps in superblock
    470                 if ( ! heapManagersStorage || remaining != 0 ) {
    471                         // Each block of heaps is a multiple of the number of cores on the computer.
    472                         int HeapDim = get_nprocs();                                     // get_nprocs_conf does not work
    473                         size_t size = HeapDim * sizeof( Heap );
    474 
    475                         heapManagersStorage = (Heap *)mmap( 0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
    476                         if ( unlikely( heapManagersStorage == (Heap *)MAP_FAILED ) ) { // failed ?
    477                                 if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, size ); // no memory
    478                                 // Do not call strerror( errno ) as it may call malloc.
    479                                 abort( "attempt to allocate block of heaps of size %zu bytes and mmap failed with errno %d.", size, errno );
    480                         } // if
    481                         heapManagersStorageEnd = &heapManagersStorage[HeapDim]; // outside array
    482                 } // if
    483 
    484                 heap = heapManagersStorage;
    485                 heapManagersStorage = heapManagersStorage + 1; // bump next heap
    486 
    487                 #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ )
    488                 heap->nextHeapManager = heapManagersList;
    489                 #endif // __STATISTICS__ || __CFA_DEBUG__
    490                 heapManagersList = heap;
    491 
    492                 #ifdef __STATISTICS__
    493                 new_heap += 1;
    494                 #endif // __STATISTICS__
    495 
    496                 with( *heap ) {
    497                         for ( unsigned int j = 0; j < NoBucketSizes; j += 1 ) { // initialize free lists
    498                                 #ifdef OWNERSHIP
    499                                 #ifdef RETURNSPIN
    500                                 ?{}( freeLists[j].returnLock );
    501                                 #endif // RETURNSPIN
    502                                 freeLists[j].returnList = 0p;
    503                                 #endif // OWNERSHIP
    504                                 freeLists[j].freeList = 0p;
    505                                 freeLists[j].homeManager = heap;
    506                                 freeLists[j].blockSize = bucketSizes[j];
    507                         } // for
    508        
    509                         heapBuffer = 0p;
    510                         heapReserve = 0;
    511                         nextFreeHeapManager = 0p;
    512                         #ifdef __CFA_DEBUG__
    513                         allocUnfreed = 0;
    514                         #endif // __CFA_DEBUG__
    515                 } // with
    516         } // if
    517         return heap;
    518 } // getHeap
    519 
    520 
    521 void heapManagerCtor() libcfa_public {
    522         if ( unlikely( ! heapMasterBootFlag ) ) heapMasterCtor();
    523 
    524         lock( heapMaster.mgrLock );             // protect heapMaster counters
    525 
    526         // get storage for heap manager
    527 
    528         heapManager = getHeap();
    529 
    530         #ifdef __STATISTICS__
    531         HeapStatisticsCtor( heapManager->stats );                       // heap local
    532         heapMaster.threads_started += 1;
    533         #endif // __STATISTICS__
    534 
    535         unlock( heapMaster.mgrLock );
    536 } // heapManagerCtor
    537 
    538 
    539 void heapManagerDtor() libcfa_public {
    540         lock( heapMaster.mgrLock );
    541 
    542         // place heap on list of free heaps for reusability
    543         heapManager->nextFreeHeapManager = heapMaster.freeHeapManagersList;
    544         heapMaster.freeHeapManagersList = heapManager;
    545 
    546         #ifdef __STATISTICS__
    547         heapMaster.threads_exited += 1;
    548         #endif // __STATISTICS__
    549 
    550         // Do not set heapManager to NULL because it is used after Cforall is shutdown but before the program shuts down.
    551 
    552         unlock( heapMaster.mgrLock );
    553 } // heapManagerDtor
     270// The constructor for heapManager is called explicitly in memory_startup.
     271static Heap heapManager __attribute__(( aligned (128) )) @= {}; // size of cache line to prevent false sharing
    554272
    555273
    556274//####################### Memory Allocation Routines Helpers ####################
    557275
     276
     277#ifdef __CFA_DEBUG__
     278static size_t allocUnfreed;                                                             // running total of allocations minus frees
     279
     280static void prtUnfreed() {
     281        if ( allocUnfreed != 0 ) {
     282                // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT.
     283                char helpText[512];
     284                __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),
     285                                                                        "CFA warning (UNIX pid:%ld) : program terminating with %zu(0x%zx) bytes of storage allocated but not freed.\n"
     286                                                                        "Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n",
     287                                                                        (long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid
     288        } // if
     289} // prtUnfreed
    558290
    559291extern int cfa_main_returned;                                                   // from interpose.cfa
    560292extern "C" {
    561         void memory_startup( void ) {
    562                 if ( ! heapMasterBootFlag ) heapManagerCtor();  // sanity check
    563         } // memory_startup
    564 
    565         void memory_shutdown( void ) {
    566                 heapManagerDtor();
    567         } // memory_shutdown
    568 
    569293        void heapAppStart() {                                                           // called by __cfaabi_appready_startup
    570                 verify( heapManager );
    571                 #ifdef __CFA_DEBUG__
    572                 heapManager->allocUnfreed = 0;                                  // clear prior allocation counts
    573                 #endif // __CFA_DEBUG__
    574 
    575                 #ifdef __STATISTICS__
    576                 HeapStatisticsCtor( heapManager->stats );               // clear prior statistic counters
    577                 #endif // __STATISTICS__
     294                allocUnfreed = 0;
    578295        } // heapAppStart
    579296
    580297        void heapAppStop() {                                                            // called by __cfaabi_appready_startdown
    581                 fclose( stdin ); fclose( stdout );                              // free buffer storage
    582           if ( ! cfa_main_returned ) return;                            // do not check unfreed storage if exit called
    583 
    584                 #ifdef __CFA_DEBUG__
    585                 // allocUnfreed is set to 0 when a heap is created and it accumulates any unfreed storage during its multiple thread
    586                 // usages.  At the end, add up each heap allocUnfreed value across all heaps to get the total unfreed storage.
    587                 long long int allocUnfreed = 0;
    588                 for ( Heap * heap = heapMaster.heapManagersList; heap; heap = heap->nextHeapManager ) {
    589                         allocUnfreed += heap->allocUnfreed;
    590                 } // for
    591 
    592                 allocUnfreed -= malloc_unfreed();                               // subtract any user specified unfreed storage
    593                 if ( allocUnfreed > 0 ) {
    594                         // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT.
    595                         char helpText[512];
    596                         __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),
    597                                                                                 "CFA warning (UNIX pid:%ld) : program terminating with %llu(0x%llx) bytes of storage allocated but not freed.\n"
    598                                                                                 "Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n",
    599                                                                                 (long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid
    600                 } // if
    601                 #endif // __CFA_DEBUG__
     298                fclose( stdin ); fclose( stdout );
     299                if ( cfa_main_returned ) prtUnfreed();                  // do not check unfreed storage if exit called
    602300        } // heapAppStop
    603301} // extern "C"
     302#endif // __CFA_DEBUG__
    604303
    605304
    606305#ifdef __STATISTICS__
    607306static HeapStatistics stats;                                                    // zero filled
     307static unsigned int sbrk_calls;
     308static unsigned long long int sbrk_storage;
     309// Statistics file descriptor (changed by malloc_stats_fd).
     310static int stats_fd = STDERR_FILENO;                                    // default stderr
    608311
    609312#define prtFmt \
     
    618321        "  realloc   >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
    619322        "  free      !null calls %'u; null calls %'u; storage %'llu / %'llu bytes\n" \
    620         "  return    pulls %'u; pushes %'u; storage %'llu / %'llu bytes\n" \
    621         "  sbrk      calls %'u; storage %'llu bytes\n" \
    622         "  mmap      calls %'u; storage %'llu / %'llu bytes\n" \
    623         "  munmap    calls %'u; storage %'llu / %'llu bytes\n" \
    624         "  threads   started %'lu; exited %'lu\n" \
    625         "  heaps     new %'lu; reused %'lu\n"
     323        "  sbrk      calls %'u; storage %'llu bytes\n"                                          \
     324        "  mmap      calls %'u; storage %'llu / %'llu bytes\n"                          \
     325        "  munmap    calls %'u; storage %'llu / %'llu bytes\n"                          \
    626326
    627327// Use "write" because streams may be shutdown when calls are made.
    628 static int printStats( HeapStatistics & stats ) with( heapMaster, stats ) {     // see malloc_stats
     328static int printStats() {                                                               // see malloc_stats
    629329        char helpText[sizeof(prtFmt) + 1024];                           // space for message and values
    630         return __cfaabi_bits_print_buffer( stats_fd, helpText, sizeof(helpText), prtFmt,
    631                         malloc_calls, malloc_0_calls, malloc_storage_request, malloc_storage_alloc,
    632                         aalloc_calls, aalloc_0_calls, aalloc_storage_request, aalloc_storage_alloc,
    633                         calloc_calls, calloc_0_calls, calloc_storage_request, calloc_storage_alloc,
    634                         memalign_calls, memalign_0_calls, memalign_storage_request, memalign_storage_alloc,
    635                         amemalign_calls, amemalign_0_calls, amemalign_storage_request, amemalign_storage_alloc,
    636                         cmemalign_calls, cmemalign_0_calls, cmemalign_storage_request, cmemalign_storage_alloc,
    637                         resize_calls, resize_0_calls, resize_storage_request, resize_storage_alloc,
    638                         realloc_calls, realloc_0_calls, realloc_storage_request, realloc_storage_alloc,
    639                         free_calls, free_null_calls, free_storage_request, free_storage_alloc,
    640                         return_pulls, return_pushes, return_storage_request, return_storage_alloc,
     330        return __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText), prtFmt,
     331                        stats.malloc_calls, stats.malloc_0_calls, stats.malloc_storage_request, stats.malloc_storage_alloc,
     332                        stats.aalloc_calls, stats.aalloc_0_calls, stats.aalloc_storage_request, stats.aalloc_storage_alloc,
     333                        stats.calloc_calls, stats.calloc_0_calls, stats.calloc_storage_request, stats.calloc_storage_alloc,
     334                        stats.memalign_calls, stats.memalign_0_calls, stats.memalign_storage_request, stats.memalign_storage_alloc,
     335                        stats.amemalign_calls, stats.amemalign_0_calls, stats.amemalign_storage_request, stats.amemalign_storage_alloc,
     336                        stats.cmemalign_calls, stats.cmemalign_0_calls, stats.cmemalign_storage_request, stats.cmemalign_storage_alloc,
     337                        stats.resize_calls, stats.resize_0_calls, stats.resize_storage_request, stats.resize_storage_alloc,
     338                        stats.realloc_calls, stats.realloc_0_calls, stats.realloc_storage_request, stats.realloc_storage_alloc,
     339                        stats.free_calls, stats.free_null_calls, stats.free_storage_request, stats.free_storage_alloc,
    641340                        sbrk_calls, sbrk_storage,
    642                         mmap_calls, mmap_storage_request, mmap_storage_alloc,
    643                         munmap_calls, munmap_storage_request, munmap_storage_alloc,
    644                         threads_started, threads_exited,
    645                         new_heap, reused_heap
     341                        stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc,
     342                        stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc
    646343                );
    647344} // printStats
     
    661358        "<total type=\"realloc\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
    662359        "<total type=\"free\" !null=\"%'u;\" 0 null=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
    663         "<total type=\"return\" pulls=\"%'u;\" 0 pushes=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
    664360        "<total type=\"sbrk\" count=\"%'u;\" size=\"%'llu\"/> bytes\n" \
    665361        "<total type=\"mmap\" count=\"%'u;\" size=\"%'llu / %'llu\" / > bytes\n" \
    666362        "<total type=\"munmap\" count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
    667         "<total type=\"threads\" started=\"%'lu;\" exited=\"%'lu\"/>\n" \
    668         "<total type=\"heaps\" new=\"%'lu;\" reused=\"%'lu\"/>\n" \
    669363        "</malloc>"
    670364
    671 static int printStatsXML( HeapStatistics & stats, FILE * stream ) with( heapMaster, stats ) { // see malloc_info
     365static int printStatsXML( FILE * stream ) {                             // see malloc_info
    672366        char helpText[sizeof(prtFmtXML) + 1024];                        // space for message and values
    673367        return __cfaabi_bits_print_buffer( fileno( stream ), helpText, sizeof(helpText), prtFmtXML,
    674                         malloc_calls, malloc_0_calls, malloc_storage_request, malloc_storage_alloc,
    675                         aalloc_calls, aalloc_0_calls, aalloc_storage_request, aalloc_storage_alloc,
    676                         calloc_calls, calloc_0_calls, calloc_storage_request, calloc_storage_alloc,
    677                         memalign_calls, memalign_0_calls, memalign_storage_request, memalign_storage_alloc,
    678                         amemalign_calls, amemalign_0_calls, amemalign_storage_request, amemalign_storage_alloc,
    679                         cmemalign_calls, cmemalign_0_calls, cmemalign_storage_request, cmemalign_storage_alloc,
    680                         resize_calls, resize_0_calls, resize_storage_request, resize_storage_alloc,
    681                         realloc_calls, realloc_0_calls, realloc_storage_request, realloc_storage_alloc,
    682                         free_calls, free_null_calls, free_storage_request, free_storage_alloc,
    683                         return_pulls, return_pushes, return_storage_request, return_storage_alloc,
     368                        stats.malloc_calls, stats.malloc_0_calls, stats.malloc_storage_request, stats.malloc_storage_alloc,
     369                        stats.aalloc_calls, stats.aalloc_0_calls, stats.aalloc_storage_request, stats.aalloc_storage_alloc,
     370                        stats.calloc_calls, stats.calloc_0_calls, stats.calloc_storage_request, stats.calloc_storage_alloc,
     371                        stats.memalign_calls, stats.memalign_0_calls, stats.memalign_storage_request, stats.memalign_storage_alloc,
     372                        stats.amemalign_calls, stats.amemalign_0_calls, stats.amemalign_storage_request, stats.amemalign_storage_alloc,
     373                        stats.cmemalign_calls, stats.cmemalign_0_calls, stats.cmemalign_storage_request, stats.cmemalign_storage_alloc,
     374                        stats.resize_calls, stats.resize_0_calls, stats.resize_storage_request, stats.resize_storage_alloc,
     375                        stats.realloc_calls, stats.realloc_0_calls, stats.realloc_storage_request, stats.realloc_storage_alloc,
     376                        stats.free_calls, stats.free_null_calls, stats.free_storage_request, stats.free_storage_alloc,
    684377                        sbrk_calls, sbrk_storage,
    685                         mmap_calls, mmap_storage_request, mmap_storage_alloc,
    686                     munmap_calls, munmap_storage_request, munmap_storage_alloc,
    687                         threads_started, threads_exited,
    688                         new_heap, reused_heap
     378                        stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc,
     379                        stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc
    689380                );
    690381} // printStatsXML
    691 
    692 static HeapStatistics & collectStats( HeapStatistics & stats ) with( heapMaster ) {
    693         lock( mgrLock );
    694 
    695         stats += heapMaster.stats;
    696         for ( Heap * heap = heapManagersList; heap; heap = heap->nextHeapManager ) {
    697                 stats += heap->stats;
    698         } // for
    699 
    700         unlock( mgrLock );
    701         return stats;
    702 } // collectStats
    703382#endif // __STATISTICS__
    704383
    705384
    706 static bool setMmapStart( size_t value ) with( heapMaster ) { // true => mmapped, false => sbrk
     385// statically allocated variables => zero filled.
     386static size_t heapExpand;                                                               // sbrk advance
     387static size_t mmapStart;                                                                // cross over point for mmap
     388static unsigned int maxBucketsUsed;                                             // maximum number of buckets in use
     389// extern visibility, used by runtime kernel
     390// would be cool to remove libcfa_public but it's needed for libcfathread
     391libcfa_public size_t __page_size;                                                       // architecture pagesize
     392libcfa_public int __map_prot;                                                           // common mmap/mprotect protection
     393
     394
     395// thunk problem
     396size_t Bsearchl( unsigned int key, const unsigned int * vals, size_t dim ) {
     397        size_t l = 0, m, h = dim;
     398        while ( l < h ) {
     399                m = (l + h) / 2;
     400                if ( (unsigned int &)(vals[m]) < key ) {                // cast away const
     401                        l = m + 1;
     402                } else {
     403                        h = m;
     404                } // if
     405        } // while
     406        return l;
     407} // Bsearchl
     408
     409
     410static inline bool setMmapStart( size_t value ) {               // true => mmapped, false => sbrk
    707411  if ( value < __page_size || bucketSizes[NoBucketSizes - 1] < value ) return false;
    708412        mmapStart = value;                                                                      // set global
    709413
    710414        // find the closest bucket size less than or equal to the mmapStart size
    711         maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search
    712         verify( maxBucketsUsed < NoBucketSizes );                       // subscript failure ?
    713         verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?
     415        maxBucketsUsed = Bsearchl( (unsigned int)mmapStart, bucketSizes, NoBucketSizes ); // binary search
     416        assert( maxBucketsUsed < NoBucketSizes );                       // subscript failure ?
     417        assert( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?
    714418        return true;
    715419} // setMmapStart
     
    734438
    735439
    736 inline __attribute__((always_inline))
    737 static void checkAlign( size_t alignment ) {
     440static inline void checkAlign( size_t alignment ) {
    738441        if ( unlikely( alignment < libAlign() || ! is_pow2( alignment ) ) ) {
    739442                abort( "**** Error **** alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, libAlign() );
     
    742445
    743446
    744 inline __attribute__((always_inline))
    745 static void checkHeader( bool check, const char name[], void * addr ) {
     447static inline void checkHeader( bool check, const char name[], void * addr ) {
    746448        if ( unlikely( check ) ) {                                                      // bad address ?
    747449                abort( "**** Error **** attempt to %s storage %p with address outside the heap.\n"
     
    768470
    769471
    770 inline __attribute__((always_inline))
    771 static void fakeHeader( Heap.Storage.Header *& header, size_t & alignment ) {
     472static inline void fakeHeader( Heap.Storage.Header *& header, size_t & alignment ) {
    772473        if ( unlikely( AlignmentBit( header ) ) ) {                     // fake header ?
    773474                alignment = ClearAlignmentBit( header );                // clear flag from value
     
    782483
    783484
    784 inline __attribute__((always_inline))
    785 static bool headers( const char name[] __attribute__(( unused )), void * addr, Heap.Storage.Header *& header,
    786                                                         Heap.FreeHeader *& freeHead, size_t & size, size_t & alignment ) with( heapMaster, *heapManager ) {
     485static inline bool headers( const char name[] __attribute__(( unused )), void * addr, Heap.Storage.Header *& header,
     486                                                        Heap.FreeHeader *& freeHead, size_t & size, size_t & alignment ) with( heapManager ) {
    787487        header = HeaderAddr( addr );
    788488
     
    809509        checkHeader( header < (Heap.Storage.Header *)heapBegin || (Heap.Storage.Header *)heapEnd < header, name, addr ); // bad address ? (offset could be + or -)
    810510
    811         Heap * homeManager;
    812511        if ( unlikely( freeHead == 0p || // freed and only free-list node => null link
    813512                                   // freed and link points at another free block not to a bucket in the bucket array.
    814                                    (homeManager = freeHead->homeManager, freeHead < &homeManager->freeLists[0] ||
    815                                         &homeManager->freeLists[NoBucketSizes] <= freeHead ) ) ) {
     513                                   freeHead < &freeLists[0] || &freeLists[NoBucketSizes] <= freeHead ) ) {
    816514                abort( "**** Error **** attempt to %s storage %p with corrupted header.\n"
    817515                           "Possible cause is duplicate free on same block or overwriting of header information.",
     
    823521} // headers
    824522
    825 
    826 static void * master_extend( size_t size ) with( heapMaster ) {
    827         lock( extLock );
     523// #ifdef __CFA_DEBUG__
     524// #if __SIZEOF_POINTER__ == 4
     525// #define MASK 0xdeadbeef
     526// #else
     527// #define MASK 0xdeadbeefdeadbeef
     528// #endif
     529// #define STRIDE size_t
     530
     531// static void * Memset( void * addr, STRIDE size ) {           // debug only
     532//      if ( size % sizeof(STRIDE) != 0 ) abort( "Memset() : internal error, size %zd not multiple of %zd.", size, sizeof(STRIDE) );
     533//      if ( (STRIDE)addr % sizeof(STRIDE) != 0 ) abort( "Memset() : internal error, addr %p not multiple of %zd.", addr, sizeof(STRIDE) );
     534
     535//      STRIDE * end = (STRIDE *)addr + size / sizeof(STRIDE);
     536//      for ( STRIDE * p = (STRIDE *)addr; p < end; p += 1 ) *p = MASK;
     537//      return addr;
     538// } // Memset
     539// #endif // __CFA_DEBUG__
     540
     541
     542#define NO_MEMORY_MSG "insufficient heap memory available for allocating %zd new bytes."
     543
     544static inline void * extend( size_t size ) with( heapManager ) {
     545        lock( extlock __cfaabi_dbg_ctx2 );
    828546
    829547        ptrdiff_t rem = heapRemaining - size;
     
    831549                // If the size requested is bigger than the current remaining storage, increase the size of the heap.
    832550
    833                 size_t increase = ceiling2( size > heapExpand ? size : heapExpand, libAlign() );
     551                size_t increase = ceiling2( size > heapExpand ? size : heapExpand, __page_size );
    834552                // Do not call abort or strerror( errno ) as they may call malloc.
    835                 if ( unlikely( sbrk( increase ) == (void *)-1 ) ) {     // failed, no memory ?
    836                         unlock( extLock );
     553                if ( sbrk( increase ) == (void *)-1 ) {                 // failed, no memory ?
     554                        unlock( extlock );
    837555                        __cfaabi_bits_print_nolock( STDERR_FILENO, NO_MEMORY_MSG, size );
    838556                        _exit( EXIT_FAILURE );                                          // give up
    839557                } // if
    840                 rem = heapRemaining + increase - size;
     558
     559                // Make storage executable for thunks.
     560                if ( mprotect( (char *)heapEnd + heapRemaining, increase, __map_prot ) ) {
     561                        unlock( extlock );
     562                        __cfaabi_bits_print_nolock( STDERR_FILENO, "extend() : internal error, mprotect failure, heapEnd:%p size:%zd, errno:%d.\n", heapEnd, increase, errno );
     563                        _exit( EXIT_FAILURE );
     564                } // if
    841565
    842566                #ifdef __STATISTICS__
     
    844568                sbrk_storage += increase;
    845569                #endif // __STATISTICS__
     570
     571                #ifdef __CFA_DEBUG__
     572                // Set new memory to garbage so subsequent uninitialized usages might fail.
     573                memset( (char *)heapEnd + heapRemaining, '\xde', increase );
     574                //Memset( (char *)heapEnd + heapRemaining, increase );
     575                #endif // __CFA_DEBUG__
     576
     577                rem = heapRemaining + increase - size;
    846578        } // if
    847579
     
    849581        heapRemaining = rem;
    850582        heapEnd = (char *)heapEnd + size;
    851 
    852         unlock( extLock );
     583        unlock( extlock );
    853584        return block;
    854 } // master_extend
    855 
    856 
    857 __attribute__(( noinline ))
    858 static void * manager_extend( size_t size ) with( *heapManager ) {
    859         ptrdiff_t rem = heapReserve - size;
    860 
    861         if ( unlikely( rem < 0 ) ) {                                            // negative
    862                 // If the size requested is bigger than the current remaining reserve, use the current reserve to populate
    863                 // smaller freeLists, and increase the reserve.
    864 
    865                 rem = heapReserve;                                                              // positive
    866 
    867                 if ( rem >= bucketSizes[0] ) {                                  // minimal size ? otherwise ignore
    868                         size_t bucket;
    869                         #ifdef FASTLOOKUP
    870                         if ( likely( rem < LookupSizes ) ) bucket = lookup[rem];
    871                         #endif // FASTLOOKUP
    872                                 bucket = Bsearchl( rem, bucketSizes, heapMaster.maxBucketsUsed );
    873                         verify( 0 <= bucket && bucket <= heapMaster.maxBucketsUsed );
    874                         Heap.FreeHeader * freeHead = &(freeLists[bucket]);
    875 
    876                         // The remaining storage many not be bucket size, whereas all other allocations are. Round down to previous
    877                         // bucket size in this case.
    878                         if ( unlikely( freeHead->blockSize > (size_t)rem ) ) freeHead -= 1;
    879                         Heap.Storage * block = (Heap.Storage *)heapBuffer;
    880 
    881                         block->header.kind.real.next = freeHead->freeList; // push on stack
    882                         freeHead->freeList = block;
    883                 } // if
    884 
    885                 size_t increase = ceiling( size > ( heapMaster.heapExpand / 10 ) ? size : ( heapMaster.heapExpand / 10 ), libAlign() );
    886                 heapBuffer = master_extend( increase );
    887                 rem = increase - size;
    888         } // if
    889 
    890         Heap.Storage * block = (Heap.Storage *)heapBuffer;
    891         heapReserve = rem;
    892         heapBuffer = (char *)heapBuffer + size;
    893 
    894         return block;
    895 } // manager_extend
    896 
    897 
    898 #define BOOT_HEAP_MANAGER \
    899         if ( unlikely( ! heapMasterBootFlag ) ) { \
    900                 heapManagerCtor(); /* trigger for first heap */ \
    901         } /* if */
    902 
    903 #ifdef __STATISTICS__
    904 #define STAT_NAME __counter
    905 #define STAT_PARM , unsigned int STAT_NAME
    906 #define STAT_ARG( name ) , name
    907 #define STAT_0_CNT( counter ) stats.counters[counter].calls_0 += 1
    908 #else
    909 #define STAT_NAME
    910 #define STAT_PARM
    911 #define STAT_ARG( name )
    912 #define STAT_0_CNT( counter )
    913 #endif // __STATISTICS__
    914 
    915 #define PROLOG( counter, ... ) \
    916         BOOT_HEAP_MANAGER; \
    917         if ( unlikely( size == 0 ) ||                                           /* 0 BYTE ALLOCATION RETURNS NULL POINTER */ \
    918                 unlikely( size > ULONG_MAX - sizeof(Heap.Storage) ) ) { /* error check */ \
    919                 STAT_0_CNT( counter ); \
    920                 __VA_ARGS__; \
    921                 return 0p; \
    922         } /* if */
    923 
    924 
    925 #define SCRUB_SIZE 1024lu
    926 // Do not use '\xfe' for scrubbing because dereferencing an address composed of it causes a SIGSEGV *without* a valid IP
    927 // pointer in the interrupt frame.
    928 #define SCRUB '\xff'
    929 
    930 static void * doMalloc( size_t size STAT_PARM ) libcfa_nopreempt with( *heapManager ) {
    931         PROLOG( STAT_NAME );
    932 
    933         verify( heapManager );
    934         Heap.Storage * block;                                                           // pointer to new block of storage
     585} // extend
     586
     587
     588static inline void * doMalloc( size_t size ) with( heapManager ) {
     589        Heap.Storage * block;                                           // pointer to new block of storage
    935590
    936591        // Look up size in the size list.  Make sure the user request includes space for the header that must be allocated
    937592        // along with the block and is a multiple of the alignment size.
     593
    938594        size_t tsize = size + sizeof(Heap.Storage);
    939595
    940         #ifdef __STATISTICS__
    941         stats.counters[STAT_NAME].calls += 1;
    942         stats.counters[STAT_NAME].request += size;
    943         #endif // __STATISTICS__
    944 
    945         #ifdef __CFA_DEBUG__
    946         allocUnfreed += size;
    947         #endif // __CFA_DEBUG__
    948 
    949         if ( likely( tsize < heapMaster.mmapStart ) ) {         // small size => sbrk
    950                 size_t bucket;
     596        if ( likely( tsize < mmapStart ) ) {                            // small size => sbrk
     597                size_t posn;
    951598                #ifdef FASTLOOKUP
    952                 if ( likely( tsize < LookupSizes ) ) bucket = lookup[tsize];
     599                if ( tsize < LookupSizes ) posn = lookup[tsize];
    953600                else
    954601                #endif // FASTLOOKUP
    955                         bucket = Bsearchl( tsize, bucketSizes, heapMaster.maxBucketsUsed );
    956                 verify( 0 <= bucket && bucket <= heapMaster.maxBucketsUsed );
    957                 Heap.FreeHeader * freeHead = &freeLists[bucket];
    958 
    959                 verify( freeHead <= &freeLists[heapMaster.maxBucketsUsed] ); // subscripting error ?
    960                 verify( tsize <= freeHead->blockSize );                 // search failure ?
    961 
    962                 tsize = freeHead->blockSize;                                    // total space needed for request
    963                 #ifdef __STATISTICS__
    964                 stats.counters[STAT_NAME].alloc += tsize;
    965                 #endif // __STATISTICS__
     602                        posn = Bsearchl( (unsigned int)tsize, bucketSizes, (size_t)maxBucketsUsed );
     603                Heap.FreeHeader * freeElem = &freeLists[posn];
     604                verify( freeElem <= &freeLists[maxBucketsUsed] ); // subscripting error ?
     605                verify( tsize <= freeElem->blockSize );                 // search failure ?
     606                tsize = freeElem->blockSize;                                    // total space needed for request
    966607
    967608                // Spin until the lock is acquired for this particular size of block.
    968609
    969610                #if BUCKETLOCK == SPINLOCK
    970                 block = freeHead->freeList;                                             // remove node from stack
     611                lock( freeElem->lock __cfaabi_dbg_ctx2 );
     612                block = freeElem->freeList;                                             // remove node from stack
    971613                #else
    972                 block = pop( freeHead->freeList );
     614                block = pop( freeElem->freeList );
    973615                #endif // BUCKETLOCK
    974616                if ( unlikely( block == 0p ) ) {                                // no free block ?
    975                         #ifdef OWNERSHIP
    976                         // Freelist for that size is empty, so carve it out of the heap, if there is enough left, or get some more
     617                        #if BUCKETLOCK == SPINLOCK
     618                        unlock( freeElem->lock );
     619                        #endif // BUCKETLOCK
     620
     621                        // Freelist for that size was empty, so carve it out of the heap if there's enough left, or get some more
    977622                        // and then carve it off.
    978                         #ifdef RETURNSPIN
    979                         #if BUCKETLOCK == SPINLOCK
    980                         lock( freeHead->returnLock );
    981                         block = freeHead->returnList;
    982                         freeHead->returnList = 0p;
    983                         unlock( freeHead->returnLock );
    984                         #else
    985                         block = __atomic_exchange_n( &freeHead->returnList, nullptr, __ATOMIC_SEQ_CST );
    986                         #endif // RETURNSPIN
    987 
    988                         if ( likely( block == 0p ) ) {                  // return list also empty?
    989                         #endif // OWNERSHIP
    990                                 // Do not leave kernel thread as manager_extend accesses heapManager.
    991                                 disable_interrupts();
    992                                 block = (Heap.Storage *)manager_extend( tsize ); // mutual exclusion on call
    993                                 enable_interrupts( false );
    994 
    995                                 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
    996 
    997                                 #ifdef __CFA_DEBUG__
    998                                 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes.
    999                                 memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) );
    1000                                 #endif // __CFA_DEBUG__
    1001                         #endif // BUCKETLOCK
    1002                         #ifdef OWNERSHIP
    1003                         } else {                                                                        // merge returnList into freeHead
    1004                                 #ifdef __STATISTICS__
    1005                                 stats.return_pulls += 1;
    1006                                 #endif // __STATISTICS__
    1007 
    1008                                 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
    1009 
    1010                                 freeHead->freeList = block->header.kind.real.next;
    1011                         } // if
    1012                         #endif // OWNERSHIP
     623
     624                        block = (Heap.Storage *)extend( tsize );        // mutual exclusion on call
     625                #if BUCKETLOCK == SPINLOCK
    1013626                } else {
    1014                         // Memory is scrubbed in doFree.
    1015                         freeHead->freeList = block->header.kind.real.next;
    1016                 } // if
    1017 
    1018                 block->header.kind.real.home = freeHead;                // pointer back to free list of apropriate size
     627                        freeElem->freeList = block->header.kind.real.next;
     628                        unlock( freeElem->lock );
     629                #endif // BUCKETLOCK
     630                } // if
     631
     632                block->header.kind.real.home = freeElem;                // pointer back to free list of apropriate size
    1019633        } else {                                                                                        // large size => mmap
    1020634  if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p;
    1021635                tsize = ceiling2( tsize, __page_size );                 // must be multiple of page size
    1022636                #ifdef __STATISTICS__
    1023                 stats.counters[STAT_NAME].alloc += tsize;
    1024                 stats.mmap_calls += 1;
    1025                 stats.mmap_storage_request += size;
    1026                 stats.mmap_storage_alloc += tsize;
    1027                 #endif // __STATISTICS__
    1028 
    1029                 disable_interrupts();
    1030                 block = (Heap.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
    1031                 enable_interrupts( false );
    1032 
    1033                 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
    1034 
    1035                 if ( unlikely( block == (Heap.Storage *)MAP_FAILED ) ) { // failed ?
     637                __atomic_add_fetch( &stats.mmap_calls, 1, __ATOMIC_SEQ_CST );
     638                __atomic_add_fetch( &stats.mmap_storage_request, size, __ATOMIC_SEQ_CST );
     639                __atomic_add_fetch( &stats.mmap_storage_alloc, tsize, __ATOMIC_SEQ_CST );
     640                #endif // __STATISTICS__
     641
     642                block = (Heap.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 );
     643                if ( block == (Heap.Storage *)MAP_FAILED ) { // failed ?
    1036644                        if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory
    1037645                        // Do not call strerror( errno ) as it may call malloc.
    1038                         abort( "attempt to allocate large object (> %zu) of size %zu bytes and mmap failed with errno %d.", size, heapMaster.mmapStart, errno );
    1039                 } // if
     646                        abort( "(Heap &)0x%p.doMalloc() : internal error, mmap failure, size:%zu errno:%d.", &heapManager, tsize, errno );
     647                } //if
     648                #ifdef __CFA_DEBUG__
     649                // Set new memory to garbage so subsequent uninitialized usages might fail.
     650                memset( block, '\xde', tsize );
     651                //Memset( block, tsize );
     652                #endif // __CFA_DEBUG__
    1040653                block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap
    1041 
    1042                 #ifdef __CFA_DEBUG__
    1043                 // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes.  The rest of
    1044                 // the storage set to 0 by mmap.
    1045                 memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) );
    1046                 #endif // __CFA_DEBUG__
    1047654        } // if
    1048655
     
    1052659
    1053660        #ifdef __CFA_DEBUG__
     661        __atomic_add_fetch( &allocUnfreed, tsize, __ATOMIC_SEQ_CST );
    1054662        if ( traceHeap() ) {
    1055663                char helpText[64];
     
    1059667        #endif // __CFA_DEBUG__
    1060668
    1061 //      poll_interrupts();                                                                      // call rollforward
    1062 
    1063669        return addr;
    1064670} // doMalloc
    1065671
    1066672
    1067 static void doFree( void * addr ) libcfa_nopreempt with( *heapManager ) {
    1068         verify( addr );
    1069 
    1070         // detect free after thread-local storage destruction and use global stats in that case
     673static inline void doFree( void * addr ) with( heapManager ) {
     674        #ifdef __CFA_DEBUG__
     675        if ( unlikely( heapManager.heapBegin == 0p ) ) {
     676                abort( "doFree( %p ) : internal error, called before heap is initialized.", addr );
     677        } // if
     678        #endif // __CFA_DEBUG__
    1071679
    1072680        Heap.Storage.Header * header;
    1073         Heap.FreeHeader * freeHead;
    1074         size_t size, alignment;
    1075 
    1076         bool mapped = headers( "free", addr, header, freeHead, size, alignment );
    1077         #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ )
    1078         size_t rsize = header->kind.real.size;                          // optimization
    1079         #endif // __STATISTICS__ || __CFA_DEBUG__
    1080 
    1081         #ifdef __STATISTICS__
    1082         stats.free_storage_request += rsize;
    1083         stats.free_storage_alloc += size;
    1084         #endif // __STATISTICS__
    1085 
    1086         #ifdef __CFA_DEBUG__
    1087         allocUnfreed -= rsize;
    1088         #endif // __CFA_DEBUG__
    1089 
    1090         if ( unlikely( mapped ) ) {                                                     // mmapped ?
    1091                 #ifdef __STATISTICS__
    1092                 stats.munmap_calls += 1;
    1093                 stats.munmap_storage_request += rsize;
    1094                 stats.munmap_storage_alloc += size;
    1095                 #endif // __STATISTICS__
    1096 
    1097                 // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
    1098 
    1099                 // Does not matter where this storage is freed.
    1100                 if ( unlikely( munmap( header, size ) == -1 ) ) {
    1101                         // Do not call strerror( errno ) as it may call malloc.
    1102                         abort( "attempt to deallocate large object %p and munmap failed with errno %d.\n"
    1103                                    "Possible cause is invalid delete pointer: either not allocated or with corrupt header.",
    1104                                    addr, errno );
     681        Heap.FreeHeader * freeElem;
     682        size_t size, alignment;                                                         // not used (see realloc)
     683
     684        if ( headers( "free", addr, header, freeElem, size, alignment ) ) { // mmapped ?
     685                #ifdef __STATISTICS__
     686                __atomic_add_fetch( &stats.munmap_calls, 1, __ATOMIC_SEQ_CST );
     687                __atomic_add_fetch( &stats.munmap_storage_request, header->kind.real.size, __ATOMIC_SEQ_CST );
     688                __atomic_add_fetch( &stats.munmap_storage_alloc, size, __ATOMIC_SEQ_CST );
     689                #endif // __STATISTICS__
     690                if ( munmap( header, size ) == -1 ) {
     691                        abort( "Attempt to deallocate storage %p not allocated or with corrupt header.\n"
     692                                   "Possible cause is invalid pointer.",
     693                                   addr );
    1105694                } // if
    1106695        } else {
    1107696                #ifdef __CFA_DEBUG__
    1108                 // memset is NOT always inlined!
    1109                 disable_interrupts();
    1110                 // Scrub old memory so subsequent usages might fail. Only scrub the first/last SCRUB_SIZE bytes.
    1111                 char * data = ((Heap.Storage *)header)->data;   // data address
    1112                 size_t dsize = size - sizeof(Heap.Storage);             // data size
    1113                 if ( dsize <= SCRUB_SIZE * 2 ) {
    1114                         memset( data, SCRUB, dsize );                           // scrub all
    1115                 } else {
    1116                         memset( data, SCRUB, SCRUB_SIZE );                      // scrub front
    1117                         memset( data + dsize - SCRUB_SIZE, SCRUB, SCRUB_SIZE ); // scrub back
    1118                 } // if
    1119                 enable_interrupts( false );
     697                // Set free memory to garbage so subsequent usages might fail.
     698                memset( ((Heap.Storage *)header)->data, '\xde', freeElem->blockSize - sizeof( Heap.Storage ) );
     699                //Memset( ((Heap.Storage *)header)->data, freeElem->blockSize - sizeof( Heap.Storage ) );
    1120700                #endif // __CFA_DEBUG__
    1121701
    1122                 if ( likely( heapManager == freeHead->homeManager ) ) { // belongs to this thread
    1123                         header->kind.real.next = freeHead->freeList; // push on stack
    1124                         freeHead->freeList = (Heap.Storage *)header;
    1125                 } else {                                                                                // return to thread owner
    1126                         verify( heapManager );
    1127 
    1128                         #ifdef OWNERSHIP
    1129                         #ifdef RETURNSPIN
    1130                         lock( freeHead->returnLock );
    1131                         header->kind.real.next = freeHead->returnList; // push to bucket return list
    1132                         freeHead->returnList = (Heap.Storage *)header;
    1133                         unlock( freeHead->returnLock );
    1134                         #else                                                                           // lock free
    1135                         header->kind.real.next = freeHead->returnList; // link new node to top node
    1136                         // CAS resets header->kind.real.next = freeHead->returnList on failure
    1137                         while ( ! __atomic_compare_exchange_n( &freeHead->returnList, &header->kind.real.next, header,
    1138                                                                                                    false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) );
    1139                         #endif // RETURNSPIN
    1140 
    1141                         #else                                                                           // no OWNERSHIP
    1142 
    1143                         freeHead = &heap->freeLists[ClearStickyBits( header->kind.real.home ) - &freeHead->homeManager->freeLists[0]];
    1144                         header->kind.real.next = freeHead->freeList; // push on stack
    1145                         freeHead->freeList = (Heap.Storage *)header;
    1146                         #endif // ! OWNERSHIP
    1147 
    1148                         #ifdef __U_STATISTICS__
    1149                         stats.return_pushes += 1;
    1150                         stats.return_storage_request += rsize;
    1151                         stats.return_storage_alloc += size;
    1152                         #endif // __U_STATISTICS__
    1153 
    1154                         // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
    1155                 } // if
     702                #ifdef __STATISTICS__
     703                __atomic_add_fetch( &stats.free_calls, 1, __ATOMIC_SEQ_CST );
     704                __atomic_add_fetch( &stats.free_storage_request, header->kind.real.size, __ATOMIC_SEQ_CST );
     705                __atomic_add_fetch( &stats.free_storage_alloc, size, __ATOMIC_SEQ_CST );
     706                #endif // __STATISTICS__
     707
     708                #if BUCKETLOCK == SPINLOCK
     709                lock( freeElem->lock __cfaabi_dbg_ctx2 );               // acquire spin lock
     710                header->kind.real.next = freeElem->freeList;    // push on stack
     711                freeElem->freeList = (Heap.Storage *)header;
     712                unlock( freeElem->lock );                                               // release spin lock
     713                #else
     714                push( freeElem->freeList, *(Heap.Storage *)header );
     715                #endif // BUCKETLOCK
    1156716        } // if
    1157717
    1158718        #ifdef __CFA_DEBUG__
     719        __atomic_add_fetch( &allocUnfreed, -size, __ATOMIC_SEQ_CST );
    1159720        if ( traceHeap() ) {
    1160721                char helpText[64];
     
    1163724        } // if
    1164725        #endif // __CFA_DEBUG__
    1165 
    1166 //      poll_interrupts();                                                                      // call rollforward
    1167726} // doFree
    1168727
    1169728
    1170 size_t prtFree( Heap & manager ) with( manager ) {
     729static size_t prtFree( Heap & manager ) with( manager ) {
    1171730        size_t total = 0;
    1172731        #ifdef __STATISTICS__
     
    1174733        __cfaabi_bits_print_nolock( STDERR_FILENO, "\nBin lists (bin size : free blocks on list)\n" );
    1175734        #endif // __STATISTICS__
    1176         for ( unsigned int i = 0; i < heapMaster.maxBucketsUsed; i += 1 ) {
     735        for ( unsigned int i = 0; i < maxBucketsUsed; i += 1 ) {
    1177736                size_t size = freeLists[i].blockSize;
    1178737                #ifdef __STATISTICS__
     
    1205764        __cfaabi_bits_release();
    1206765        #endif // __STATISTICS__
    1207         return (char *)heapMaster.heapEnd - (char *)heapMaster.heapBegin - total;
     766        return (char *)heapEnd - (char *)heapBegin - total;
    1208767} // prtFree
    1209768
    1210769
    1211 #ifdef __STATISTICS__
    1212 static void incCalls( long int statName ) libcfa_nopreempt {
    1213         heapManager->stats.counters[statName].calls += 1;
    1214 } // incCalls
    1215 
    1216 static void incZeroCalls( long int statName ) libcfa_nopreempt {
    1217         heapManager->stats.counters[statName].calls_0 += 1;
    1218 } // incZeroCalls
    1219 #endif // __STATISTICS__
    1220 
    1221 #ifdef __CFA_DEBUG__
    1222 static void incUnfreed( size_t offset ) libcfa_nopreempt {
    1223         heapManager->allocUnfreed += offset;
    1224 } // incUnfreed
    1225 #endif // __CFA_DEBUG__
    1226 
    1227 
    1228 static void * memalignNoStats( size_t alignment, size_t size STAT_PARM ) {
     770static void ?{}( Heap & manager ) with( manager ) {
     771        __page_size = sysconf( _SC_PAGESIZE );
     772        __map_prot = PROT_READ | PROT_WRITE | PROT_EXEC;
     773
     774        for ( unsigned int i = 0; i < NoBucketSizes; i += 1 ) { // initialize the free lists
     775                freeLists[i].blockSize = bucketSizes[i];
     776        } // for
     777
     778        #ifdef FASTLOOKUP
     779        unsigned int idx = 0;
     780        for ( unsigned int i = 0; i < LookupSizes; i += 1 ) {
     781                if ( i > bucketSizes[idx] ) idx += 1;
     782                lookup[i] = idx;
     783        } // for
     784        #endif // FASTLOOKUP
     785
     786        if ( ! setMmapStart( malloc_mmap_start() ) ) {
     787                abort( "Heap : internal error, mmap start initialization failure." );
     788        } // if
     789        heapExpand = malloc_expansion();
     790
     791        char * end = (char *)sbrk( 0 );
     792        heapBegin = heapEnd = sbrk( (char *)ceiling2( (long unsigned int)end, __page_size ) - end ); // move start of heap to multiple of alignment
     793} // Heap
     794
     795
     796static void ^?{}( Heap & ) {
     797        #ifdef __STATISTICS__
     798        if ( traceHeapTerm() ) {
     799                printStats();
     800                // prtUnfreed() called in heapAppStop()
     801        } // if
     802        #endif // __STATISTICS__
     803} // ~Heap
     804
     805
     806static void memory_startup( void ) __attribute__(( constructor( STARTUP_PRIORITY_MEMORY ) ));
     807void memory_startup( void ) {
     808        #ifdef __CFA_DEBUG__
     809        if ( heapBoot ) {                                                                       // check for recursion during system boot
     810                abort( "boot() : internal error, recursively invoked during system boot." );
     811        } // if
     812        heapBoot = true;
     813        #endif // __CFA_DEBUG__
     814
     815        //verify( heapManager.heapBegin != 0 );
     816        //heapManager{};
     817        if ( heapManager.heapBegin == 0p ) heapManager{};       // sanity check
     818} // memory_startup
     819
     820static void memory_shutdown( void ) __attribute__(( destructor( STARTUP_PRIORITY_MEMORY ) ));
     821void memory_shutdown( void ) {
     822        ^heapManager{};
     823} // memory_shutdown
     824
     825
     826static inline void * mallocNoStats( size_t size ) {             // necessary for malloc statistics
     827        verify( heapManager.heapBegin != 0p );                          // called before memory_startup ?
     828  if ( unlikely( size ) == 0 ) return 0p;                               // 0 BYTE ALLOCATION RETURNS NULL POINTER
     829
     830#if __SIZEOF_POINTER__ == 8
     831        verify( size < ((typeof(size_t))1 << 48) );
     832#endif // __SIZEOF_POINTER__ == 8
     833        return doMalloc( size );
     834} // mallocNoStats
     835
     836
     837static inline void * memalignNoStats( size_t alignment, size_t size ) {
     838  if ( unlikely( size ) == 0 ) return 0p;                               // 0 BYTE ALLOCATION RETURNS NULL POINTER
     839
     840        #ifdef __CFA_DEBUG__
    1229841        checkAlign( alignment );                                                        // check alignment
    1230 
    1231         // if alignment <= default alignment or size == 0, do normal malloc as two headers are unnecessary
    1232   if ( unlikely( alignment <= libAlign() || size == 0 ) ) return doMalloc( size STAT_ARG( STAT_NAME ) );
     842        #endif // __CFA_DEBUG__
     843
     844        // if alignment <= default alignment, do normal malloc as two headers are unnecessary
     845  if ( unlikely( alignment <= libAlign() ) ) return mallocNoStats( size );
    1233846
    1234847        // Allocate enough storage to guarantee an address on the alignment boundary, and sufficient space before it for
     
    1241854        // subtract libAlign() because it is already the minimum alignment
    1242855        // add sizeof(Storage) for fake header
    1243         size_t offset = alignment - libAlign() + sizeof(Heap.Storage);
    1244         char * addr = (char *)doMalloc( size + offset STAT_ARG( STAT_NAME ) );
     856        char * addr = (char *)mallocNoStats( size + alignment - libAlign() + sizeof(Heap.Storage) );
    1245857
    1246858        // address in the block of the "next" alignment address
     
    1248860
    1249861        // address of header from malloc
    1250         Heap.Storage.Header * realHeader = HeaderAddr( addr );
    1251         realHeader->kind.real.size = size;                                      // correct size to eliminate above alignment offset
    1252         #ifdef __CFA_DEBUG__
    1253         incUnfreed( -offset );                                                          // adjustment off the offset from call to doMalloc
    1254         #endif // __CFA_DEBUG__
    1255 
    1256         // address of fake header *before* the alignment location
     862        Heap.Storage.Header * RealHeader = HeaderAddr( addr );
     863        RealHeader->kind.real.size = size;                                      // correct size to eliminate above alignment offset
     864        // address of fake header * before* the alignment location
    1257865        Heap.Storage.Header * fakeHeader = HeaderAddr( user );
    1258 
    1259866        // SKULLDUGGERY: insert the offset to the start of the actual storage block and remember alignment
    1260         fakeHeader->kind.fake.offset = (char *)fakeHeader - (char *)realHeader;
     867        fakeHeader->kind.fake.offset = (char *)fakeHeader - (char *)RealHeader;
    1261868        // SKULLDUGGERY: odd alignment implies fake header
    1262869        fakeHeader->kind.fake.alignment = MarkAlignmentBit( alignment );
     
    1273880        // then malloc() returns a unique pointer value that can later be successfully passed to free().
    1274881        void * malloc( size_t size ) libcfa_public {
    1275                 return doMalloc( size STAT_ARG( MALLOC ) );
     882                #ifdef __STATISTICS__
     883                if ( likely( size > 0 ) ) {
     884                        __atomic_add_fetch( &stats.malloc_calls, 1, __ATOMIC_SEQ_CST );
     885                        __atomic_add_fetch( &stats.malloc_storage_request, size, __ATOMIC_SEQ_CST );
     886                } else {
     887                        __atomic_add_fetch( &stats.malloc_0_calls, 1, __ATOMIC_SEQ_CST );
     888                } // if
     889                #endif // __STATISTICS__
     890
     891                return mallocNoStats( size );
    1276892        } // malloc
    1277893
     
    1279895        // Same as malloc() except size bytes is an array of dim elements each of elemSize bytes.
    1280896        void * aalloc( size_t dim, size_t elemSize ) libcfa_public {
    1281                 return doMalloc( dim * elemSize STAT_ARG( AALLOC ) );
     897                size_t size = dim * elemSize;
     898                #ifdef __STATISTICS__
     899                if ( likely( size > 0 ) ) {
     900                        __atomic_add_fetch( &stats.aalloc_calls, 1, __ATOMIC_SEQ_CST );
     901                        __atomic_add_fetch( &stats.aalloc_storage_request, size, __ATOMIC_SEQ_CST );
     902                } else {
     903                        __atomic_add_fetch( &stats.aalloc_0_calls, 1, __ATOMIC_SEQ_CST );
     904                } // if
     905                #endif // __STATISTICS__
     906
     907                return mallocNoStats( size );
    1282908        } // aalloc
    1283909
     
    1286912        void * calloc( size_t dim, size_t elemSize ) libcfa_public {
    1287913                size_t size = dim * elemSize;
    1288                 char * addr = (char *)doMalloc( size STAT_ARG( CALLOC ) );
    1289 
    1290           if ( unlikely( addr == NULL ) ) return NULL;          // stop further processing if 0p is returned
     914          if ( unlikely( size ) == 0 ) {                        // 0 BYTE ALLOCATION RETURNS NULL POINTER
     915                        #ifdef __STATISTICS__
     916                        __atomic_add_fetch( &stats.calloc_0_calls, 1, __ATOMIC_SEQ_CST );
     917                        #endif // __STATISTICS__
     918                        return 0p;
     919                } // if
     920                #ifdef __STATISTICS__
     921                __atomic_add_fetch( &stats.calloc_calls, 1, __ATOMIC_SEQ_CST );
     922                __atomic_add_fetch( &stats.calloc_storage_request, dim * elemSize, __ATOMIC_SEQ_CST );
     923                #endif // __STATISTICS__
     924
     925                char * addr = (char *)mallocNoStats( size );
    1291926
    1292927                Heap.Storage.Header * header;
    1293                 Heap.FreeHeader * freeHead;
     928                Heap.FreeHeader * freeElem;
    1294929                size_t bsize, alignment;
    1295930
     
    1297932                bool mapped =
    1298933                        #endif // __CFA_DEBUG__
    1299                         headers( "calloc", addr, header, freeHead, bsize, alignment );
     934                        headers( "calloc", addr, header, freeElem, bsize, alignment );
    1300935
    1301936                #ifndef __CFA_DEBUG__
    1302937                // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
    1303                 if ( likely( ! mapped ) )
     938                if ( ! mapped )
    1304939                #endif // __CFA_DEBUG__
    1305940                        // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined
     
    1317952        // call to malloc(), alloc(), calloc() or realloc(). If the area pointed to was moved, a free(oaddr) is done.
    1318953        void * resize( void * oaddr, size_t size ) libcfa_public {
    1319           if ( unlikely( oaddr == 0p ) ) {                              // => malloc( size )
    1320                         return doMalloc( size STAT_ARG( RESIZE ) );
    1321                 } // if
    1322 
    1323                 PROLOG( RESIZE, doFree( oaddr ) );                              // => free( oaddr )
     954                // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
     955          if ( unlikely( size == 0 ) ) {                                        // special cases
     956                        #ifdef __STATISTICS__
     957                        __atomic_add_fetch( &stats.resize_0_calls, 1, __ATOMIC_SEQ_CST );
     958                        #endif // __STATISTICS__
     959                        free( oaddr );
     960                        return 0p;
     961                } // if
     962                #ifdef __STATISTICS__
     963                __atomic_add_fetch( &stats.resize_calls, 1, __ATOMIC_SEQ_CST );
     964                #endif // __STATISTICS__
     965
     966          if ( unlikely( oaddr == 0p ) ) {
     967                        #ifdef __STATISTICS__
     968                        __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST );
     969                        #endif // __STATISTICS__
     970                        return mallocNoStats( size );
     971                } // if
    1324972
    1325973                Heap.Storage.Header * header;
    1326                 Heap.FreeHeader * freeHead;
     974                Heap.FreeHeader * freeElem;
    1327975                size_t bsize, oalign;
    1328                 headers( "resize", oaddr, header, freeHead, bsize, oalign );
     976                headers( "resize", oaddr, header, freeElem, bsize, oalign );
    1329977
    1330978                size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket
     
    1332980                if ( oalign == libAlign() && size <= odsize && odsize <= size * 2 ) { // allow 50% wasted storage for smaller size
    1333981                        ClearZeroFillBit( header );                                     // no alignment and turn off 0 fill
    1334                         #ifdef __CFA_DEBUG__
    1335                         incUnfreed( size - header->kind.real.size ); // adjustment off the size difference
    1336                         #endif // __CFA_DEBUG__
    1337982                        header->kind.real.size = size;                          // reset allocation size
    1338                         #ifdef __STATISTICS__
    1339                         incCalls( RESIZE );
    1340                         #endif // __STATISTICS__
    1341983                        return oaddr;
    1342984                } // if
    1343985
     986                #ifdef __STATISTICS__
     987                __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST );
     988                #endif // __STATISTICS__
     989
    1344990                // change size, DO NOT preserve STICKY PROPERTIES.
    1345                 doFree( oaddr );                                                                // free previous storage
    1346 
    1347                 return doMalloc( size STAT_ARG( RESIZE ) );             // create new area
     991                free( oaddr );
     992                return mallocNoStats( size );                                   // create new area
    1348993        } // resize
    1349994
     
    1352997        // the old and new sizes.
    1353998        void * realloc( void * oaddr, size_t size ) libcfa_public {
    1354           if ( unlikely( oaddr == 0p ) ) {                                      // => malloc( size )
    1355                   return doMalloc( size STAT_ARG( REALLOC ) );
    1356                 } // if
    1357 
    1358                 PROLOG( REALLOC, doFree( oaddr ) );                             // => free( oaddr )
     999                // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
     1000          if ( unlikely( size == 0 ) ) {                                        // special cases
     1001                        #ifdef __STATISTICS__
     1002                        __atomic_add_fetch( &stats.realloc_0_calls, 1, __ATOMIC_SEQ_CST );
     1003                        #endif // __STATISTICS__
     1004                        free( oaddr );
     1005                        return 0p;
     1006                } // if
     1007                #ifdef __STATISTICS__
     1008                __atomic_add_fetch( &stats.realloc_calls, 1, __ATOMIC_SEQ_CST );
     1009                #endif // __STATISTICS__
     1010
     1011          if ( unlikely( oaddr == 0p ) ) {
     1012                        #ifdef __STATISTICS__
     1013                        __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST );
     1014                        #endif // __STATISTICS__
     1015                        return mallocNoStats( size );
     1016                } // if
    13591017
    13601018                Heap.Storage.Header * header;
    1361                 Heap.FreeHeader * freeHead;
     1019                Heap.FreeHeader * freeElem;
    13621020                size_t bsize, oalign;
    1363                 headers( "realloc", oaddr, header, freeHead, bsize, oalign );
     1021                headers( "realloc", oaddr, header, freeElem, bsize, oalign );
    13641022
    13651023                size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket
     
    13671025                bool ozfill = ZeroFillBit( header );                    // old allocation zero filled
    13681026          if ( unlikely( size <= odsize ) && odsize <= size * 2 ) { // allow up to 50% wasted storage
    1369                         #ifdef __CFA_DEBUG__
    1370                         incUnfreed( size - header->kind.real.size ); // adjustment off the size difference
    1371                         #endif // __CFA_DEBUG__
    1372                         header->kind.real.size = size;                          // reset allocation size
     1027                        header->kind.real.size = size;                          // reset allocation size
    13731028                        if ( unlikely( ozfill ) && size > osize ) {     // previous request zero fill and larger ?
    13741029                                memset( (char *)oaddr + osize, '\0', size - osize ); // initialize added storage
    13751030                        } // if
    1376                         #ifdef __STATISTICS__
    1377                         incCalls( REALLOC );
    1378                         #endif // __STATISTICS__
    13791031                        return oaddr;
    13801032                } // if
    13811033
     1034                #ifdef __STATISTICS__
     1035                __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST );
     1036                #endif // __STATISTICS__
     1037
    13821038                // change size and copy old content to new storage
    13831039
    13841040                void * naddr;
    1385                 if ( likely( oalign <= libAlign() ) ) {                 // previous request not aligned ?
    1386                         naddr = doMalloc( size STAT_ARG( REALLOC ) ); // create new area
     1041                if ( likely( oalign == libAlign() ) ) {                 // previous request not aligned ?
     1042                        naddr = mallocNoStats( size );                          // create new area
    13871043                } else {
    1388                         naddr = memalignNoStats( oalign, size STAT_ARG( REALLOC ) ); // create new aligned area
    1389                 } // if
    1390 
    1391                 headers( "realloc", naddr, header, freeHead, bsize, oalign );
    1392                 // To preserve prior fill, the entire bucket must be copied versus the size.
     1044                        naddr = memalignNoStats( oalign, size );        // create new aligned area
     1045                } // if
     1046
     1047                headers( "realloc", naddr, header, freeElem, bsize, oalign );
    13931048                memcpy( naddr, oaddr, min( osize, size ) );             // copy bytes
    1394                 doFree( oaddr );                                                                // free previous storage
     1049                free( oaddr );
    13951050
    13961051                if ( unlikely( ozfill ) ) {                                             // previous request zero fill ?
     
    14121067        // Same as malloc() except the memory address is a multiple of alignment, which must be a power of two. (obsolete)
    14131068        void * memalign( size_t alignment, size_t size ) libcfa_public {
    1414                 return memalignNoStats( alignment, size STAT_ARG( MEMALIGN ) );
     1069                #ifdef __STATISTICS__
     1070                if ( likely( size > 0 ) ) {
     1071                        __atomic_add_fetch( &stats.memalign_calls, 1, __ATOMIC_SEQ_CST );
     1072                        __atomic_add_fetch( &stats.memalign_storage_request, size, __ATOMIC_SEQ_CST );
     1073                } else {
     1074                        __atomic_add_fetch( &stats.memalign_0_calls, 1, __ATOMIC_SEQ_CST );
     1075                } // if
     1076                #endif // __STATISTICS__
     1077
     1078                return memalignNoStats( alignment, size );
    14151079        } // memalign
    14161080
     
    14181082        // Same as aalloc() with memory alignment.
    14191083        void * amemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public {
    1420                 return memalignNoStats( alignment, dim * elemSize STAT_ARG( AMEMALIGN ) );
     1084                size_t size = dim * elemSize;
     1085                #ifdef __STATISTICS__
     1086                if ( likely( size > 0 ) ) {
     1087                        __atomic_add_fetch( &stats.cmemalign_calls, 1, __ATOMIC_SEQ_CST );
     1088                        __atomic_add_fetch( &stats.cmemalign_storage_request, size, __ATOMIC_SEQ_CST );
     1089                } else {
     1090                        __atomic_add_fetch( &stats.cmemalign_0_calls, 1, __ATOMIC_SEQ_CST );
     1091                } // if
     1092                #endif // __STATISTICS__
     1093
     1094                return memalignNoStats( alignment, size );
    14211095        } // amemalign
    14221096
     
    14251099        void * cmemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public {
    14261100                size_t size = dim * elemSize;
    1427                 char * addr = (char *)memalignNoStats( alignment, size STAT_ARG( CMEMALIGN ) );
    1428 
    1429           if ( unlikely( addr == NULL ) ) return NULL;          // stop further processing if 0p is returned
     1101          if ( unlikely( size ) == 0 ) {                                        // 0 BYTE ALLOCATION RETURNS NULL POINTER
     1102                        #ifdef __STATISTICS__
     1103                        __atomic_add_fetch( &stats.cmemalign_0_calls, 1, __ATOMIC_SEQ_CST );
     1104                        #endif // __STATISTICS__
     1105                        return 0p;
     1106                } // if
     1107                #ifdef __STATISTICS__
     1108                __atomic_add_fetch( &stats.cmemalign_calls, 1, __ATOMIC_SEQ_CST );
     1109                __atomic_add_fetch( &stats.cmemalign_storage_request, dim * elemSize, __ATOMIC_SEQ_CST );
     1110                #endif // __STATISTICS__
     1111
     1112                char * addr = (char *)memalignNoStats( alignment, size );
    14301113
    14311114                Heap.Storage.Header * header;
    1432                 Heap.FreeHeader * freeHead;
     1115                Heap.FreeHeader * freeElem;
    14331116                size_t bsize;
    14341117
     
    14361119                bool mapped =
    14371120                        #endif // __CFA_DEBUG__
    1438                         headers( "cmemalign", addr, header, freeHead, bsize, alignment );
     1121                        headers( "cmemalign", addr, header, freeElem, bsize, alignment );
    14391122
    14401123                // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
     
    14861169        // 0p, no operation is performed.
    14871170        void free( void * addr ) libcfa_public {
    1488 //              verify( heapManager );
    1489 
    14901171          if ( unlikely( addr == 0p ) ) {                                       // special case
    14911172                        #ifdef __STATISTICS__
    1492                   if ( heapManager )
    1493                         incZeroCalls( FREE );
     1173                        __atomic_add_fetch( &stats.free_null_calls, 1, __ATOMIC_SEQ_CST );
    14941174                        #endif // __STATISTICS__
     1175
     1176                        // #ifdef __CFA_DEBUG__
     1177                        // if ( traceHeap() ) {
     1178                        //      #define nullmsg "Free( 0x0 ) size:0\n"
     1179                        //      // Do not debug print free( 0p ), as it can cause recursive entry from sprintf.
     1180                        //      __cfaabi_dbg_write( nullmsg, sizeof(nullmsg) - 1 );
     1181                        // } // if
     1182                        // #endif // __CFA_DEBUG__
    14951183                        return;
    1496                 } // if
    1497 
    1498                 #ifdef __STATISTICS__
    1499                 incCalls( FREE );
    1500                 #endif // __STATISTICS__
    1501 
    1502                 doFree( addr );                                                                 // handles heapManager == nullptr
     1184                } // exit
     1185
     1186                doFree( addr );
    15031187        } // free
    15041188
     
    15431227          if ( unlikely( addr == 0p ) ) return 0;                       // null allocation has 0 size
    15441228                Heap.Storage.Header * header;
    1545                 Heap.FreeHeader * freeHead;
     1229                Heap.FreeHeader * freeElem;
    15461230                size_t bsize, alignment;
    15471231
    1548                 headers( "malloc_usable_size", addr, header, freeHead, bsize, alignment );
     1232                headers( "malloc_usable_size", addr, header, freeElem, bsize, alignment );
    15491233                return DataStorage( bsize, addr, header );              // data storage in bucket
    15501234        } // malloc_usable_size
     
    15541238        void malloc_stats( void ) libcfa_public {
    15551239                #ifdef __STATISTICS__
    1556                 HeapStatistics stats;
    1557                 HeapStatisticsCtor( stats );
    1558                 if ( printStats( collectStats( stats ) ) == -1 ) {
    1559                 #else
    1560                 #define MALLOC_STATS_MSG "malloc_stats statistics disabled.\n"
    1561                 if ( write( STDERR_FILENO, MALLOC_STATS_MSG, sizeof( MALLOC_STATS_MSG ) - 1 /* size includes '\0' */ ) == -1 ) {
    1562                 #endif // __STATISTICS__
    1563                         abort( "write failed in malloc_stats" );
    1564                 } // if
     1240                printStats();
     1241                if ( prtFree() ) prtFree( heapManager );
     1242                #endif // __STATISTICS__
    15651243        } // malloc_stats
    15661244
     
    15691247        int malloc_stats_fd( int fd __attribute__(( unused )) ) libcfa_public {
    15701248                #ifdef __STATISTICS__
    1571                 int temp = heapMaster.stats_fd;
    1572                 heapMaster.stats_fd = fd;
     1249                int temp = stats_fd;
     1250                stats_fd = fd;
    15731251                return temp;
    15741252                #else
     
    15841262          if ( options != 0 ) { errno = EINVAL; return -1; }
    15851263                #ifdef __STATISTICS__
    1586                 HeapStatistics stats;
    1587                 HeapStatisticsCtor( stats );
    1588                 return printStatsXML( collectStats( stats ), stream ); // returns bytes written or -1
     1264                return printStatsXML( stream );
    15891265                #else
    15901266                return 0;                                                                               // unsupported
     
    15991275                choose( option ) {
    16001276                  case M_TOP_PAD:
    1601                         heapMaster.heapExpand = ceiling2( value, __page_size );
     1277                        heapExpand = ceiling2( value, __page_size );
    16021278                        return 1;
    16031279                  case M_MMAP_THRESHOLD:
     
    16431319// Must have CFA linkage to overload with C linkage realloc.
    16441320void * resize( void * oaddr, size_t nalign, size_t size ) libcfa_public {
    1645   if ( unlikely( oaddr == 0p ) ) {                                              // => malloc( size )
    1646                 return memalignNoStats( nalign, size STAT_ARG( RESIZE ) );
    1647         } // if
    1648 
    1649         PROLOG( RESIZE, doFree( oaddr ) );                                      // => free( oaddr )
     1321        // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
     1322  if ( unlikely( size == 0 ) ) {                                                // special cases
     1323                #ifdef __STATISTICS__
     1324                __atomic_add_fetch( &stats.resize_0_calls, 1, __ATOMIC_SEQ_CST );
     1325                #endif // __STATISTICS__
     1326                free( oaddr );
     1327                return 0p;
     1328        } // if
     1329
     1330        if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum
     1331        #ifdef __CFA_DEBUG__
     1332        else checkAlign( nalign );                                                      // check alignment
     1333        #endif // __CFA_DEBUG__
     1334
     1335  if ( unlikely( oaddr == 0p ) ) {
     1336                #ifdef __STATISTICS__
     1337                __atomic_add_fetch( &stats.resize_calls, 1, __ATOMIC_SEQ_CST );
     1338                __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST );
     1339                #endif // __STATISTICS__
     1340                return memalignNoStats( nalign, size );
     1341        } // if
    16501342
    16511343        // Attempt to reuse existing alignment.
     
    16551347
    16561348        if ( unlikely( isFakeHeader ) ) {
    1657                 checkAlign( nalign );                                                   // check alignment
    16581349                oalign = ClearAlignmentBit( header );                   // old alignment
    16591350                if ( unlikely( (uintptr_t)oaddr % nalign == 0   // lucky match ?
     
    16621353                        ) ) {
    16631354                        HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same)
    1664                         Heap.FreeHeader * freeHead;
     1355                        Heap.FreeHeader * freeElem;
    16651356                        size_t bsize, oalign;
    1666                         headers( "resize", oaddr, header, freeHead, bsize, oalign );
     1357                        headers( "resize", oaddr, header, freeElem, bsize, oalign );
    16671358                        size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket
    16681359
     
    16701361                                HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same)
    16711362                                ClearZeroFillBit( header );                             // turn off 0 fill
    1672                                 #ifdef __CFA_DEBUG__
    1673                                 incUnfreed( size - header->kind.real.size ); // adjustment off the size difference
    1674                                 #endif // __CFA_DEBUG__
    16751363                                header->kind.real.size = size;                  // reset allocation size
    1676                                 #ifdef __STATISTICS__
    1677                                 incCalls( RESIZE );
    1678                                 #endif // __STATISTICS__
    16791364                                return oaddr;
    16801365                        } // if
     
    16851370        } // if
    16861371
     1372        #ifdef __STATISTICS__
     1373        __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST );
     1374        #endif // __STATISTICS__
     1375
    16871376        // change size, DO NOT preserve STICKY PROPERTIES.
    1688         doFree( oaddr );                                                                        // free previous storage
    1689         return memalignNoStats( nalign, size STAT_ARG( RESIZE ) ); // create new aligned area
     1377        free( oaddr );
     1378        return memalignNoStats( nalign, size );                         // create new aligned area
    16901379} // resize
    16911380
    16921381
    16931382void * realloc( void * oaddr, size_t nalign, size_t size ) libcfa_public {
    1694   if ( unlikely( oaddr == 0p ) ) {                                              // => malloc( size )
    1695                 return memalignNoStats( nalign, size STAT_ARG( REALLOC ) );
    1696         } // if
    1697 
    1698         PROLOG( REALLOC, doFree( oaddr ) );                                     // => free( oaddr )
     1383        // If size is equal to 0, either NULL or a pointer suitable to be passed to free() is returned.
     1384  if ( unlikely( size == 0 ) ) {                                                // special cases
     1385                #ifdef __STATISTICS__
     1386                __atomic_add_fetch( &stats.realloc_0_calls, 1, __ATOMIC_SEQ_CST );
     1387                #endif // __STATISTICS__
     1388                free( oaddr );
     1389                return 0p;
     1390        } // if
     1391
     1392        if ( unlikely( nalign < libAlign() ) ) nalign = libAlign(); // reset alignment to minimum
     1393        #ifdef __CFA_DEBUG__
     1394        else checkAlign( nalign );                                                      // check alignment
     1395        #endif // __CFA_DEBUG__
     1396
     1397  if ( unlikely( oaddr == 0p ) ) {
     1398                #ifdef __STATISTICS__
     1399                __atomic_add_fetch( &stats.realloc_calls, 1, __ATOMIC_SEQ_CST );
     1400                __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST );
     1401                #endif // __STATISTICS__
     1402                return memalignNoStats( nalign, size );
     1403        } // if
    16991404
    17001405        // Attempt to reuse existing alignment.
     
    17031408        size_t oalign;
    17041409        if ( unlikely( isFakeHeader ) ) {
    1705                 checkAlign( nalign );                                                   // check alignment
    17061410                oalign = ClearAlignmentBit( header );                   // old alignment
    17071411                if ( unlikely( (uintptr_t)oaddr % nalign == 0   // lucky match ?
     
    17171421        } // if
    17181422
    1719         Heap.FreeHeader * freeHead;
     1423        #ifdef __STATISTICS__
     1424        __atomic_add_fetch( &stats.realloc_calls, 1, __ATOMIC_SEQ_CST );
     1425        __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST );
     1426        #endif // __STATISTICS__
     1427
     1428        Heap.FreeHeader * freeElem;
    17201429        size_t bsize;
    1721         headers( "realloc", oaddr, header, freeHead, bsize, oalign );
     1430        headers( "realloc", oaddr, header, freeElem, bsize, oalign );
    17221431
    17231432        // change size and copy old content to new storage
     
    17261435        bool ozfill = ZeroFillBit( header );                            // old allocation zero filled
    17271436
    1728         void * naddr = memalignNoStats( nalign, size STAT_ARG( REALLOC ) ); // create new aligned area
    1729 
    1730         headers( "realloc", naddr, header, freeHead, bsize, oalign );
     1437        void * naddr = memalignNoStats( nalign, size );         // create new aligned area
     1438
     1439        headers( "realloc", naddr, header, freeElem, bsize, oalign );
    17311440        memcpy( naddr, oaddr, min( osize, size ) );                     // copy bytes
    1732         doFree( oaddr );                                                                        // free previous storage
     1441        free( oaddr );
    17331442
    17341443        if ( unlikely( ozfill ) ) {                                                     // previous request zero fill ?
     
    17421451
    17431452
    1744 void * reallocarray( void * oaddr, size_t nalign, size_t dim, size_t elemSize ) __THROW {
    1745         return realloc( oaddr, nalign, dim * elemSize );
    1746 } // reallocarray
    1747 
    1748 
    17491453// Local Variables: //
    17501454// tab-width: 4 //
Note: See TracChangeset for help on using the changeset viewer.