Changeset 116a2ea for libcfa/src


Ignore:
Timestamp:
Oct 11, 2022, 8:23:26 PM (2 years ago)
Author:
Peter A. Buhr <pabuhr@…>
Branches:
ADT, ast-experimental, master
Children:
4f102fa
Parents:
301071a
Message:

new heap and associated tests updated

Location:
libcfa/src
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/concurrency/kernel/startup.cfa

    r301071a r116a2ea  
    184184
    185185
     186extern void heapManagerCtor();
     187extern void heapManagerDtor();
     188
    186189//=============================================================================================
    187190// Kernel Setup logic
     
    365368        proc->local_data = &__cfaabi_tls;
    366369
     370        heapManagerCtor();                                                                      // initialize heap
     371
    367372        __cfa_io_start( proc );
    368373        register_tls( proc );
     
    416421        unregister_tls( proc );
    417422        __cfa_io_stop( proc );
     423
     424        heapManagerDtor();                                                                      // de-initialize heap
    418425
    419426        return 0p;
  • libcfa/src/heap.cfa

    r301071a r116a2ea  
    1010// Created On       : Tue Dec 19 21:58:35 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Apr 29 19:05:03 2022
    13 // Update Count     : 1167
     12// Last Modified On : Tue Oct 11 15:08:33 2022
     13// Update Count     : 1525
    1414//
    1515
     16#include <stdio.h>
    1617#include <string.h>                                                                             // memset, memcpy
    1718#include <limits.h>                                                                             // ULONG_MAX
     
    2122#include <malloc.h>                                                                             // memalign, malloc_usable_size
    2223#include <sys/mman.h>                                                                   // mmap, munmap
     24extern "C" {
    2325#include <sys/sysinfo.h>                                                                // get_nprocs
     26} // extern "C"
    2427
    2528#include "bits/align.hfa"                                                               // libAlign
    2629#include "bits/defs.hfa"                                                                // likely, unlikely
    2730#include "bits/locks.hfa"                                                               // __spinlock_t
     31#include "concurrency/kernel/fwd.hfa"                                   // __POLL_PREEMPTION
    2832#include "startup.hfa"                                                                  // STARTUP_PRIORITY_MEMORY
    29 #include "math.hfa"                                                                             // min
     33#include "math.hfa"                                                                             // ceiling, min
    3034#include "bitmanip.hfa"                                                                 // is_pow2, ceiling2
    3135
    32 #define FASTLOOKUP
    33 #define __STATISTICS__
     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
     56enum {
     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 ####################
    3472
    3573
     
    5593static bool prtFree = false;
    5694
    57 static bool prtFree() {
     95bool prtFree() {
    5896        return prtFree;
    5997} // prtFree
    6098
    61 static bool prtFreeOn() {
     99bool prtFreeOn() {
    62100        bool temp = prtFree;
    63101        prtFree = true;
     
    65103} // prtFreeOn
    66104
    67 static bool prtFreeOff() {
     105bool prtFreeOff() {
    68106        bool temp = prtFree;
    69107        prtFree = false;
     
    72110
    73111
    74 enum {
    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
     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
     124typedef volatile uintptr_t SpinLock_t CALIGN;                   // aligned addressable word-size
     125
     126static 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
     139static inline __attribute__((always_inline)) void unlock( volatile SpinLock_t & slock ) {
     140        __atomic_clear( &slock, __ATOMIC_SEQ_CST );                     // Fence
     141} // spin_unlock
    87142
    88143
     
    120175                unsigned int free_calls, free_null_calls;
    121176                unsigned long long int free_storage_request, free_storage_alloc;
    122                 unsigned int away_pulls, away_pushes;
    123                 unsigned long long int away_storage_request, away_storage_alloc;
     177                unsigned int return_pulls, return_pushes;
     178                unsigned long long int return_storage_request, return_storage_alloc;
    124179                unsigned int mmap_calls, mmap_0_calls;                  // no zero calls
    125180                unsigned long long int mmap_storage_request, mmap_storage_alloc;
     
    131186
    132187static_assert( sizeof(HeapStatistics) == CntTriples * sizeof(StatsOverlay),
    133                            "Heap statistics counter-triplets does not match with array size" );
     188                           "Heap statistics counter-triplets does not match with array size" );
    134189
    135190static void HeapStatisticsCtor( HeapStatistics & stats ) {
     
    203258        static_assert( libAlign() >= sizeof( Storage ), "minimum alignment < sizeof( Storage )" );
    204259
    205         struct FreeHeader {
    206                 size_t blockSize __attribute__(( aligned (8) )); // size of allocations on this list
     260        struct __attribute__(( aligned (8) )) FreeHeader {
     261                size_t blockSize __attribute__(( aligned(8) )); // size of allocations on this list
    207262                #if BUCKETLOCK == SPINLOCK
    208                 __spinlock_t lock;
    209                 Storage * freeList;
     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
    210270                #else
    211271                StackLF(Storage) freeList;
    212272                #endif // BUCKETLOCK
    213         } __attribute__(( aligned (8) )); // FreeHeader
     273                Heap * homeManager;                                                             // heap owner (free storage to bucket, from bucket to heap)
     274        }; // FreeHeader
    214275
    215276        FreeHeader freeLists[NoBucketSizes];                            // buckets for different allocation sizes
    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
     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__
    221292}; // Heap
    222293
    223294#if BUCKETLOCK == LOCKFREE
    224 static inline {
     295inline __attribute__((always_inline))
     296static {
    225297        Link(Heap.Storage) * ?`next( Heap.Storage * this ) { return &this->header.kind.real.next; }
    226298        void ?{}( Heap.FreeHeader & ) {}
     
    229301#endif // LOCKFREE
    230302
    231 static inline size_t getKey( const Heap.FreeHeader & freeheader ) { return freeheader.blockSize; }
     303
     304struct 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
    232332
    233333
    234334#ifdef FASTLOOKUP
    235 enum { LookupSizes = 65_536 + sizeof(Heap.Storage) }; // number of fast lookup sizes
     335enum { LookupSizes = 65_536 + sizeof(Heap.Storage) };   // number of fast lookup sizes
    236336static unsigned char lookup[LookupSizes];                               // O(1) lookup for small sizes
    237337#endif // FASTLOOKUP
    238338
    239 static const off_t mmapFd = -1;                                                 // fake or actual fd for anonymous file
    240 #ifdef __CFA_DEBUG__
    241 static bool heapBoot = 0;                                                               // detect recursion during boot
    242 #endif // __CFA_DEBUG__
     339static volatile bool heapMasterBootFlag = false;                // trigger for first heap
     340static HeapMaster heapMaster @= {};                                             // program global
     341
     342static void heapMasterCtor();
     343static void heapMasterDtor();
     344static Heap * getHeap();
    243345
    244346
     
    268370static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" );
    269371
    270 // The constructor for heapManager is called explicitly in memory_startup.
    271 static Heap heapManager __attribute__(( aligned (128) )) @= {}; // size of cache line to prevent false sharing
     372
     373// extern visibility, used by runtime kernel
     374libcfa_public size_t __page_size;                                               // architecture pagesize
     375libcfa_public int __map_prot;                                                   // common mmap/mprotect protection
     376
     377
     378// Thread-local storage is allocated lazily when the storage is accessed.
     379static __thread size_t PAD1 CALIGN TLSMODEL __attribute__(( unused )); // protect false sharing
     380static __thread Heap * volatile heapManager CALIGN TLSMODEL;
     381static __thread size_t PAD2 CALIGN TLSMODEL __attribute__(( unused )); // protect further false sharing
     382
     383
     384// declare helper functions for HeapMaster
     385void 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.
     389inline __attribute__((always_inline))
     390static 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
     404void 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
     457Heap * 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
     521void 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
     539void 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
    272554
    273555
    274556//####################### Memory Allocation Routines Helpers ####################
    275557
    276 
    277 #ifdef __CFA_DEBUG__
    278 static size_t allocUnfreed;                                                             // running total of allocations minus frees
    279 
    280 static 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
    290558
    291559extern int cfa_main_returned;                                                   // from interpose.cfa
    292560extern "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
    293569        void heapAppStart() {                                                           // called by __cfaabi_appready_startup
    294                 allocUnfreed = 0;
     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__
    295578        } // heapAppStart
    296579
    297580        void heapAppStop() {                                                            // called by __cfaabi_appready_startdown
    298                 fclose( stdin ); fclose( stdout );
    299                 if ( cfa_main_returned ) prtUnfreed();                  // do not check unfreed storage if exit called
     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__
    300602        } // heapAppStop
    301603} // extern "C"
    302 #endif // __CFA_DEBUG__
    303604
    304605
    305606#ifdef __STATISTICS__
    306607static HeapStatistics stats;                                                    // zero filled
    307 static unsigned int sbrk_calls;
    308 static unsigned long long int sbrk_storage;
    309 // Statistics file descriptor (changed by malloc_stats_fd).
    310 static int stats_fd = STDERR_FILENO;                                    // default stderr
    311608
    312609#define prtFmt \
     
    321618        "  realloc   >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
    322619        "  free      !null calls %'u; null calls %'u; storage %'llu / %'llu bytes\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"                          \
     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"
    326626
    327627// Use "write" because streams may be shutdown when calls are made.
    328 static int printStats() {                                                               // see malloc_stats
     628static int printStats( HeapStatistics & stats ) with( heapMaster, stats ) {     // see malloc_stats
    329629        char helpText[sizeof(prtFmt) + 1024];                           // space for message and values
    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,
     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,
    340641                        sbrk_calls, sbrk_storage,
    341                         stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc,
    342                         stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc
     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
    343646                );
    344647} // printStats
     
    358661        "<total type=\"realloc\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
    359662        "<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" \
    360664        "<total type=\"sbrk\" count=\"%'u;\" size=\"%'llu\"/> bytes\n" \
    361665        "<total type=\"mmap\" count=\"%'u;\" size=\"%'llu / %'llu\" / > bytes\n" \
    362666        "<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" \
    363669        "</malloc>"
    364670
    365 static int printStatsXML( FILE * stream ) {                             // see malloc_info
     671static int printStatsXML( HeapStatistics & stats, FILE * stream ) with( heapMaster, stats ) { // see malloc_info
    366672        char helpText[sizeof(prtFmtXML) + 1024];                        // space for message and values
    367673        return __cfaabi_bits_print_buffer( fileno( stream ), helpText, sizeof(helpText), prtFmtXML,
    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,
     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,
    377684                        sbrk_calls, sbrk_storage,
    378                         stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc,
    379                         stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc
     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
    380689                );
    381690} // printStatsXML
     691
     692static 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
    382703#endif // __STATISTICS__
    383704
    384705
    385 // statically allocated variables => zero filled.
    386 static size_t heapExpand;                                                               // sbrk advance
    387 static size_t mmapStart;                                                                // cross over point for mmap
    388 static 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
    391 libcfa_public size_t __page_size;                                                       // architecture pagesize
    392 libcfa_public int __map_prot;                                                           // common mmap/mprotect protection
    393 
    394 
    395 // thunk problem
    396 size_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 
    410 static inline bool setMmapStart( size_t value ) {               // true => mmapped, false => sbrk
     706static bool setMmapStart( size_t value ) with( heapMaster ) { // true => mmapped, false => sbrk
    411707  if ( value < __page_size || bucketSizes[NoBucketSizes - 1] < value ) return false;
    412708        mmapStart = value;                                                                      // set global
    413709
    414710        // find the closest bucket size less than or equal to the mmapStart size
    415         maxBucketsUsed = Bsearchl( (unsigned int)mmapStart, bucketSizes, NoBucketSizes ); // binary search
    416         assert( maxBucketsUsed < NoBucketSizes );                       // subscript failure ?
    417         assert( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?
     711        maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search
     712        verify( maxBucketsUsed < NoBucketSizes );                       // subscript failure ?
     713        verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?
    418714        return true;
    419715} // setMmapStart
     
    438734
    439735
    440 static inline void checkAlign( size_t alignment ) {
     736inline __attribute__((always_inline))
     737static void checkAlign( size_t alignment ) {
    441738        if ( unlikely( alignment < libAlign() || ! is_pow2( alignment ) ) ) {
    442739                abort( "**** Error **** alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, libAlign() );
     
    445742
    446743
    447 static inline void checkHeader( bool check, const char name[], void * addr ) {
     744inline __attribute__((always_inline))
     745static void checkHeader( bool check, const char name[], void * addr ) {
    448746        if ( unlikely( check ) ) {                                                      // bad address ?
    449747                abort( "**** Error **** attempt to %s storage %p with address outside the heap.\n"
     
    470768
    471769
    472 static inline void fakeHeader( Heap.Storage.Header *& header, size_t & alignment ) {
     770inline __attribute__((always_inline))
     771static void fakeHeader( Heap.Storage.Header *& header, size_t & alignment ) {
    473772        if ( unlikely( AlignmentBit( header ) ) ) {                     // fake header ?
    474773                alignment = ClearAlignmentBit( header );                // clear flag from value
     
    483782
    484783
    485 static 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 ) {
     784inline __attribute__((always_inline))
     785static 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 ) {
    487787        header = HeaderAddr( addr );
    488788
     
    509809        checkHeader( header < (Heap.Storage.Header *)heapBegin || (Heap.Storage.Header *)heapEnd < header, name, addr ); // bad address ? (offset could be + or -)
    510810
     811        Heap * homeManager;
    511812        if ( unlikely( freeHead == 0p || // freed and only free-list node => null link
    512813                                   // freed and link points at another free block not to a bucket in the bucket array.
    513                                    freeHead < &freeLists[0] || &freeLists[NoBucketSizes] <= freeHead ) ) {
     814                                   (homeManager = freeHead->homeManager, freeHead < &homeManager->freeLists[0] ||
     815                                        &homeManager->freeLists[NoBucketSizes] <= freeHead ) ) ) {
    514816                abort( "**** Error **** attempt to %s storage %p with corrupted header.\n"
    515817                           "Possible cause is duplicate free on same block or overwriting of header information.",
     
    521823} // headers
    522824
    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 
    544 static inline void * extend( size_t size ) with( heapManager ) {
    545         lock( extlock __cfaabi_dbg_ctx2 );
     825
     826static void * master_extend( size_t size ) with( heapMaster ) {
     827        lock( extLock );
    546828
    547829        ptrdiff_t rem = heapRemaining - size;
     
    549831                // If the size requested is bigger than the current remaining storage, increase the size of the heap.
    550832
    551                 size_t increase = ceiling2( size > heapExpand ? size : heapExpand, __page_size );
     833                size_t increase = ceiling2( size > heapExpand ? size : heapExpand, libAlign() );
    552834                // Do not call abort or strerror( errno ) as they may call malloc.
    553                 if ( sbrk( increase ) == (void *)-1 ) {                 // failed, no memory ?
    554                         unlock( extlock );
     835                if ( unlikely( sbrk( increase ) == (void *)-1 ) ) {     // failed, no memory ?
     836                        unlock( extLock );
    555837                        __cfaabi_bits_print_nolock( STDERR_FILENO, NO_MEMORY_MSG, size );
    556838                        _exit( EXIT_FAILURE );                                          // give up
    557839                } // if
    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
     840                rem = heapRemaining + increase - size;
    565841
    566842                #ifdef __STATISTICS__
     
    568844                sbrk_storage += increase;
    569845                #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;
    578846        } // if
    579847
     
    581849        heapRemaining = rem;
    582850        heapEnd = (char *)heapEnd + size;
    583         unlock( extlock );
     851
     852        unlock( extLock );
    584853        return block;
    585 } // extend
    586 
    587 
    588 static inline void * doMalloc( size_t size ) with( heapManager ) {
    589         Heap.Storage * block;                                           // pointer to new block of storage
     854} // master_extend
     855
     856
     857__attribute__(( noinline ))
     858static 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
     930static 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
    590935
    591936        // Look up size in the size list.  Make sure the user request includes space for the header that must be allocated
    592937        // along with the block and is a multiple of the alignment size.
    593 
    594938        size_t tsize = size + sizeof(Heap.Storage);
    595939
    596         if ( likely( tsize < mmapStart ) ) {                            // small size => sbrk
    597                 size_t posn;
     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;
    598951                #ifdef FASTLOOKUP
    599                 if ( tsize < LookupSizes ) posn = lookup[tsize];
     952                if ( likely( tsize < LookupSizes ) ) bucket = lookup[tsize];
    600953                else
    601954                #endif // FASTLOOKUP
    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
     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__
    607966
    608967                // Spin until the lock is acquired for this particular size of block.
    609968
    610969                #if BUCKETLOCK == SPINLOCK
    611                 lock( freeElem->lock __cfaabi_dbg_ctx2 );
    612                 block = freeElem->freeList;                                             // remove node from stack
     970                block = freeHead->freeList;                                             // remove node from stack
    613971                #else
    614                 block = pop( freeElem->freeList );
     972                block = pop( freeHead->freeList );
    615973                #endif // BUCKETLOCK
    616974                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
     977                        // and then carve it off.
     978                        #ifdef RETURNSPIN
    617979                        #if BUCKETLOCK == SPINLOCK
    618                         unlock( freeElem->lock );
     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__
    6191001                        #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
    622                         // and then carve it off.
    623 
    624                         block = (Heap.Storage *)extend( tsize );        // mutual exclusion on call
    625                 #if BUCKETLOCK == SPINLOCK
     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
    6261013                } else {
    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
     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
    6331019        } else {                                                                                        // large size => mmap
    6341020  if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p;
    6351021                tsize = ceiling2( tsize, __page_size );                 // must be multiple of page size
    6361022                #ifdef __STATISTICS__
    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 );
     1023                stats.counters[STAT_NAME].alloc += tsize;
     1024                stats.mmap_calls += 1;
     1025                stats.mmap_storage_request += size;
     1026                stats.mmap_storage_alloc += tsize;
    6401027                #endif // __STATISTICS__
    6411028
    642                 block = (Heap.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, mmapFd, 0 );
    643                 if ( block == (Heap.Storage *)MAP_FAILED ) { // failed ?
     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 ?
    6441036                        if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory
    6451037                        // Do not call strerror( errno ) as it may call malloc.
    646                         abort( "(Heap &)0x%p.doMalloc() : internal error, mmap failure, size:%zu errno:%d.", &heapManager, tsize, errno );
    647                 } //if
     1038                        abort( "attempt to allocate large object (> %zu) of size %zu bytes and mmap failed with errno %d.", size, heapMaster.mmapStart, errno );
     1039                } // if
     1040                block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap
     1041
    6481042                #ifdef __CFA_DEBUG__
    649                 // Set new memory to garbage so subsequent uninitialized usages might fail.
    650                 memset( block, '\xde', tsize );
    651                 //Memset( block, tsize );
     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) ) );
    6521046                #endif // __CFA_DEBUG__
    653                 block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap
    6541047        } // if
    6551048
     
    6591052
    6601053        #ifdef __CFA_DEBUG__
    661         __atomic_add_fetch( &allocUnfreed, tsize, __ATOMIC_SEQ_CST );
    6621054        if ( traceHeap() ) {
    6631055                char helpText[64];
     
    6671059        #endif // __CFA_DEBUG__
    6681060
     1061//      poll_interrupts();                                                                      // call rollforward
     1062
    6691063        return addr;
    6701064} // doMalloc
    6711065
    6721066
    673 static inline void doFree( void * addr ) with( heapManager ) {
     1067static 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
     1071
     1072        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
    6741086        #ifdef __CFA_DEBUG__
    675         if ( unlikely( heapManager.heapBegin == 0p ) ) {
    676                 abort( "doFree( %p ) : internal error, called before heap is initialized.", addr );
    677         } // if
     1087        allocUnfreed -= rsize;
    6781088        #endif // __CFA_DEBUG__
    6791089
    680         Heap.Storage.Header * header;
    681         Heap.FreeHeader * freeElem;
    682         size_t size, alignment;                                                         // not used (see realloc)
    683 
    684         if ( headers( "free", addr, header, freeElem, size, alignment ) ) { // mmapped ?
     1090        if ( unlikely( mapped ) ) {                                                     // mmapped ?
    6851091                #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 );
     1092                stats.munmap_calls += 1;
     1093                stats.munmap_storage_request += rsize;
     1094                stats.munmap_storage_alloc += size;
    6891095                #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 );
     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 );
    6941105                } // if
    6951106        } else {
    6961107                #ifdef __CFA_DEBUG__
    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 ) );
     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 );
    7001120                #endif // __CFA_DEBUG__
    7011121
    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
     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
    7161156        } // if
    7171157
    7181158        #ifdef __CFA_DEBUG__
    719         __atomic_add_fetch( &allocUnfreed, -size, __ATOMIC_SEQ_CST );
    7201159        if ( traceHeap() ) {
    7211160                char helpText[64];
     
    7241163        } // if
    7251164        #endif // __CFA_DEBUG__
     1165
     1166//      poll_interrupts();                                                                      // call rollforward
    7261167} // doFree
    7271168
    7281169
    729 static size_t prtFree( Heap & manager ) with( manager ) {
     1170size_t prtFree( Heap & manager ) with( manager ) {
    7301171        size_t total = 0;
    7311172        #ifdef __STATISTICS__
     
    7331174        __cfaabi_bits_print_nolock( STDERR_FILENO, "\nBin lists (bin size : free blocks on list)\n" );
    7341175        #endif // __STATISTICS__
    735         for ( unsigned int i = 0; i < maxBucketsUsed; i += 1 ) {
     1176        for ( unsigned int i = 0; i < heapMaster.maxBucketsUsed; i += 1 ) {
    7361177                size_t size = freeLists[i].blockSize;
    7371178                #ifdef __STATISTICS__
     
    7641205        __cfaabi_bits_release();
    7651206        #endif // __STATISTICS__
    766         return (char *)heapEnd - (char *)heapBegin - total;
     1207        return (char *)heapMaster.heapEnd - (char *)heapMaster.heapBegin - total;
    7671208} // prtFree
    7681209
    7691210
    770 static 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 
    796 static void ^?{}( Heap & ) {
    797         #ifdef __STATISTICS__
    798         if ( traceHeapTerm() ) {
    799                 printStats();
    800                 // prtUnfreed() called in heapAppStop()
    801         } // if
    802         #endif // __STATISTICS__
    803 } // ~Heap
    804 
    805 
    806 static void memory_startup( void ) __attribute__(( constructor( STARTUP_PRIORITY_MEMORY ) ));
    807 void 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 
    820 static void memory_shutdown( void ) __attribute__(( destructor( STARTUP_PRIORITY_MEMORY ) ));
    821 void memory_shutdown( void ) {
    822         ^heapManager{};
    823 } // memory_shutdown
    824 
    825 
    826 static 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 
    837 static 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__
     1211#ifdef __STATISTICS__
     1212static void incCalls( long int statName ) libcfa_nopreempt {
     1213        heapManager->stats.counters[statName].calls += 1;
     1214} // incCalls
     1215
     1216static 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__
     1222static void incUnfreed( size_t offset ) libcfa_nopreempt {
     1223        heapManager->allocUnfreed += offset;
     1224} // incUnfreed
     1225#endif // __CFA_DEBUG__
     1226
     1227
     1228static void * memalignNoStats( size_t alignment, size_t size STAT_PARM ) {
    8411229        checkAlign( alignment );                                                        // check alignment
    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 );
     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 ) );
    8461233
    8471234        // Allocate enough storage to guarantee an address on the alignment boundary, and sufficient space before it for
     
    8541241        // subtract libAlign() because it is already the minimum alignment
    8551242        // add sizeof(Storage) for fake header
    856         char * addr = (char *)mallocNoStats( size + alignment - libAlign() + sizeof(Heap.Storage) );
     1243        size_t offset = alignment - libAlign() + sizeof(Heap.Storage);
     1244        char * addr = (char *)doMalloc( size + offset STAT_ARG( STAT_NAME ) );
    8571245
    8581246        // address in the block of the "next" alignment address
     
    8601248
    8611249        // address of header from malloc
    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
     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
    8651257        Heap.Storage.Header * fakeHeader = HeaderAddr( user );
     1258
    8661259        // SKULLDUGGERY: insert the offset to the start of the actual storage block and remember alignment
    867         fakeHeader->kind.fake.offset = (char *)fakeHeader - (char *)RealHeader;
     1260        fakeHeader->kind.fake.offset = (char *)fakeHeader - (char *)realHeader;
    8681261        // SKULLDUGGERY: odd alignment implies fake header
    8691262        fakeHeader->kind.fake.alignment = MarkAlignmentBit( alignment );
     
    8801273        // then malloc() returns a unique pointer value that can later be successfully passed to free().
    8811274        void * malloc( size_t size ) libcfa_public {
    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 );
     1275                return doMalloc( size STAT_ARG( MALLOC ) );
    8921276        } // malloc
    8931277
     
    8951279        // Same as malloc() except size bytes is an array of dim elements each of elemSize bytes.
    8961280        void * aalloc( size_t dim, size_t elemSize ) libcfa_public {
    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 );
     1281                return doMalloc( dim * elemSize STAT_ARG( AALLOC ) );
    9081282        } // aalloc
    9091283
     
    9121286        void * calloc( size_t dim, size_t elemSize ) libcfa_public {
    9131287                size_t size = dim * elemSize;
    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 );
     1288                char * addr = (char *)doMalloc( size STAT_ARG( CALLOC ) );
     1289
     1290          if ( unlikely( addr == NULL ) ) return NULL;          // stop further processing if 0p is returned
    9261291
    9271292                Heap.Storage.Header * header;
    928                 Heap.FreeHeader * freeElem;
     1293                Heap.FreeHeader * freeHead;
    9291294                size_t bsize, alignment;
    9301295
     
    9321297                bool mapped =
    9331298                        #endif // __CFA_DEBUG__
    934                         headers( "calloc", addr, header, freeElem, bsize, alignment );
     1299                        headers( "calloc", addr, header, freeHead, bsize, alignment );
    9351300
    9361301                #ifndef __CFA_DEBUG__
    9371302                // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
    938                 if ( ! mapped )
     1303                if ( likely( ! mapped ) )
    9391304                #endif // __CFA_DEBUG__
    9401305                        // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined
     
    9521317        // call to malloc(), alloc(), calloc() or realloc(). If the area pointed to was moved, a free(oaddr) is done.
    9531318        void * resize( void * oaddr, size_t size ) libcfa_public {
    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
     1319          if ( unlikely( oaddr == 0p ) ) {                              // => malloc( size )
     1320                        return doMalloc( size STAT_ARG( RESIZE ) );
     1321                } // if
     1322
     1323                PROLOG( RESIZE, doFree( oaddr ) );                              // => free( oaddr )
    9721324
    9731325                Heap.Storage.Header * header;
    974                 Heap.FreeHeader * freeElem;
     1326                Heap.FreeHeader * freeHead;
    9751327                size_t bsize, oalign;
    976                 headers( "resize", oaddr, header, freeElem, bsize, oalign );
     1328                headers( "resize", oaddr, header, freeHead, bsize, oalign );
    9771329
    9781330                size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket
     
    9801332                if ( oalign == libAlign() && size <= odsize && odsize <= size * 2 ) { // allow 50% wasted storage for smaller size
    9811333                        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__
    9821337                        header->kind.real.size = size;                          // reset allocation size
     1338                        #ifdef __STATISTICS__
     1339                        incCalls( RESIZE );
     1340                        #endif // __STATISTICS__
    9831341                        return oaddr;
    9841342                } // if
    9851343
    986                 #ifdef __STATISTICS__
    987                 __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST );
    988                 #endif // __STATISTICS__
    989 
    9901344                // change size, DO NOT preserve STICKY PROPERTIES.
    991                 free( oaddr );
    992                 return mallocNoStats( size );                                   // create new area
     1345                doFree( oaddr );                                                                // free previous storage
     1346
     1347                return doMalloc( size STAT_ARG( RESIZE ) );             // create new area
    9931348        } // resize
    9941349
     
    9971352        // the old and new sizes.
    9981353        void * realloc( void * oaddr, size_t size ) libcfa_public {
    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
     1354          if ( unlikely( oaddr == 0p ) ) {                                      // => malloc( size )
     1355                  return doMalloc( size STAT_ARG( REALLOC ) );
     1356                } // if
     1357
     1358                PROLOG( REALLOC, doFree( oaddr ) );                             // => free( oaddr )
    10171359
    10181360                Heap.Storage.Header * header;
    1019                 Heap.FreeHeader * freeElem;
     1361                Heap.FreeHeader * freeHead;
    10201362                size_t bsize, oalign;
    1021                 headers( "realloc", oaddr, header, freeElem, bsize, oalign );
     1363                headers( "realloc", oaddr, header, freeHead, bsize, oalign );
    10221364
    10231365                size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket
     
    10251367                bool ozfill = ZeroFillBit( header );                    // old allocation zero filled
    10261368          if ( unlikely( size <= odsize ) && odsize <= size * 2 ) { // allow up to 50% wasted storage
    1027                         header->kind.real.size = size;                          // reset allocation size
     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
    10281373                        if ( unlikely( ozfill ) && size > osize ) {     // previous request zero fill and larger ?
    10291374                                memset( (char *)oaddr + osize, '\0', size - osize ); // initialize added storage
    10301375                        } // if
     1376                        #ifdef __STATISTICS__
     1377                        incCalls( REALLOC );
     1378                        #endif // __STATISTICS__
    10311379                        return oaddr;
    10321380                } // if
    10331381
    1034                 #ifdef __STATISTICS__
    1035                 __atomic_add_fetch( &stats.realloc_storage_request, size, __ATOMIC_SEQ_CST );
    1036                 #endif // __STATISTICS__
    1037 
    10381382                // change size and copy old content to new storage
    10391383
    10401384                void * naddr;
    1041                 if ( likely( oalign == libAlign() ) ) {                 // previous request not aligned ?
    1042                         naddr = mallocNoStats( size );                          // create new area
     1385                if ( likely( oalign <= libAlign() ) ) {                 // previous request not aligned ?
     1386                        naddr = doMalloc( size STAT_ARG( REALLOC ) ); // create new area
    10431387                } else {
    1044                         naddr = memalignNoStats( oalign, size );        // create new aligned area
    1045                 } // if
    1046 
    1047                 headers( "realloc", naddr, header, freeElem, bsize, oalign );
     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.
    10481393                memcpy( naddr, oaddr, min( osize, size ) );             // copy bytes
    1049                 free( oaddr );
     1394                doFree( oaddr );                                                                // free previous storage
    10501395
    10511396                if ( unlikely( ozfill ) ) {                                             // previous request zero fill ?
     
    10671412        // Same as malloc() except the memory address is a multiple of alignment, which must be a power of two. (obsolete)
    10681413        void * memalign( size_t alignment, size_t size ) libcfa_public {
    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 );
     1414                return memalignNoStats( alignment, size STAT_ARG( MEMALIGN ) );
    10791415        } // memalign
    10801416
     
    10821418        // Same as aalloc() with memory alignment.
    10831419        void * amemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public {
    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 );
     1420                return memalignNoStats( alignment, dim * elemSize STAT_ARG( AMEMALIGN ) );
    10951421        } // amemalign
    10961422
     
    10991425        void * cmemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public {
    11001426                size_t size = dim * elemSize;
    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 );
     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
    11131430
    11141431                Heap.Storage.Header * header;
    1115                 Heap.FreeHeader * freeElem;
     1432                Heap.FreeHeader * freeHead;
    11161433                size_t bsize;
    11171434
     
    11191436                bool mapped =
    11201437                        #endif // __CFA_DEBUG__
    1121                         headers( "cmemalign", addr, header, freeElem, bsize, alignment );
     1438                        headers( "cmemalign", addr, header, freeHead, bsize, alignment );
    11221439
    11231440                // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
     
    11691486        // 0p, no operation is performed.
    11701487        void free( void * addr ) libcfa_public {
     1488//              verify( heapManager );
     1489
    11711490          if ( unlikely( addr == 0p ) ) {                                       // special case
    11721491                        #ifdef __STATISTICS__
    1173                         __atomic_add_fetch( &stats.free_null_calls, 1, __ATOMIC_SEQ_CST );
     1492                  if ( heapManager )
     1493                        incZeroCalls( FREE );
    11741494                        #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__
    11831495                        return;
    1184                 } // exit
    1185 
    1186                 doFree( addr );
     1496                } // if
     1497
     1498                #ifdef __STATISTICS__
     1499                incCalls( FREE );
     1500                #endif // __STATISTICS__
     1501
     1502                doFree( addr );                                                                 // handles heapManager == nullptr
    11871503        } // free
    11881504
     
    12271543          if ( unlikely( addr == 0p ) ) return 0;                       // null allocation has 0 size
    12281544                Heap.Storage.Header * header;
    1229                 Heap.FreeHeader * freeElem;
     1545                Heap.FreeHeader * freeHead;
    12301546                size_t bsize, alignment;
    12311547
    1232                 headers( "malloc_usable_size", addr, header, freeElem, bsize, alignment );
     1548                headers( "malloc_usable_size", addr, header, freeHead, bsize, alignment );
    12331549                return DataStorage( bsize, addr, header );              // data storage in bucket
    12341550        } // malloc_usable_size
     
    12381554        void malloc_stats( void ) libcfa_public {
    12391555                #ifdef __STATISTICS__
    1240                 printStats();
    1241                 if ( prtFree() ) prtFree( heapManager );
     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 ) {
    12421562                #endif // __STATISTICS__
     1563                        abort( "write failed in malloc_stats" );
     1564                } // if
    12431565        } // malloc_stats
    12441566
     
    12471569        int malloc_stats_fd( int fd __attribute__(( unused )) ) libcfa_public {
    12481570                #ifdef __STATISTICS__
    1249                 int temp = stats_fd;
    1250                 stats_fd = fd;
     1571                int temp = heapMaster.stats_fd;
     1572                heapMaster.stats_fd = fd;
    12511573                return temp;
    12521574                #else
     
    12621584          if ( options != 0 ) { errno = EINVAL; return -1; }
    12631585                #ifdef __STATISTICS__
    1264                 return printStatsXML( stream );
     1586                HeapStatistics stats;
     1587                HeapStatisticsCtor( stats );
     1588                return printStatsXML( collectStats( stats ), stream ); // returns bytes written or -1
    12651589                #else
    12661590                return 0;                                                                               // unsupported
     
    12751599                choose( option ) {
    12761600                  case M_TOP_PAD:
    1277                         heapExpand = ceiling2( value, __page_size );
     1601                        heapMaster.heapExpand = ceiling2( value, __page_size );
    12781602                        return 1;
    12791603                  case M_MMAP_THRESHOLD:
     
    13191643// Must have CFA linkage to overload with C linkage realloc.
    13201644void * resize( void * oaddr, size_t nalign, size_t size ) libcfa_public {
    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;
     1645  if ( unlikely( oaddr == 0p ) ) {                                              // => malloc( size )
     1646                return memalignNoStats( nalign, size STAT_ARG( RESIZE ) );
    13281647        } // if
    13291648
    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
     1649        PROLOG( RESIZE, doFree( oaddr ) );                                      // => free( oaddr )
    13421650
    13431651        // Attempt to reuse existing alignment.
     
    13471655
    13481656        if ( unlikely( isFakeHeader ) ) {
     1657                checkAlign( nalign );                                                   // check alignment
    13491658                oalign = ClearAlignmentBit( header );                   // old alignment
    13501659                if ( unlikely( (uintptr_t)oaddr % nalign == 0   // lucky match ?
     
    13531662                        ) ) {
    13541663                        HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same)
    1355                         Heap.FreeHeader * freeElem;
     1664                        Heap.FreeHeader * freeHead;
    13561665                        size_t bsize, oalign;
    1357                         headers( "resize", oaddr, header, freeElem, bsize, oalign );
     1666                        headers( "resize", oaddr, header, freeHead, bsize, oalign );
    13581667                        size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket
    13591668
     
    13611670                                HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same)
    13621671                                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__
    13631675                                header->kind.real.size = size;                  // reset allocation size
     1676                                #ifdef __STATISTICS__
     1677                                incCalls( RESIZE );
     1678                                #endif // __STATISTICS__
    13641679                                return oaddr;
    13651680                        } // if
     
    13701685        } // if
    13711686
    1372         #ifdef __STATISTICS__
    1373         __atomic_add_fetch( &stats.resize_storage_request, size, __ATOMIC_SEQ_CST );
    1374         #endif // __STATISTICS__
    1375 
    13761687        // change size, DO NOT preserve STICKY PROPERTIES.
    1377         free( oaddr );
    1378         return memalignNoStats( nalign, size );                         // create new aligned area
     1688        doFree( oaddr );                                                                        // free previous storage
     1689        return memalignNoStats( nalign, size STAT_ARG( RESIZE ) ); // create new aligned area
    13791690} // resize
    13801691
    13811692
    13821693void * realloc( void * oaddr, size_t nalign, size_t size ) libcfa_public {
    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;
     1694  if ( unlikely( oaddr == 0p ) ) {                                              // => malloc( size )
     1695                return memalignNoStats( nalign, size STAT_ARG( REALLOC ) );
    13901696        } // if
    13911697
    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
     1698        PROLOG( REALLOC, doFree( oaddr ) );                                     // => free( oaddr )
    14041699
    14051700        // Attempt to reuse existing alignment.
     
    14081703        size_t oalign;
    14091704        if ( unlikely( isFakeHeader ) ) {
     1705                checkAlign( nalign );                                                   // check alignment
    14101706                oalign = ClearAlignmentBit( header );                   // old alignment
    14111707                if ( unlikely( (uintptr_t)oaddr % nalign == 0   // lucky match ?
     
    14211717        } // if
    14221718
    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;
     1719        Heap.FreeHeader * freeHead;
    14291720        size_t bsize;
    1430         headers( "realloc", oaddr, header, freeElem, bsize, oalign );
     1721        headers( "realloc", oaddr, header, freeHead, bsize, oalign );
    14311722
    14321723        // change size and copy old content to new storage
     
    14351726        bool ozfill = ZeroFillBit( header );                            // old allocation zero filled
    14361727
    1437         void * naddr = memalignNoStats( nalign, size );         // create new aligned area
    1438 
    1439         headers( "realloc", naddr, header, freeElem, bsize, oalign );
     1728        void * naddr = memalignNoStats( nalign, size STAT_ARG( REALLOC ) ); // create new aligned area
     1729
     1730        headers( "realloc", naddr, header, freeHead, bsize, oalign );
    14401731        memcpy( naddr, oaddr, min( osize, size ) );                     // copy bytes
    1441         free( oaddr );
     1732        doFree( oaddr );                                                                        // free previous storage
    14421733
    14431734        if ( unlikely( ozfill ) ) {                                                     // previous request zero fill ?
     
    14511742
    14521743
     1744void * reallocarray( void * oaddr, size_t nalign, size_t dim, size_t elemSize ) __THROW {
     1745        return realloc( oaddr, nalign, dim * elemSize );
     1746} // reallocarray
     1747
     1748
    14531749// Local Variables: //
    14541750// tab-width: 4 //
  • libcfa/src/heap.hfa

    r301071a r116a2ea  
    1010// Created On       : Tue May 26 11:23:55 2020
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Apr 21 22:52:25 2022
    13 // Update Count     : 21
     12// Last Modified On : Tue Oct  4 19:08:55 2022
     13// Update Count     : 23
    1414//
    1515
     
    3030bool checkFreeOff();
    3131
    32 // supported mallopt options
    33 #ifndef M_MMAP_THRESHOLD
    34 #define M_MMAP_THRESHOLD (-1)
    35 #endif // M_MMAP_THRESHOLD
    36 
    37 #ifndef M_TOP_PAD
    38 #define M_TOP_PAD (-2)
    39 #endif // M_TOP_PAD
    40 
    4132extern "C" {
    4233        // New allocation operations.
     
    4940        size_t malloc_size( void * addr );
    5041        int malloc_stats_fd( int fd );
    51         size_t malloc_usable_size( void * addr );
    5242        size_t malloc_expansion();                                                      // heap expansion size (bytes)
    5343        size_t malloc_mmap_start();                                                     // crossover allocation size from sbrk to mmap
  • libcfa/src/startup.cfa

    r301071a r116a2ea  
    1010// Created On       : Tue Jul 24 16:21:57 2018
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Jan 17 16:41:54 2022
    13 // Update Count     : 55
     12// Last Modified On : Thu Oct  6 13:51:57 2022
     13// Update Count     : 57
    1414//
    1515
     
    2424
    2525extern "C" {
     26        void __cfaabi_memory_startup( void ) __attribute__(( constructor( STARTUP_PRIORITY_MEMORY ) ));
     27        void __cfaabi_memory_startup( void ) {
     28                extern void memory_startup();
     29                memory_startup();
     30        } // __cfaabi_memory_startup
     31
     32        void __cfaabi_memory_shutdown( void ) __attribute__(( destructor( STARTUP_PRIORITY_MEMORY ) ));
     33        void __cfaabi_memory_shutdown( void ) {
     34                extern void memory_shutdown();
     35                memory_shutdown();
     36        } // __cfaabi_memory_shutdown
     37
    2638        void __cfaabi_appready_startup( void ) __attribute__(( constructor( STARTUP_PRIORITY_APPREADY ) ));
    2739        void __cfaabi_appready_startup( void ) {
    2840                tzset();                                                                                // initialize time global variables
    29                 #ifdef __CFA_DEBUG__
    3041                extern void heapAppStart();
    3142                heapAppStart();
    32                 #endif // __CFA_DEBUG__
    3343        } // __cfaabi_appready_startup
    3444
    3545        void __cfaabi_appready_shutdown( void ) __attribute__(( destructor( STARTUP_PRIORITY_APPREADY ) ));
    3646        void __cfaabi_appready_shutdown( void ) {
    37                 #ifdef __CFA_DEBUG__
    3847                extern void heapAppStop();
    3948                heapAppStop();
    40                 #endif // __CFA_DEBUG__
    4149        } // __cfaabi_appready_shutdown
    4250
Note: See TracChangeset for help on using the changeset viewer.