source: libcfa/src/heap.cfa @ 5951956

ADTast-experimental
Last change on this file since 5951956 was 5951956, checked in by Peter A. Buhr <pabuhr@…>, 21 months ago

fix 32-bit problemgenrating spurious unfreed-storage message

  • Property mode set to 100644
File size: 68.1 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2017 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// heap.cfa --
8//
9// Author           : Peter A. Buhr
10// Created On       : Tue Dec 19 21:58:35 2017
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Thu Oct 13 21:41:32 2022
13// Update Count     : 1553
14//
15
16#include <stdio.h>
17#include <string.h>                                                                             // memset, memcpy
18#include <limits.h>                                                                             // ULONG_MAX
19#include <stdlib.h>                                                                             // EXIT_FAILURE
20#include <errno.h>                                                                              // errno, ENOMEM, EINVAL
21#include <unistd.h>                                                                             // STDERR_FILENO, sbrk, sysconf
22#include <malloc.h>                                                                             // memalign, malloc_usable_size
23#include <sys/mman.h>                                                                   // mmap, munmap
24extern "C" {
25#include <sys/sysinfo.h>                                                                // get_nprocs
26} // extern "C"
27
28#include "bits/align.hfa"                                                               // libAlign
29#include "bits/defs.hfa"                                                                // likely, unlikely
30#include "bits/locks.hfa"                                                               // __spinlock_t
31#include "concurrency/kernel/fwd.hfa"                                   // __POLL_PREEMPTION
32#include "startup.hfa"                                                                  // STARTUP_PRIORITY_MEMORY
33#include "math.hfa"                                                                             // ceiling, min
34#include "bitmanip.hfa"                                                                 // is_pow2, ceiling2
35
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 ####################
72
73
74static bool traceHeap = false;
75
76inline bool traceHeap() libcfa_public { return traceHeap; }
77
78bool traceHeapOn() libcfa_public {
79        bool temp = traceHeap;
80        traceHeap = true;
81        return temp;
82} // traceHeapOn
83
84bool traceHeapOff() libcfa_public {
85        bool temp = traceHeap;
86        traceHeap = false;
87        return temp;
88} // traceHeapOff
89
90bool traceHeapTerm() libcfa_public { return false; }
91
92
93static bool prtFree = false;
94
95bool prtFree() {
96        return prtFree;
97} // prtFree
98
99bool prtFreeOn() {
100        bool temp = prtFree;
101        prtFree = true;
102        return temp;
103} // prtFreeOn
104
105bool prtFreeOff() {
106        bool temp = prtFree;
107        prtFree = false;
108        return temp;
109} // prtFreeOff
110
111
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
142
143
144//####################### Heap Statistics ####################
145
146
147#ifdef __STATISTICS__
148enum { CntTriples = 12 };                                                               // number of counter triples
149enum { MALLOC, AALLOC, CALLOC, MEMALIGN, AMEMALIGN, CMEMALIGN, RESIZE, REALLOC, FREE };
150
151struct StatsOverlay {                                                                   // overlay for iteration
152        unsigned int calls, calls_0;
153        unsigned long long int request, alloc;
154};
155
156// Heap statistics counters.
157union HeapStatistics {
158        struct {                                                                                        // minimum qualification
159                unsigned int malloc_calls, malloc_0_calls;
160                unsigned long long int malloc_storage_request, malloc_storage_alloc;
161                unsigned int aalloc_calls, aalloc_0_calls;
162                unsigned long long int aalloc_storage_request, aalloc_storage_alloc;
163                unsigned int calloc_calls, calloc_0_calls;
164                unsigned long long int calloc_storage_request, calloc_storage_alloc;
165                unsigned int memalign_calls, memalign_0_calls;
166                unsigned long long int memalign_storage_request, memalign_storage_alloc;
167                unsigned int amemalign_calls, amemalign_0_calls;
168                unsigned long long int amemalign_storage_request, amemalign_storage_alloc;
169                unsigned int cmemalign_calls, cmemalign_0_calls;
170                unsigned long long int cmemalign_storage_request, cmemalign_storage_alloc;
171                unsigned int resize_calls, resize_0_calls;
172                unsigned long long int resize_storage_request, resize_storage_alloc;
173                unsigned int realloc_calls, realloc_0_calls;
174                unsigned long long int realloc_storage_request, realloc_storage_alloc;
175                unsigned int free_calls, free_null_calls;
176                unsigned long long int free_storage_request, free_storage_alloc;
177                unsigned int return_pulls, return_pushes;
178                unsigned long long int return_storage_request, return_storage_alloc;
179                unsigned int mmap_calls, mmap_0_calls;                  // no zero calls
180                unsigned long long int mmap_storage_request, mmap_storage_alloc;
181                unsigned int munmap_calls, munmap_0_calls;              // no zero calls
182                unsigned long long int munmap_storage_request, munmap_storage_alloc;
183        };
184        struct StatsOverlay counters[CntTriples];                       // overlay for iteration
185}; // HeapStatistics
186
187static_assert( sizeof(HeapStatistics) == CntTriples * sizeof(StatsOverlay),
188                           "Heap statistics counter-triplets does not match with array size" );
189
190static void HeapStatisticsCtor( HeapStatistics & stats ) {
191        memset( &stats, '\0', sizeof(stats) );                          // very fast
192        // for ( unsigned int i = 0; i < CntTriples; i += 1 ) {
193        //      stats.counters[i].calls = stats.counters[i].calls_0 = stats.counters[i].request = stats.counters[i].alloc = 0;
194        // } // for
195} // HeapStatisticsCtor
196
197static HeapStatistics & ?+=?( HeapStatistics & lhs, const HeapStatistics & rhs ) {
198        for ( unsigned int i = 0; i < CntTriples; i += 1 ) {
199                lhs.counters[i].calls += rhs.counters[i].calls;
200                lhs.counters[i].calls_0 += rhs.counters[i].calls_0;
201                lhs.counters[i].request += rhs.counters[i].request;
202                lhs.counters[i].alloc += rhs.counters[i].alloc;
203        } // for
204        return lhs;
205} // ?+=?
206#endif // __STATISTICS__
207
208
209#define SPINLOCK 0
210#define LOCKFREE 1
211#define BUCKETLOCK SPINLOCK
212#if BUCKETLOCK == SPINLOCK
213#elif BUCKETLOCK == LOCKFREE
214#include <stackLockFree.hfa>
215#else
216        #error undefined lock type for bucket lock
217#endif // LOCKFREE
218
219// Recursive definitions: HeapManager needs size of bucket array and bucket area needs sizeof HeapManager storage.
220// Break recursion by hardcoding number of buckets and statically checking number is correct after bucket array defined.
221enum { NoBucketSizes = 91 };                                                    // number of buckets sizes
222
223struct Heap {
224        struct Storage {
225                struct Header {                                                                 // header
226                        union Kind {
227                                struct RealHeader {
228                                        union {
229                                                struct {                                                // 4-byte word => 8-byte header, 8-byte word => 16-byte header
230                                                        union {
231                                                                // 2nd low-order bit => zero filled, 3rd low-order bit => mmapped
232                                                                // FreeHeader * home;           // allocated block points back to home locations (must overlay alignment)
233                                                                void * home;                    // allocated block points back to home locations (must overlay alignment)
234                                                                size_t blockSize;               // size for munmap (must overlay alignment)
235                                                                #if BUCKETLOCK == SPINLOCK
236                                                                Storage * next;                 // freed block points to next freed block of same size
237                                                                #endif // SPINLOCK
238                                                        };
239                                                        size_t size;                            // allocation size in bytes
240                                                };
241                                                #if BUCKETLOCK == LOCKFREE
242                                                Link(Storage) next;                             // freed block points next freed block of same size (double-wide)
243                                                #endif // LOCKFREE
244                                        };
245                                } real; // RealHeader
246
247                                struct FakeHeader {
248                                        uintptr_t alignment;                            // 1st low-order bit => fake header & alignment
249                                        uintptr_t offset;
250                                } fake; // FakeHeader
251                        } kind; // Kind
252                } header; // Header
253
254                char pad[libAlign() - sizeof( Header )];
255                char data[0];                                                                   // storage
256        }; // Storage
257
258        static_assert( libAlign() >= sizeof( Storage ), "minimum alignment < sizeof( Storage )" );
259
260        struct __attribute__(( aligned (8) )) FreeHeader {
261                size_t blockSize __attribute__(( aligned(8) )); // size of allocations on this list
262                #if BUCKETLOCK == SPINLOCK
263                #ifdef OWNERSHIP
264                #ifdef RETURNSPIN
265                SpinLock_t returnLock;
266                #endif // RETURNSPIN
267                Storage * returnList;                                                   // other thread return list
268                #endif // OWNERSHIP
269                Storage * freeList;                                                             // thread free list
270                #else
271                StackLF(Storage) freeList;
272                #endif // BUCKETLOCK
273                Heap * homeManager;                                                             // heap owner (free storage to bucket, from bucket to heap)
274        }; // FreeHeader
275
276        FreeHeader freeLists[NoBucketSizes];                            // buckets for different allocation sizes
277        void * heapBuffer;                                                                      // start of free storage in buffer
278        size_t heapReserve;                                                                     // amount of remaining free storage in buffer
279
280        #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ )
281        Heap * nextHeapManager;                                                         // intrusive link of existing heaps; traversed to collect statistics or check unfreed storage
282        #endif // __STATISTICS__ || __CFA_DEBUG__
283        Heap * nextFreeHeapManager;                                                     // intrusive link of free heaps from terminated threads; reused by new threads
284
285        #ifdef __CFA_DEBUG__
286        int64_t allocUnfreed;                                                           // running total of allocations minus frees; can be negative
287        #endif // __CFA_DEBUG__
288
289        #ifdef __STATISTICS__
290        HeapStatistics stats;                                                           // local statistic table for this heap
291        #endif // __STATISTICS__
292}; // Heap
293
294#if BUCKETLOCK == LOCKFREE
295inline __attribute__((always_inline))
296static {
297        Link(Heap.Storage) * ?`next( Heap.Storage * this ) { return &this->header.kind.real.next; }
298        void ?{}( Heap.FreeHeader & ) {}
299        void ^?{}( Heap.FreeHeader & ) {}
300} // distribution
301#endif // LOCKFREE
302
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
332
333
334#ifdef FASTLOOKUP
335enum { LookupSizes = 65_536 + sizeof(Heap.Storage) };   // number of fast lookup sizes
336static unsigned char lookup[LookupSizes];                               // O(1) lookup for small sizes
337#endif // FASTLOOKUP
338
339static volatile bool heapMasterBootFlag = false;                // trigger for first heap
340static HeapMaster heapMaster @= {};                                             // program global
341
342static void heapMasterCtor();
343static void heapMasterDtor();
344static Heap * getHeap();
345
346
347// Size of array must harmonize with NoBucketSizes and individual bucket sizes must be multiple of 16.
348// Smaller multiples of 16 and powers of 2 are common allocation sizes, so make them generate the minimum required bucket size.
349// malloc(0) returns 0p, so no bucket is necessary for 0 bytes returning an address that can be freed.
350static const unsigned int bucketSizes[] @= {                    // different bucket sizes
351        16 + sizeof(Heap.Storage), 32 + sizeof(Heap.Storage), 48 + sizeof(Heap.Storage), 64 + sizeof(Heap.Storage), // 4
352        96 + sizeof(Heap.Storage), 112 + sizeof(Heap.Storage), 128 + sizeof(Heap.Storage), // 3
353        160, 192, 224, 256 + sizeof(Heap.Storage), // 4
354        320, 384, 448, 512 + sizeof(Heap.Storage), // 4
355        640, 768, 896, 1_024 + sizeof(Heap.Storage), // 4
356        1_536, 2_048 + sizeof(Heap.Storage), // 2
357        2_560, 3_072, 3_584, 4_096 + sizeof(Heap.Storage), // 4
358        6_144, 8_192 + sizeof(Heap.Storage), // 2
359        9_216, 10_240, 11_264, 12_288, 13_312, 14_336, 15_360, 16_384 + sizeof(Heap.Storage), // 8
360        18_432, 20_480, 22_528, 24_576, 26_624, 28_672, 30_720, 32_768 + sizeof(Heap.Storage), // 8
361        36_864, 40_960, 45_056, 49_152, 53_248, 57_344, 61_440, 65_536 + sizeof(Heap.Storage), // 8
362        73_728, 81_920, 90_112, 98_304, 106_496, 114_688, 122_880, 131_072 + sizeof(Heap.Storage), // 8
363        147_456, 163_840, 180_224, 196_608, 212_992, 229_376, 245_760, 262_144 + sizeof(Heap.Storage), // 8
364        294_912, 327_680, 360_448, 393_216, 425_984, 458_752, 491_520, 524_288 + sizeof(Heap.Storage), // 8
365        655_360, 786_432, 917_504, 1_048_576 + sizeof(Heap.Storage), // 4
366        1_179_648, 1_310_720, 1_441_792, 1_572_864, 1_703_936, 1_835_008, 1_966_080, 2_097_152 + sizeof(Heap.Storage), // 8
367        2_621_440, 3_145_728, 3_670_016, 4_194_304 + sizeof(Heap.Storage), // 4
368};
369
370static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" );
371
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 "**** Error **** 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( "**** Error **** 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
518        return heap;
519} // getHeap
520
521
522void heapManagerCtor() libcfa_public {
523        if ( unlikely( ! heapMasterBootFlag ) ) heapMasterCtor();
524
525        lock( heapMaster.mgrLock );             // protect heapMaster counters
526
527        // get storage for heap manager
528
529        heapManager = getHeap();
530
531        #ifdef __STATISTICS__
532        HeapStatisticsCtor( heapManager->stats );                       // heap local
533        heapMaster.threads_started += 1;
534        #endif // __STATISTICS__
535
536        unlock( heapMaster.mgrLock );
537} // heapManagerCtor
538
539
540void heapManagerDtor() libcfa_public {
541        lock( heapMaster.mgrLock );
542
543        // place heap on list of free heaps for reusability
544        heapManager->nextFreeHeapManager = heapMaster.freeHeapManagersList;
545        heapMaster.freeHeapManagersList = heapManager;
546
547        #ifdef __STATISTICS__
548        heapMaster.threads_exited += 1;
549        #endif // __STATISTICS__
550
551        // Do not set heapManager to NULL because it is used after Cforall is shutdown but before the program shuts down.
552
553        unlock( heapMaster.mgrLock );
554} // heapManagerDtor
555
556
557//####################### Memory Allocation Routines Helpers ####################
558
559
560extern int cfa_main_returned;                                                   // from interpose.cfa
561extern "C" {
562        void memory_startup( void ) {
563                if ( ! heapMasterBootFlag ) heapManagerCtor();  // sanity check
564        } // memory_startup
565
566        void memory_shutdown( void ) {
567                heapManagerDtor();
568        } // memory_shutdown
569
570        void heapAppStart() {                                                           // called by __cfaabi_appready_startup
571                verify( heapManager );
572                #ifdef __CFA_DEBUG__
573                heapManager->allocUnfreed = 0;                                  // clear prior allocation counts
574                #endif // __CFA_DEBUG__
575
576                #ifdef __STATISTICS__
577                HeapStatisticsCtor( heapManager->stats );               // clear prior statistic counters
578                #endif // __STATISTICS__
579        } // heapAppStart
580
581        void heapAppStop() {                                                            // called by __cfaabi_appready_startdown
582                fclose( stdin ); fclose( stdout );                              // free buffer storage
583          if ( ! cfa_main_returned ) return;                            // do not check unfreed storage if exit called
584
585                #ifdef __CFA_DEBUG__
586                // allocUnfreed is set to 0 when a heap is created and it accumulates any unfreed storage during its multiple thread
587                // usages.  At the end, add up each heap allocUnfreed value across all heaps to get the total unfreed storage.
588                int64_t allocUnfreed = 0;
589                for ( Heap * heap = heapMaster.heapManagersList; heap; heap = heap->nextHeapManager ) {
590                        allocUnfreed += heap->allocUnfreed;
591                } // for
592
593                allocUnfreed -= malloc_unfreed();                               // subtract any user specified unfreed storage
594                if ( allocUnfreed > 0 ) {
595                        // DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT.
596                        char helpText[512];
597                        __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),
598                                                                                "CFA warning (UNIX pid:%ld) : program terminating with %llu(0x%llx) bytes of storage allocated but not freed.\n"
599                                                                                "Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n",
600                                                                                (long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid
601                } // if
602                #endif // __CFA_DEBUG__
603        } // heapAppStop
604} // extern "C"
605
606
607#ifdef __STATISTICS__
608static HeapStatistics stats;                                                    // zero filled
609
610#define prtFmt \
611        "\nHeap statistics: (storage request / allocation)\n" \
612        "  malloc    >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
613        "  aalloc    >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
614        "  calloc    >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
615        "  memalign  >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
616        "  amemalign >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
617        "  cmemalign >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
618        "  resize    >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
619        "  realloc   >0 calls %'u; 0 calls %'u; storage %'llu / %'llu bytes\n" \
620        "  free      !null calls %'u; null calls %'u; storage %'llu / %'llu bytes\n" \
621        "  return    pulls %'u; pushes %'u; storage %'llu / %'llu bytes\n" \
622        "  sbrk      calls %'u; storage %'llu bytes\n" \
623        "  mmap      calls %'u; storage %'llu / %'llu bytes\n" \
624        "  munmap    calls %'u; storage %'llu / %'llu bytes\n" \
625        "  threads   started %'lu; exited %'lu\n" \
626        "  heaps     new %'lu; reused %'lu\n"
627
628// Use "write" because streams may be shutdown when calls are made.
629static int printStats( HeapStatistics & stats ) with( heapMaster, stats ) {     // see malloc_stats
630        char helpText[sizeof(prtFmt) + 1024];                           // space for message and values
631        return __cfaabi_bits_print_buffer( stats_fd, helpText, sizeof(helpText), prtFmt,
632                        malloc_calls, malloc_0_calls, malloc_storage_request, malloc_storage_alloc,
633                        aalloc_calls, aalloc_0_calls, aalloc_storage_request, aalloc_storage_alloc,
634                        calloc_calls, calloc_0_calls, calloc_storage_request, calloc_storage_alloc,
635                        memalign_calls, memalign_0_calls, memalign_storage_request, memalign_storage_alloc,
636                        amemalign_calls, amemalign_0_calls, amemalign_storage_request, amemalign_storage_alloc,
637                        cmemalign_calls, cmemalign_0_calls, cmemalign_storage_request, cmemalign_storage_alloc,
638                        resize_calls, resize_0_calls, resize_storage_request, resize_storage_alloc,
639                        realloc_calls, realloc_0_calls, realloc_storage_request, realloc_storage_alloc,
640                        free_calls, free_null_calls, free_storage_request, free_storage_alloc,
641                        return_pulls, return_pushes, return_storage_request, return_storage_alloc,
642                        sbrk_calls, sbrk_storage,
643                        mmap_calls, mmap_storage_request, mmap_storage_alloc,
644                        munmap_calls, munmap_storage_request, munmap_storage_alloc,
645                        threads_started, threads_exited,
646                        new_heap, reused_heap
647                );
648} // printStats
649
650#define prtFmtXML \
651        "<malloc version=\"1\">\n" \
652        "<heap nr=\"0\">\n" \
653        "<sizes>\n" \
654        "</sizes>\n" \
655        "<total type=\"malloc\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
656        "<total type=\"aalloc\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
657        "<total type=\"calloc\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
658        "<total type=\"memalign\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
659        "<total type=\"amemalign\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
660        "<total type=\"cmemalign\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
661        "<total type=\"resize\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
662        "<total type=\"realloc\" >0 count=\"%'u;\" 0 count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
663        "<total type=\"free\" !null=\"%'u;\" 0 null=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
664        "<total type=\"return\" pulls=\"%'u;\" 0 pushes=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
665        "<total type=\"sbrk\" count=\"%'u;\" size=\"%'llu\"/> bytes\n" \
666        "<total type=\"mmap\" count=\"%'u;\" size=\"%'llu / %'llu\" / > bytes\n" \
667        "<total type=\"munmap\" count=\"%'u;\" size=\"%'llu / %'llu\"/> bytes\n" \
668        "<total type=\"threads\" started=\"%'lu;\" exited=\"%'lu\"/>\n" \
669        "<total type=\"heaps\" new=\"%'lu;\" reused=\"%'lu\"/>\n" \
670        "</malloc>"
671
672static int printStatsXML( HeapStatistics & stats, FILE * stream ) with( heapMaster, stats ) { // see malloc_info
673        char helpText[sizeof(prtFmtXML) + 1024];                        // space for message and values
674        return __cfaabi_bits_print_buffer( fileno( stream ), helpText, sizeof(helpText), prtFmtXML,
675                        malloc_calls, malloc_0_calls, malloc_storage_request, malloc_storage_alloc,
676                        aalloc_calls, aalloc_0_calls, aalloc_storage_request, aalloc_storage_alloc,
677                        calloc_calls, calloc_0_calls, calloc_storage_request, calloc_storage_alloc,
678                        memalign_calls, memalign_0_calls, memalign_storage_request, memalign_storage_alloc,
679                        amemalign_calls, amemalign_0_calls, amemalign_storage_request, amemalign_storage_alloc,
680                        cmemalign_calls, cmemalign_0_calls, cmemalign_storage_request, cmemalign_storage_alloc,
681                        resize_calls, resize_0_calls, resize_storage_request, resize_storage_alloc,
682                        realloc_calls, realloc_0_calls, realloc_storage_request, realloc_storage_alloc,
683                        free_calls, free_null_calls, free_storage_request, free_storage_alloc,
684                        return_pulls, return_pushes, return_storage_request, return_storage_alloc,
685                        sbrk_calls, sbrk_storage,
686                        mmap_calls, mmap_storage_request, mmap_storage_alloc,
687                    munmap_calls, munmap_storage_request, munmap_storage_alloc,
688                        threads_started, threads_exited,
689                        new_heap, reused_heap
690                );
691} // printStatsXML
692
693static HeapStatistics & collectStats( HeapStatistics & stats ) with( heapMaster ) {
694        lock( mgrLock );
695
696        stats += heapMaster.stats;
697        for ( Heap * heap = heapManagersList; heap; heap = heap->nextHeapManager ) {
698                stats += heap->stats;
699        } // for
700
701        unlock( mgrLock );
702        return stats;
703} // collectStats
704#endif // __STATISTICS__
705
706
707static bool setMmapStart( size_t value ) with( heapMaster ) { // true => mmapped, false => sbrk
708  if ( value < __page_size || bucketSizes[NoBucketSizes - 1] < value ) return false;
709        mmapStart = value;                                                                      // set global
710
711        // find the closest bucket size less than or equal to the mmapStart size
712        maxBucketsUsed = Bsearchl( mmapStart, bucketSizes, NoBucketSizes ); // binary search
713        verify( maxBucketsUsed < NoBucketSizes );                       // subscript failure ?
714        verify( mmapStart <= bucketSizes[maxBucketsUsed] ); // search failure ?
715        return true;
716} // setMmapStart
717
718
719// <-------+----------------------------------------------------> bsize (bucket size)
720// |header |addr
721//==================================================================================
722//                   align/offset |
723// <-----------------<------------+-----------------------------> bsize (bucket size)
724//                   |fake-header | addr
725#define HeaderAddr( addr ) ((Heap.Storage.Header *)( (char *)addr - sizeof(Heap.Storage) ))
726#define RealHeader( header ) ((Heap.Storage.Header *)((char *)header - header->kind.fake.offset))
727
728// <-------<<--------------------- dsize ---------------------->> bsize (bucket size)
729// |header |addr
730//==================================================================================
731//                   align/offset |
732// <------------------------------<<---------- dsize --------->>> bsize (bucket size)
733//                   |fake-header |addr
734#define DataStorage( bsize, addr, header ) (bsize - ( (char *)addr - (char *)header ))
735
736
737inline __attribute__((always_inline))
738static void checkAlign( size_t alignment ) {
739        if ( unlikely( alignment < libAlign() || ! is_pow2( alignment ) ) ) {
740                abort( "**** Error **** alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, libAlign() );
741        } // if
742} // checkAlign
743
744
745inline __attribute__((always_inline))
746static void checkHeader( bool check, const char name[], void * addr ) {
747        if ( unlikely( check ) ) {                                                      // bad address ?
748                abort( "**** Error **** attempt to %s storage %p with address outside the heap.\n"
749                           "Possible cause is duplicate free on same block or overwriting of memory.",
750                           name, addr );
751        } // if
752} // checkHeader
753
754
755// Manipulate sticky bits stored in unused 3 low-order bits of an address.
756//   bit0 => alignment => fake header
757//   bit1 => zero filled (calloc)
758//   bit2 => mapped allocation versus sbrk
759#define StickyBits( header ) (((header)->kind.real.blockSize & 0x7))
760#define ClearStickyBits( addr ) (typeof(addr))((uintptr_t)(addr) & ~7)
761#define MarkAlignmentBit( align ) ((align) | 1)
762#define AlignmentBit( header ) ((((header)->kind.fake.alignment) & 1))
763#define ClearAlignmentBit( header ) (((header)->kind.fake.alignment) & ~1)
764#define ZeroFillBit( header ) ((((header)->kind.real.blockSize) & 2))
765#define ClearZeroFillBit( header ) ((((header)->kind.real.blockSize) &= ~2))
766#define MarkZeroFilledBit( header ) ((header)->kind.real.blockSize |= 2)
767#define MmappedBit( header ) ((((header)->kind.real.blockSize) & 4))
768#define MarkMmappedBit( size ) ((size) | 4)
769
770
771inline __attribute__((always_inline))
772static void fakeHeader( Heap.Storage.Header *& header, size_t & alignment ) {
773        if ( unlikely( AlignmentBit( header ) ) ) {                     // fake header ?
774                alignment = ClearAlignmentBit( header );                // clear flag from value
775                #ifdef __CFA_DEBUG__
776                checkAlign( alignment );                                                // check alignment
777                #endif // __CFA_DEBUG__
778                header = RealHeader( header );                                  // backup from fake to real header
779        } else {
780                alignment = libAlign();                                                 // => no fake header
781        } // if
782} // fakeHeader
783
784
785inline __attribute__((always_inline))
786static bool headers( const char name[] __attribute__(( unused )), void * addr, Heap.Storage.Header *& header,
787                                                        Heap.FreeHeader *& freeHead, size_t & size, size_t & alignment ) with( heapMaster, *heapManager ) {
788        header = HeaderAddr( addr );
789
790        #ifdef __CFA_DEBUG__
791        checkHeader( header < (Heap.Storage.Header *)heapBegin, name, addr ); // bad low address ?
792        #endif // __CFA_DEBUG__
793
794        if ( likely( ! StickyBits( header ) ) ) {                       // no sticky bits ?
795                freeHead = (Heap.FreeHeader *)(header->kind.real.home);
796                alignment = libAlign();
797        } else {
798                fakeHeader( header, alignment );
799                if ( unlikely( MmappedBit( header ) ) ) {               // mmapped ?
800                        verify( addr < heapBegin || heapEnd < addr );
801                        size = ClearStickyBits( header->kind.real.blockSize ); // mmap size
802                        return true;
803                } // if
804
805                freeHead = (Heap.FreeHeader *)(ClearStickyBits( header->kind.real.home ));
806        } // if
807        size = freeHead->blockSize;
808
809        #ifdef __CFA_DEBUG__
810        checkHeader( header < (Heap.Storage.Header *)heapBegin || (Heap.Storage.Header *)heapEnd < header, name, addr ); // bad address ? (offset could be + or -)
811
812        Heap * homeManager;
813        if ( unlikely( freeHead == 0p || // freed and only free-list node => null link
814                                   // freed and link points at another free block not to a bucket in the bucket array.
815                                   (homeManager = freeHead->homeManager, freeHead < &homeManager->freeLists[0] ||
816                                        &homeManager->freeLists[NoBucketSizes] <= freeHead ) ) ) {
817                abort( "**** Error **** attempt to %s storage %p with corrupted header.\n"
818                           "Possible cause is duplicate free on same block or overwriting of header information.",
819                           name, addr );
820        } // if
821        #endif // __CFA_DEBUG__
822
823        return false;
824} // headers
825
826
827static void * master_extend( size_t size ) with( heapMaster ) {
828        lock( extLock );
829
830        ptrdiff_t rem = heapRemaining - size;
831        if ( unlikely( rem < 0 ) ) {
832                // If the size requested is bigger than the current remaining storage, increase the size of the heap.
833
834                size_t increase = ceiling2( size > heapExpand ? size : heapExpand, libAlign() );
835                // Do not call abort or strerror( errno ) as they may call malloc.
836                if ( unlikely( sbrk( increase ) == (void *)-1 ) ) {     // failed, no memory ?
837                        unlock( extLock );
838                        abort( NO_MEMORY_MSG, size );                           // no memory
839                } // if
840
841                // Make storage executable for thunks.
842                if ( mprotect( (char *)heapEnd + heapRemaining, increase, __map_prot ) ) {
843                        unlock( extLock );
844                        abort( "**** Error **** attempt to make heap storage executable for thunks and mprotect failed with errno %d.", errno );
845                } // if
846
847                rem = heapRemaining + increase - size;
848
849                #ifdef __STATISTICS__
850                sbrk_calls += 1;
851                sbrk_storage += increase;
852                #endif // __STATISTICS__
853        } // if
854
855        Heap.Storage * block = (Heap.Storage *)heapEnd;
856        heapRemaining = rem;
857        heapEnd = (char *)heapEnd + size;
858
859        unlock( extLock );
860        return block;
861} // master_extend
862
863
864__attribute__(( noinline ))
865static void * manager_extend( size_t size ) with( *heapManager ) {
866        ptrdiff_t rem = heapReserve - size;
867
868        if ( unlikely( rem < 0 ) ) {                                            // negative
869                // If the size requested is bigger than the current remaining reserve, use the current reserve to populate
870                // smaller freeLists, and increase the reserve.
871
872                rem = heapReserve;                                                              // positive
873
874                if ( rem >= bucketSizes[0] ) {                                  // minimal size ? otherwise ignore
875                        size_t bucket;
876                        #ifdef FASTLOOKUP
877                        if ( likely( rem < LookupSizes ) ) bucket = lookup[rem];
878                        #endif // FASTLOOKUP
879                                bucket = Bsearchl( rem, bucketSizes, heapMaster.maxBucketsUsed );
880                        verify( 0 <= bucket && bucket <= heapMaster.maxBucketsUsed );
881                        Heap.FreeHeader * freeHead = &(freeLists[bucket]);
882
883                        // The remaining storage many not be bucket size, whereas all other allocations are. Round down to previous
884                        // bucket size in this case.
885                        if ( unlikely( freeHead->blockSize > (size_t)rem ) ) freeHead -= 1;
886                        Heap.Storage * block = (Heap.Storage *)heapBuffer;
887
888                        block->header.kind.real.next = freeHead->freeList; // push on stack
889                        freeHead->freeList = block;
890                } // if
891
892                size_t increase = ceiling( size > ( heapMaster.heapExpand / 10 ) ? size : ( heapMaster.heapExpand / 10 ), libAlign() );
893                heapBuffer = master_extend( increase );
894                rem = increase - size;
895        } // if
896
897        Heap.Storage * block = (Heap.Storage *)heapBuffer;
898        heapReserve = rem;
899        heapBuffer = (char *)heapBuffer + size;
900
901        return block;
902} // manager_extend
903
904
905#define BOOT_HEAP_MANAGER \
906        if ( unlikely( ! heapMasterBootFlag ) ) { \
907                heapManagerCtor(); /* trigger for first heap */ \
908        } /* if */
909
910#ifdef __STATISTICS__
911#define STAT_NAME __counter
912#define STAT_PARM , unsigned int STAT_NAME
913#define STAT_ARG( name ) , name
914#define STAT_0_CNT( counter ) stats.counters[counter].calls_0 += 1
915#else
916#define STAT_NAME
917#define STAT_PARM
918#define STAT_ARG( name )
919#define STAT_0_CNT( counter )
920#endif // __STATISTICS__
921
922#define PROLOG( counter, ... ) \
923        BOOT_HEAP_MANAGER; \
924        if ( unlikely( size == 0 ) ||                                           /* 0 BYTE ALLOCATION RETURNS NULL POINTER */ \
925                unlikely( size > ULONG_MAX - sizeof(Heap.Storage) ) ) { /* error check */ \
926                STAT_0_CNT( counter ); \
927                __VA_ARGS__; \
928                return 0p; \
929        } /* if */
930
931
932#define SCRUB_SIZE 1024lu
933// Do not use '\xfe' for scrubbing because dereferencing an address composed of it causes a SIGSEGV *without* a valid IP
934// pointer in the interrupt frame.
935#define SCRUB '\xff'
936
937static void * doMalloc( size_t size STAT_PARM ) libcfa_nopreempt with( *heapManager ) {
938        PROLOG( STAT_NAME );
939
940        verify( heapManager );
941        Heap.Storage * block;                                                           // pointer to new block of storage
942
943        // Look up size in the size list.  Make sure the user request includes space for the header that must be allocated
944        // along with the block and is a multiple of the alignment size.
945        size_t tsize = size + sizeof(Heap.Storage);
946
947        #ifdef __STATISTICS__
948        stats.counters[STAT_NAME].calls += 1;
949        stats.counters[STAT_NAME].request += size;
950        #endif // __STATISTICS__
951
952        #ifdef __CFA_DEBUG__
953        allocUnfreed += size;
954        #endif // __CFA_DEBUG__
955
956        if ( likely( tsize < heapMaster.mmapStart ) ) {         // small size => sbrk
957                size_t bucket;
958                #ifdef FASTLOOKUP
959                if ( likely( tsize < LookupSizes ) ) bucket = lookup[tsize];
960                else
961                #endif // FASTLOOKUP
962                        bucket = Bsearchl( tsize, bucketSizes, heapMaster.maxBucketsUsed );
963                verify( 0 <= bucket && bucket <= heapMaster.maxBucketsUsed );
964                Heap.FreeHeader * freeHead = &freeLists[bucket];
965
966                verify( freeHead <= &freeLists[heapMaster.maxBucketsUsed] ); // subscripting error ?
967                verify( tsize <= freeHead->blockSize );                 // search failure ?
968
969                tsize = freeHead->blockSize;                                    // total space needed for request
970                #ifdef __STATISTICS__
971                stats.counters[STAT_NAME].alloc += tsize;
972                #endif // __STATISTICS__
973
974                // Spin until the lock is acquired for this particular size of block.
975
976                #if BUCKETLOCK == SPINLOCK
977                block = freeHead->freeList;                                             // remove node from stack
978                #else
979                block = pop( freeHead->freeList );
980                #endif // BUCKETLOCK
981                if ( unlikely( block == 0p ) ) {                                // no free block ?
982                        #ifdef OWNERSHIP
983                        // Freelist for that size is empty, so carve it out of the heap, if there is enough left, or get some more
984                        // and then carve it off.
985                        #ifdef RETURNSPIN
986                        #if BUCKETLOCK == SPINLOCK
987                        lock( freeHead->returnLock );
988                        block = freeHead->returnList;
989                        freeHead->returnList = 0p;
990                        unlock( freeHead->returnLock );
991                        #else
992                        block = __atomic_exchange_n( &freeHead->returnList, nullptr, __ATOMIC_SEQ_CST );
993                        #endif // RETURNSPIN
994
995                        if ( likely( block == 0p ) ) {                  // return list also empty?
996                        #endif // OWNERSHIP
997                                // Do not leave kernel thread as manager_extend accesses heapManager.
998                                disable_interrupts();
999                                block = (Heap.Storage *)manager_extend( tsize ); // mutual exclusion on call
1000                                enable_interrupts( false );
1001
1002                                // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
1003
1004                                #ifdef __CFA_DEBUG__
1005                                // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes.
1006                                memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) );
1007                                #endif // __CFA_DEBUG__
1008                        #endif // BUCKETLOCK
1009                        #ifdef OWNERSHIP
1010                        } else {                                                                        // merge returnList into freeHead
1011                                #ifdef __STATISTICS__
1012                                stats.return_pulls += 1;
1013                                #endif // __STATISTICS__
1014
1015                                // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
1016
1017                                freeHead->freeList = block->header.kind.real.next;
1018                        } // if
1019                        #endif // OWNERSHIP
1020                } else {
1021                        // Memory is scrubbed in doFree.
1022                        freeHead->freeList = block->header.kind.real.next;
1023                } // if
1024
1025                block->header.kind.real.home = freeHead;                // pointer back to free list of apropriate size
1026        } else {                                                                                        // large size => mmap
1027  if ( unlikely( size > ULONG_MAX - __page_size ) ) return 0p;
1028                tsize = ceiling2( tsize, __page_size );                 // must be multiple of page size
1029                #ifdef __STATISTICS__
1030                stats.counters[STAT_NAME].alloc += tsize;
1031                stats.mmap_calls += 1;
1032                stats.mmap_storage_request += size;
1033                stats.mmap_storage_alloc += tsize;
1034                #endif // __STATISTICS__
1035
1036                disable_interrupts();
1037                block = (Heap.Storage *)mmap( 0, tsize, __map_prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
1038                enable_interrupts( false );
1039
1040                // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
1041
1042                if ( unlikely( block == (Heap.Storage *)MAP_FAILED ) ) { // failed ?
1043                        if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, tsize ); // no memory
1044                        // Do not call strerror( errno ) as it may call malloc.
1045                        abort( "**** Error **** attempt to allocate large object (> %zu) of size %zu bytes and mmap failed with errno %d.", size, heapMaster.mmapStart, errno );
1046                } // if
1047                block->header.kind.real.blockSize = MarkMmappedBit( tsize ); // storage size for munmap
1048
1049                #ifdef __CFA_DEBUG__
1050                // Scrub new memory so subsequent uninitialized usages might fail. Only scrub the first 1024 bytes.  The rest of
1051                // the storage set to 0 by mmap.
1052                memset( block->data, SCRUB, min( SCRUB_SIZE, tsize - sizeof(Heap.Storage) ) );
1053                #endif // __CFA_DEBUG__
1054        } // if
1055
1056        block->header.kind.real.size = size;                            // store allocation size
1057        void * addr = &(block->data);                                           // adjust off header to user bytes
1058        verify( ((uintptr_t)addr & (libAlign() - 1)) == 0 ); // minimum alignment ?
1059
1060        #ifdef __CFA_DEBUG__
1061        if ( traceHeap() ) {
1062                char helpText[64];
1063                __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),
1064                                                                        "%p = Malloc( %zu ) (allocated %zu)\n", addr, size, tsize ); // print debug/nodebug
1065        } // if
1066        #endif // __CFA_DEBUG__
1067
1068//      poll_interrupts();                                                                      // call rollforward
1069
1070        return addr;
1071} // doMalloc
1072
1073
1074static void doFree( void * addr ) libcfa_nopreempt with( *heapManager ) {
1075        verify( addr );
1076
1077        // detect free after thread-local storage destruction and use global stats in that case
1078
1079        Heap.Storage.Header * header;
1080        Heap.FreeHeader * freeHead;
1081        size_t size, alignment;
1082
1083        bool mapped = headers( "free", addr, header, freeHead, size, alignment );
1084        #if defined( __STATISTICS__ ) || defined( __CFA_DEBUG__ )
1085        size_t rsize = header->kind.real.size;                          // optimization
1086        #endif // __STATISTICS__ || __CFA_DEBUG__
1087
1088        #ifdef __STATISTICS__
1089        stats.free_storage_request += rsize;
1090        stats.free_storage_alloc += size;
1091        #endif // __STATISTICS__
1092
1093        #ifdef __CFA_DEBUG__
1094        allocUnfreed -= rsize;
1095        #endif // __CFA_DEBUG__
1096
1097        if ( unlikely( mapped ) ) {                                                     // mmapped ?
1098                #ifdef __STATISTICS__
1099                stats.munmap_calls += 1;
1100                stats.munmap_storage_request += rsize;
1101                stats.munmap_storage_alloc += size;
1102                #endif // __STATISTICS__
1103
1104                // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
1105
1106                // Does not matter where this storage is freed.
1107                if ( unlikely( munmap( header, size ) == -1 ) ) {
1108                        // Do not call strerror( errno ) as it may call malloc.
1109                        abort( "**** Error **** attempt to deallocate large object %p and munmap failed with errno %d.\n"
1110                                   "Possible cause is invalid delete pointer: either not allocated or with corrupt header.",
1111                                   addr, errno );
1112                } // if
1113        } else {
1114                #ifdef __CFA_DEBUG__
1115                // memset is NOT always inlined!
1116                disable_interrupts();
1117                // Scrub old memory so subsequent usages might fail. Only scrub the first/last SCRUB_SIZE bytes.
1118                char * data = ((Heap.Storage *)header)->data;   // data address
1119                size_t dsize = size - sizeof(Heap.Storage);             // data size
1120                if ( dsize <= SCRUB_SIZE * 2 ) {
1121                        memset( data, SCRUB, dsize );                           // scrub all
1122                } else {
1123                        memset( data, SCRUB, SCRUB_SIZE );                      // scrub front
1124                        memset( data + dsize - SCRUB_SIZE, SCRUB, SCRUB_SIZE ); // scrub back
1125                } // if
1126                enable_interrupts( false );
1127                #endif // __CFA_DEBUG__
1128
1129                if ( likely( heapManager == freeHead->homeManager ) ) { // belongs to this thread
1130                        header->kind.real.next = freeHead->freeList; // push on stack
1131                        freeHead->freeList = (Heap.Storage *)header;
1132                } else {                                                                                // return to thread owner
1133                        verify( heapManager );
1134
1135                        #ifdef OWNERSHIP
1136                        #ifdef RETURNSPIN
1137                        lock( freeHead->returnLock );
1138                        header->kind.real.next = freeHead->returnList; // push to bucket return list
1139                        freeHead->returnList = (Heap.Storage *)header;
1140                        unlock( freeHead->returnLock );
1141                        #else                                                                           // lock free
1142                        header->kind.real.next = freeHead->returnList; // link new node to top node
1143                        // CAS resets header->kind.real.next = freeHead->returnList on failure
1144                        while ( ! __atomic_compare_exchange_n( &freeHead->returnList, &header->kind.real.next, header,
1145                                                                                                   false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) );
1146                        #endif // RETURNSPIN
1147
1148                        #else                                                                           // no OWNERSHIP
1149
1150                        freeHead = &heap->freeLists[ClearStickyBits( header->kind.real.home ) - &freeHead->homeManager->freeLists[0]];
1151                        header->kind.real.next = freeHead->freeList; // push on stack
1152                        freeHead->freeList = (Heap.Storage *)header;
1153                        #endif // ! OWNERSHIP
1154
1155                        #ifdef __U_STATISTICS__
1156                        stats.return_pushes += 1;
1157                        stats.return_storage_request += rsize;
1158                        stats.return_storage_alloc += size;
1159                        #endif // __U_STATISTICS__
1160
1161                        // OK TO BE PREEMPTED HERE AS heapManager IS NO LONGER ACCESSED.
1162                } // if
1163        } // if
1164
1165        #ifdef __CFA_DEBUG__
1166        if ( traceHeap() ) {
1167                char helpText[64];
1168                __cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),
1169                                                                        "Free( %p ) size:%zu\n", addr, size ); // print debug/nodebug
1170        } // if
1171        #endif // __CFA_DEBUG__
1172
1173//      poll_interrupts();                                                                      // call rollforward
1174} // doFree
1175
1176
1177size_t prtFree( Heap & manager ) with( manager ) {
1178        size_t total = 0;
1179        #ifdef __STATISTICS__
1180        __cfaabi_bits_acquire();
1181        __cfaabi_bits_print_nolock( STDERR_FILENO, "\nBin lists (bin size : free blocks on list)\n" );
1182        #endif // __STATISTICS__
1183        for ( unsigned int i = 0; i < heapMaster.maxBucketsUsed; i += 1 ) {
1184                size_t size = freeLists[i].blockSize;
1185                #ifdef __STATISTICS__
1186                unsigned int N = 0;
1187                #endif // __STATISTICS__
1188
1189                #if BUCKETLOCK == SPINLOCK
1190                for ( Heap.Storage * p = freeLists[i].freeList; p != 0p; p = p->header.kind.real.next ) {
1191                #else
1192                        for(;;) {
1193//              for ( Heap.Storage * p = top( freeLists[i].freeList ); p != 0p; p = (p)`next->top ) {
1194//              for ( Heap.Storage * p = top( freeLists[i].freeList ); p != 0p; /* p = getNext( p )->top */) {
1195//                      Heap.Storage * temp = p->header.kind.real.next.top; // FIX ME: direct assignent fails, initialization works`
1196//                      typeof(p) temp = (( p )`next)->top;                     // FIX ME: direct assignent fails, initialization works`
1197//                      p = temp;
1198                #endif // BUCKETLOCK
1199                        total += size;
1200                        #ifdef __STATISTICS__
1201                        N += 1;
1202                        #endif // __STATISTICS__
1203                } // for
1204
1205                #ifdef __STATISTICS__
1206                __cfaabi_bits_print_nolock( STDERR_FILENO, "%7zu, %-7u  ", size, N );
1207                if ( (i + 1) % 8 == 0 ) __cfaabi_bits_print_nolock( STDERR_FILENO, "\n" );
1208                #endif // __STATISTICS__
1209        } // for
1210        #ifdef __STATISTICS__
1211        __cfaabi_bits_print_nolock( STDERR_FILENO, "\ntotal free blocks:%zu\n", total );
1212        __cfaabi_bits_release();
1213        #endif // __STATISTICS__
1214        return (char *)heapMaster.heapEnd - (char *)heapMaster.heapBegin - total;
1215} // prtFree
1216
1217
1218#ifdef __STATISTICS__
1219static void incCalls( intptr_t statName ) libcfa_nopreempt {
1220        heapManager->stats.counters[statName].calls += 1;
1221} // incCalls
1222
1223static void incZeroCalls( intptr_t statName ) libcfa_nopreempt {
1224        heapManager->stats.counters[statName].calls_0 += 1;
1225} // incZeroCalls
1226#endif // __STATISTICS__
1227
1228#ifdef __CFA_DEBUG__
1229static void incUnfreed( intptr_t offset ) libcfa_nopreempt {
1230        heapManager->allocUnfreed += offset;
1231} // incUnfreed
1232#endif // __CFA_DEBUG__
1233
1234
1235static void * memalignNoStats( size_t alignment, size_t size STAT_PARM ) {
1236        checkAlign( alignment );                                                        // check alignment
1237
1238        // if alignment <= default alignment or size == 0, do normal malloc as two headers are unnecessary
1239  if ( unlikely( alignment <= libAlign() || size == 0 ) ) return doMalloc( size STAT_ARG( STAT_NAME ) );
1240
1241        // Allocate enough storage to guarantee an address on the alignment boundary, and sufficient space before it for
1242        // administrative storage. NOTE, WHILE THERE ARE 2 HEADERS, THE FIRST ONE IS IMPLICITLY CREATED BY DOMALLOC.
1243        //      .-------------v-----------------v----------------v----------,
1244        //      | Real Header | ... padding ... |   Fake Header  | data ... |
1245        //      `-------------^-----------------^-+--------------^----------'
1246        //      |<--------------------------------' offset/align |<-- alignment boundary
1247
1248        // subtract libAlign() because it is already the minimum alignment
1249        // add sizeof(Storage) for fake header
1250        size_t offset = alignment - libAlign() + sizeof(Heap.Storage);
1251        char * addr = (char *)doMalloc( size + offset STAT_ARG( STAT_NAME ) );
1252
1253        // address in the block of the "next" alignment address
1254        char * user = (char *)ceiling2( (uintptr_t)(addr + sizeof(Heap.Storage)), alignment );
1255
1256        // address of header from malloc
1257        Heap.Storage.Header * realHeader = HeaderAddr( addr );
1258        realHeader->kind.real.size = size;                                      // correct size to eliminate above alignment offset
1259        #ifdef __CFA_DEBUG__
1260        incUnfreed( -offset );                                                          // adjustment off the offset from call to doMalloc
1261        #endif // __CFA_DEBUG__
1262
1263        // address of fake header *before* the alignment location
1264        Heap.Storage.Header * fakeHeader = HeaderAddr( user );
1265
1266        // SKULLDUGGERY: insert the offset to the start of the actual storage block and remember alignment
1267        fakeHeader->kind.fake.offset = (char *)fakeHeader - (char *)realHeader;
1268        // SKULLDUGGERY: odd alignment implies fake header
1269        fakeHeader->kind.fake.alignment = MarkAlignmentBit( alignment );
1270
1271        return user;
1272} // memalignNoStats
1273
1274
1275//####################### Memory Allocation Routines ####################
1276
1277
1278extern "C" {
1279        // Allocates size bytes and returns a pointer to the allocated memory.  The contents are undefined. If size is 0,
1280        // then malloc() returns a unique pointer value that can later be successfully passed to free().
1281        void * malloc( size_t size ) libcfa_public {
1282                return doMalloc( size STAT_ARG( MALLOC ) );
1283        } // malloc
1284
1285
1286        // Same as malloc() except size bytes is an array of dim elements each of elemSize bytes.
1287        void * aalloc( size_t dim, size_t elemSize ) libcfa_public {
1288                return doMalloc( dim * elemSize STAT_ARG( AALLOC ) );
1289        } // aalloc
1290
1291
1292        // Same as aalloc() with memory set to zero.
1293        void * calloc( size_t dim, size_t elemSize ) libcfa_public {
1294                size_t size = dim * elemSize;
1295                char * addr = (char *)doMalloc( size STAT_ARG( CALLOC ) );
1296
1297          if ( unlikely( addr == NULL ) ) return NULL;          // stop further processing if 0p is returned
1298
1299                Heap.Storage.Header * header;
1300                Heap.FreeHeader * freeHead;
1301                size_t bsize, alignment;
1302
1303                #ifndef __CFA_DEBUG__
1304                bool mapped =
1305                        #endif // __CFA_DEBUG__
1306                        headers( "calloc", addr, header, freeHead, bsize, alignment );
1307
1308                #ifndef __CFA_DEBUG__
1309                // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
1310                if ( likely( ! mapped ) )
1311                #endif // __CFA_DEBUG__
1312                        // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined
1313                        // `-header`-addr                      `-size
1314                        memset( addr, '\0', size );                                     // set to zeros
1315
1316                MarkZeroFilledBit( header );                                    // mark as zero fill
1317                return addr;
1318        } // calloc
1319
1320
1321        // Change the size of the memory block pointed to by oaddr to size bytes. The contents are undefined.  If oaddr is
1322        // 0p, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and oaddr is
1323        // not 0p, then the call is equivalent to free(oaddr). Unless oaddr is 0p, it must have been returned by an earlier
1324        // call to malloc(), alloc(), calloc() or realloc(). If the area pointed to was moved, a free(oaddr) is done.
1325        void * resize( void * oaddr, size_t size ) libcfa_public {
1326          if ( unlikely( oaddr == 0p ) ) {                              // => malloc( size )
1327                        return doMalloc( size STAT_ARG( RESIZE ) );
1328                } // if
1329
1330                PROLOG( RESIZE, doFree( oaddr ) );                              // => free( oaddr )
1331
1332                Heap.Storage.Header * header;
1333                Heap.FreeHeader * freeHead;
1334                size_t bsize, oalign;
1335                headers( "resize", oaddr, header, freeHead, bsize, oalign );
1336
1337                size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket
1338                // same size, DO NOT preserve STICKY PROPERTIES.
1339                if ( oalign == libAlign() && size <= odsize && odsize <= size * 2 ) { // allow 50% wasted storage for smaller size
1340                        ClearZeroFillBit( header );                                     // no alignment and turn off 0 fill
1341                        #ifdef __CFA_DEBUG__
1342                        incUnfreed( size - header->kind.real.size ); // adjustment off the size difference
1343                        #endif // __CFA_DEBUG__
1344                        header->kind.real.size = size;                          // reset allocation size
1345                        #ifdef __STATISTICS__
1346                        incCalls( RESIZE );
1347                        #endif // __STATISTICS__
1348                        return oaddr;
1349                } // if
1350
1351                // change size, DO NOT preserve STICKY PROPERTIES.
1352                doFree( oaddr );                                                                // free previous storage
1353
1354                return doMalloc( size STAT_ARG( RESIZE ) );             // create new area
1355        } // resize
1356
1357
1358        // Same as resize() but the contents are unchanged in the range from the start of the region up to the minimum of
1359        // the old and new sizes.
1360        void * realloc( void * oaddr, size_t size ) libcfa_public {
1361          if ( unlikely( oaddr == 0p ) ) {                                      // => malloc( size )
1362                  return doMalloc( size STAT_ARG( REALLOC ) );
1363                } // if
1364
1365                PROLOG( REALLOC, doFree( oaddr ) );                             // => free( oaddr )
1366
1367                Heap.Storage.Header * header;
1368                Heap.FreeHeader * freeHead;
1369                size_t bsize, oalign;
1370                headers( "realloc", oaddr, header, freeHead, bsize, oalign );
1371
1372                size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket
1373                size_t osize = header->kind.real.size;                  // old allocation size
1374                bool ozfill = ZeroFillBit( header );                    // old allocation zero filled
1375          if ( unlikely( size <= odsize ) && odsize <= size * 2 ) { // allow up to 50% wasted storage
1376                        #ifdef __CFA_DEBUG__
1377                        incUnfreed( size - header->kind.real.size ); // adjustment off the size difference
1378                        #endif // __CFA_DEBUG__
1379                        header->kind.real.size = size;                          // reset allocation size
1380                        if ( unlikely( ozfill ) && size > osize ) {     // previous request zero fill and larger ?
1381                                memset( (char *)oaddr + osize, '\0', size - osize ); // initialize added storage
1382                        } // if
1383                        #ifdef __STATISTICS__
1384                        incCalls( REALLOC );
1385                        #endif // __STATISTICS__
1386                        return oaddr;
1387                } // if
1388
1389                // change size and copy old content to new storage
1390
1391                void * naddr;
1392                if ( likely( oalign <= libAlign() ) ) {                 // previous request not aligned ?
1393                        naddr = doMalloc( size STAT_ARG( REALLOC ) ); // create new area
1394                } else {
1395                        naddr = memalignNoStats( oalign, size STAT_ARG( REALLOC ) ); // create new aligned area
1396                } // if
1397
1398                headers( "realloc", naddr, header, freeHead, bsize, oalign );
1399                // To preserve prior fill, the entire bucket must be copied versus the size.
1400                memcpy( naddr, oaddr, min( osize, size ) );             // copy bytes
1401                doFree( oaddr );                                                                // free previous storage
1402
1403                if ( unlikely( ozfill ) ) {                                             // previous request zero fill ?
1404                        MarkZeroFilledBit( header );                            // mark new request as zero filled
1405                        if ( size > osize ) {                                           // previous request larger ?
1406                                memset( (char *)naddr + osize, '\0', size - osize ); // initialize added storage
1407                        } // if
1408                } // if
1409                return naddr;
1410        } // realloc
1411
1412
1413        // Same as realloc() except the new allocation size is large enough for an array of nelem elements of size elsize.
1414        void * reallocarray( void * oaddr, size_t dim, size_t elemSize ) libcfa_public {
1415                return realloc( oaddr, dim * elemSize );
1416        } // reallocarray
1417
1418
1419        // Same as malloc() except the memory address is a multiple of alignment, which must be a power of two. (obsolete)
1420        void * memalign( size_t alignment, size_t size ) libcfa_public {
1421                return memalignNoStats( alignment, size STAT_ARG( MEMALIGN ) );
1422        } // memalign
1423
1424
1425        // Same as aalloc() with memory alignment.
1426        void * amemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public {
1427                return memalignNoStats( alignment, dim * elemSize STAT_ARG( AMEMALIGN ) );
1428        } // amemalign
1429
1430
1431        // Same as calloc() with memory alignment.
1432        void * cmemalign( size_t alignment, size_t dim, size_t elemSize ) libcfa_public {
1433                size_t size = dim * elemSize;
1434                char * addr = (char *)memalignNoStats( alignment, size STAT_ARG( CMEMALIGN ) );
1435
1436          if ( unlikely( addr == NULL ) ) return NULL;          // stop further processing if 0p is returned
1437
1438                Heap.Storage.Header * header;
1439                Heap.FreeHeader * freeHead;
1440                size_t bsize;
1441
1442                #ifndef __CFA_DEBUG__
1443                bool mapped =
1444                        #endif // __CFA_DEBUG__
1445                        headers( "cmemalign", addr, header, freeHead, bsize, alignment );
1446
1447                // Mapped storage is zero filled, but in debug mode mapped memory is scrubbed in doMalloc, so it has to be reset to zero.
1448                #ifndef __CFA_DEBUG__
1449                if ( ! mapped )
1450                #endif // __CFA_DEBUG__
1451                        // <-------0000000000000000000000000000UUUUUUUUUUUUUUUUUUUUUUUUU> bsize (bucket size) U => undefined
1452                        // `-header`-addr                      `-size
1453                        memset( addr, '\0', size );                                     // set to zeros
1454
1455                MarkZeroFilledBit( header );                                    // mark as zero filled
1456                return addr;
1457        } // cmemalign
1458
1459
1460        // Same as memalign(), but ISO/IEC 2011 C11 Section 7.22.2 states: the value of size shall be an integral multiple
1461        // of alignment. This requirement is universally ignored.
1462        void * aligned_alloc( size_t alignment, size_t size ) libcfa_public {
1463                return memalign( alignment, size );
1464        } // aligned_alloc
1465
1466
1467        // Allocates size bytes and places the address of the allocated memory in *memptr. The address of the allocated
1468        // memory shall be a multiple of alignment, which must be a power of two and a multiple of sizeof(void *). If size
1469        // is 0, then posix_memalign() returns either 0p, or a unique pointer value that can later be successfully passed to
1470        // free(3).
1471        int posix_memalign( void ** memptr, size_t alignment, size_t size ) libcfa_public {
1472          if ( unlikely( alignment < libAlign() || ! is_pow2( alignment ) ) ) return EINVAL; // check alignment
1473                *memptr = memalign( alignment, size );
1474                return 0;
1475        } // posix_memalign
1476
1477
1478        // Allocates size bytes and returns a pointer to the allocated memory. The memory address shall be a multiple of the
1479        // page size.  It is equivalent to memalign(sysconf(_SC_PAGESIZE),size).
1480        void * valloc( size_t size ) libcfa_public {
1481                return memalign( __page_size, size );
1482        } // valloc
1483
1484
1485        // Same as valloc but rounds size to multiple of page size.
1486        void * pvalloc( size_t size ) libcfa_public {
1487                return memalign( __page_size, ceiling2( size, __page_size ) ); // round size to multiple of page size
1488        } // pvalloc
1489
1490
1491        // Frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc()
1492        // or realloc().  Otherwise, or if free(ptr) has already been called before, undefined behaviour occurs. If ptr is
1493        // 0p, no operation is performed.
1494        void free( void * addr ) libcfa_public {
1495//              verify( heapManager );
1496
1497          if ( unlikely( addr == 0p ) ) {                                       // special case
1498                        #ifdef __STATISTICS__
1499                  if ( heapManager )
1500                        incZeroCalls( FREE );
1501                        #endif // __STATISTICS__
1502                        return;
1503                } // if
1504
1505                #ifdef __STATISTICS__
1506                incCalls( FREE );
1507                #endif // __STATISTICS__
1508
1509                doFree( addr );                                                                 // handles heapManager == nullptr
1510        } // free
1511
1512
1513        // Returns the alignment of an allocation.
1514        size_t malloc_alignment( void * addr ) libcfa_public {
1515          if ( unlikely( addr == 0p ) ) return libAlign();      // minimum alignment
1516                Heap.Storage.Header * header = HeaderAddr( addr );
1517                if ( unlikely( AlignmentBit( header ) ) ) {             // fake header ?
1518                        return ClearAlignmentBit( header );                     // clear flag from value
1519                } else {
1520                        return libAlign();                                                      // minimum alignment
1521                } // if
1522        } // malloc_alignment
1523
1524
1525        // Returns true if the allocation is zero filled, e.g., allocated by calloc().
1526        bool malloc_zero_fill( void * addr ) libcfa_public {
1527          if ( unlikely( addr == 0p ) ) return false;           // null allocation is not zero fill
1528                Heap.Storage.Header * header = HeaderAddr( addr );
1529                if ( unlikely( AlignmentBit( header ) ) ) {             // fake header ?
1530                        header = RealHeader( header );                          // backup from fake to real header
1531                } // if
1532                return ZeroFillBit( header );                                   // zero filled ?
1533        } // malloc_zero_fill
1534
1535
1536        // Returns original total allocation size (not bucket size) => array size is dimension * sizeof(T).
1537        size_t malloc_size( void * addr ) libcfa_public {
1538          if ( unlikely( addr == 0p ) ) return 0;                       // null allocation has zero size
1539                Heap.Storage.Header * header = HeaderAddr( addr );
1540                if ( unlikely( AlignmentBit( header ) ) ) {             // fake header ?
1541                        header = RealHeader( header );                          // backup from fake to real header
1542                } // if
1543                return header->kind.real.size;
1544        } // malloc_size
1545
1546
1547        // Returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by
1548        // malloc or a related function.
1549        size_t malloc_usable_size( void * addr ) libcfa_public {
1550          if ( unlikely( addr == 0p ) ) return 0;                       // null allocation has 0 size
1551                Heap.Storage.Header * header;
1552                Heap.FreeHeader * freeHead;
1553                size_t bsize, alignment;
1554
1555                headers( "malloc_usable_size", addr, header, freeHead, bsize, alignment );
1556                return DataStorage( bsize, addr, header );              // data storage in bucket
1557        } // malloc_usable_size
1558
1559
1560        // Prints (on default standard error) statistics about memory allocated by malloc and related functions.
1561        void malloc_stats( void ) libcfa_public {
1562                #ifdef __STATISTICS__
1563                HeapStatistics stats;
1564                HeapStatisticsCtor( stats );
1565                if ( printStats( collectStats( stats ) ) == -1 ) {
1566                #else
1567                #define MALLOC_STATS_MSG "malloc_stats statistics disabled.\n"
1568                if ( write( STDERR_FILENO, MALLOC_STATS_MSG, sizeof( MALLOC_STATS_MSG ) - 1 /* size includes '\0' */ ) == -1 ) {
1569                #endif // __STATISTICS__
1570                        abort( "**** Error **** write failed in malloc_stats" );
1571                } // if
1572        } // malloc_stats
1573
1574
1575        // Changes the file descriptor where malloc_stats() writes statistics.
1576        int malloc_stats_fd( int fd __attribute__(( unused )) ) libcfa_public {
1577                #ifdef __STATISTICS__
1578                int temp = heapMaster.stats_fd;
1579                heapMaster.stats_fd = fd;
1580                return temp;
1581                #else
1582                return -1;                                                                              // unsupported
1583                #endif // __STATISTICS__
1584        } // malloc_stats_fd
1585
1586
1587        // Prints an XML string that describes the current state of the memory-allocation implementation in the caller.
1588        // The string is printed on the file stream stream.  The exported string includes information about all arenas (see
1589        // malloc).
1590        int malloc_info( int options, FILE * stream __attribute__(( unused )) ) libcfa_public {
1591          if ( options != 0 ) { errno = EINVAL; return -1; }
1592                #ifdef __STATISTICS__
1593                HeapStatistics stats;
1594                HeapStatisticsCtor( stats );
1595                return printStatsXML( collectStats( stats ), stream ); // returns bytes written or -1
1596                #else
1597                return 0;                                                                               // unsupported
1598                #endif // __STATISTICS__
1599        } // malloc_info
1600
1601
1602        // Adjusts parameters that control the behaviour of the memory-allocation functions (see malloc). The param argument
1603        // specifies the parameter to be modified, and value specifies the new value for that parameter.
1604        int mallopt( int option, int value ) libcfa_public {
1605          if ( value < 0 ) return 0;
1606                choose( option ) {
1607                  case M_TOP_PAD:
1608                        heapMaster.heapExpand = ceiling2( value, __page_size );
1609                        return 1;
1610                  case M_MMAP_THRESHOLD:
1611                        if ( setMmapStart( value ) ) return 1;
1612                } // choose
1613                return 0;                                                                               // error, unsupported
1614        } // mallopt
1615
1616
1617        // Attempt to release free memory at the top of the heap (by calling sbrk with a suitable argument).
1618        int malloc_trim( size_t ) libcfa_public {
1619                return 0;                                                                               // => impossible to release memory
1620        } // malloc_trim
1621
1622
1623        // Records the current state of all malloc internal bookkeeping variables (but not the actual contents of the heap
1624        // or the state of malloc_hook functions pointers).  The state is recorded in a system-dependent opaque data
1625        // structure dynamically allocated via malloc, and a pointer to that data structure is returned as the function
1626        // result.  (The caller must free this memory.)
1627        void * malloc_get_state( void ) libcfa_public {
1628                return 0p;                                                                              // unsupported
1629        } // malloc_get_state
1630
1631
1632        // Restores the state of all malloc internal bookkeeping variables to the values recorded in the opaque data
1633        // structure pointed to by state.
1634        int malloc_set_state( void * ) libcfa_public {
1635                return 0;                                                                               // unsupported
1636        } // malloc_set_state
1637
1638
1639        // Sets the amount (bytes) to extend the heap when there is insufficent free storage to service an allocation.
1640        __attribute__((weak)) size_t malloc_expansion() libcfa_public { return __CFA_DEFAULT_HEAP_EXPANSION__; }
1641
1642        // Sets the crossover point between allocations occuring in the sbrk area or separately mmapped.
1643        __attribute__((weak)) size_t malloc_mmap_start() libcfa_public { return __CFA_DEFAULT_MMAP_START__; }
1644
1645        // Amount subtracted to adjust for unfreed program storage (debug only).
1646        __attribute__((weak)) size_t malloc_unfreed() libcfa_public { return __CFA_DEFAULT_HEAP_UNFREED__; }
1647} // extern "C"
1648
1649
1650// Must have CFA linkage to overload with C linkage realloc.
1651void * resize( void * oaddr, size_t nalign, size_t size ) libcfa_public {
1652  if ( unlikely( oaddr == 0p ) ) {                                              // => malloc( size )
1653                return memalignNoStats( nalign, size STAT_ARG( RESIZE ) );
1654        } // if
1655
1656        PROLOG( RESIZE, doFree( oaddr ) );                                      // => free( oaddr )
1657
1658        // Attempt to reuse existing alignment.
1659        Heap.Storage.Header * header = HeaderAddr( oaddr );
1660        bool isFakeHeader = AlignmentBit( header );                     // old fake header ?
1661        size_t oalign;
1662
1663        if ( unlikely( isFakeHeader ) ) {
1664                checkAlign( nalign );                                                   // check alignment
1665                oalign = ClearAlignmentBit( header );                   // old alignment
1666                if ( unlikely( (uintptr_t)oaddr % nalign == 0   // lucky match ?
1667                         && ( oalign <= nalign                                          // going down
1668                                  || (oalign >= nalign && oalign <= 256) ) // little alignment storage wasted ?
1669                        ) ) {
1670                        HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same)
1671                        Heap.FreeHeader * freeHead;
1672                        size_t bsize, oalign;
1673                        headers( "resize", oaddr, header, freeHead, bsize, oalign );
1674                        size_t odsize = DataStorage( bsize, oaddr, header ); // data storage available in bucket
1675
1676                        if ( size <= odsize && odsize <= size * 2 ) { // allow 50% wasted data storage
1677                                HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same)
1678                                ClearZeroFillBit( header );                             // turn off 0 fill
1679                                #ifdef __CFA_DEBUG__
1680                                incUnfreed( size - header->kind.real.size ); // adjustment off the size difference
1681                                #endif // __CFA_DEBUG__
1682                                header->kind.real.size = size;                  // reset allocation size
1683                                #ifdef __STATISTICS__
1684                                incCalls( RESIZE );
1685                                #endif // __STATISTICS__
1686                                return oaddr;
1687                        } // if
1688                } // if
1689        } else if ( ! isFakeHeader                                                      // old real header (aligned on libAlign) ?
1690                                && nalign == libAlign() ) {                             // new alignment also on libAlign => no fake header needed
1691                return resize( oaddr, size );                                   // duplicate special case checks
1692        } // if
1693
1694        // change size, DO NOT preserve STICKY PROPERTIES.
1695        doFree( oaddr );                                                                        // free previous storage
1696        return memalignNoStats( nalign, size STAT_ARG( RESIZE ) ); // create new aligned area
1697} // resize
1698
1699
1700void * realloc( void * oaddr, size_t nalign, size_t size ) libcfa_public {
1701  if ( unlikely( oaddr == 0p ) ) {                                              // => malloc( size )
1702                return memalignNoStats( nalign, size STAT_ARG( REALLOC ) );
1703        } // if
1704
1705        PROLOG( REALLOC, doFree( oaddr ) );                                     // => free( oaddr )
1706
1707        // Attempt to reuse existing alignment.
1708        Heap.Storage.Header * header = HeaderAddr( oaddr );
1709        bool isFakeHeader = AlignmentBit( header );                     // old fake header ?
1710        size_t oalign;
1711        if ( unlikely( isFakeHeader ) ) {
1712                checkAlign( nalign );                                                   // check alignment
1713                oalign = ClearAlignmentBit( header );                   // old alignment
1714                if ( unlikely( (uintptr_t)oaddr % nalign == 0   // lucky match ?
1715                         && ( oalign <= nalign                                          // going down
1716                                  || (oalign >= nalign && oalign <= 256) ) // little alignment storage wasted ?
1717                        ) ) {
1718                        HeaderAddr( oaddr )->kind.fake.alignment = MarkAlignmentBit( nalign ); // update alignment (could be the same)
1719                        return realloc( oaddr, size );                          // duplicate special case checks
1720                } // if
1721        } else if ( ! isFakeHeader                                                      // old real header (aligned on libAlign) ?
1722                                && nalign == libAlign() ) {                             // new alignment also on libAlign => no fake header needed
1723                return realloc( oaddr, size );                                  // duplicate special case checks
1724        } // if
1725
1726        Heap.FreeHeader * freeHead;
1727        size_t bsize;
1728        headers( "realloc", oaddr, header, freeHead, bsize, oalign );
1729
1730        // change size and copy old content to new storage
1731
1732        size_t osize = header->kind.real.size;                          // old allocation size
1733        bool ozfill = ZeroFillBit( header );                            // old allocation zero filled
1734
1735        void * naddr = memalignNoStats( nalign, size STAT_ARG( REALLOC ) ); // create new aligned area
1736
1737        headers( "realloc", naddr, header, freeHead, bsize, oalign );
1738        memcpy( naddr, oaddr, min( osize, size ) );                     // copy bytes
1739        doFree( oaddr );                                                                        // free previous storage
1740
1741        if ( unlikely( ozfill ) ) {                                                     // previous request zero fill ?
1742                MarkZeroFilledBit( header );                                    // mark new request as zero filled
1743                if ( size > osize ) {                                                   // previous request larger ?
1744                        memset( (char *)naddr + osize, '\0', size - osize ); // initialize added storage
1745                } // if
1746        } // if
1747        return naddr;
1748} // realloc
1749
1750
1751void * reallocarray( void * oaddr, size_t nalign, size_t dim, size_t elemSize ) __THROW {
1752        return realloc( oaddr, nalign, dim * elemSize );
1753} // reallocarray
1754
1755
1756// Local Variables: //
1757// tab-width: 4 //
1758// compile-command: "cfa -nodebug -O2 heap.cfa" //
1759// End: //
Note: See TracBrowser for help on using the repository browser.