-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathllheap.cc
2034 lines (1654 loc) · 85.9 KB
/
llheap.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "llheap.h"
#include <new> // set_new_handler
#include <cstdlib> // abort
#include <cstring> // strlen, memset, memcpy
#include <climits> // ULONG_MAX
#include <cstdarg> // va_start, va_end
#include <cerrno> // errno, ENOMEM, EINVAL
#include <cassert> // assert
#include <cstdint> // uintptr_t, uint64_t, uint32_t
#include <unistd.h> // STDERR_FILENO, sbrk, sysconf, write
#include <sys/mman.h> // mmap, munmap
#include <sys/sysinfo.h> // get_nprocs
#define str(s) #s
#define xstr(s) str(s)
#define WARNING( s ) xstr( GCC diagnostic ignored str( -W ## s ) )
#define NOWARNING( statement, warning ) \
_Pragma( "GCC diagnostic push" ) \
_Pragma( WARNING( warning ) ) \
statement ; \
_Pragma ( "GCC diagnostic pop" )
#define __FASTLOOKUP__ // use O(1) table lookup from allocation size to bucket size for small allocations
#define __OWNERSHIP__ // return freed memory to owner thread
//#define __REMOTESPIN__ // toggle spinlock / lockfree queue
#if ! defined( __OWNERSHIP__ ) && defined( __REMOTESPIN__ )
#warning "REMOTESPIN is ignored without OWNERSHIP; suggest commenting out REMOTESPIN"
#endif // ! __OWNERSHIP__ && __REMOTESPIN__
#define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#define CACHE_ALIGN 64
#define CALIGN __attribute__(( aligned(CACHE_ALIGN) ))
#ifdef TLS
#define TLSMODEL __attribute__(( tls_model("initial-exec") ))
#else
#define TLSMODEL
#endif // TLS
//#define __LL_DEBUG__
#ifdef __LL_DEBUG__
#define LLDEBUG( stmt ) stmt
#else
#define LLDEBUG( stmt )
#endif // __LL_DEBUG__
// The return code from "write" is ignored. Basically, if write fails, trying to write out why it failed will likely
// fail, too, so just continue.
//######################### Helpers #########################
enum { BufSize = 1024 };
#define always_assert(expr) \
(static_cast <bool> (expr) \
? void (0) \
: __assert_fail(#expr, __FILE__, __LINE__, __extension__ __PRETTY_FUNCTION__))
// Called by macro assert in assert.h. Replace to prevent recursive call to malloc.
extern "C" void __assert_fail( const char assertion[], const char file[], unsigned int line, const char function[] ) {
extern const char * __progname; // global name of running executable (argv[0])
char helpText[BufSize];
int len = snprintf( helpText, sizeof(helpText), "Internal assertion error \"%s\" from program \"%s\" in \"%s\" at line %d in file \"%s.\n",
assertion, __progname, function, line, file );
int unused __attribute__(( unused )) = write( STDERR_FILENO, helpText, len ); // file might be closed
abort();
// CONTROL NEVER REACHES HERE!
} // __assert_fail
#ifdef __DEBUG__
static bool signal_p = false;
static void backtrace( int start ); // forward
#endif // __DEBUG__
static void abort( const char fmt[], ... ) __attribute__(( format(printf, 1, 2), __nothrow__, __noreturn__ ));
static void abort( const char fmt[], ... ) { // overload real abort
va_list args;
va_start( args, fmt );
char buf[BufSize];
int len = vsnprintf( buf, BufSize, fmt, args );
int unused __attribute__(( unused )) = write( STDERR_FILENO, buf, len ); // file might be closed
if ( fmt[strlen( fmt ) - 1] != '\n' ) { // add optional newline if missing at the end of the format text
int unused __attribute__(( unused )) = write( STDERR_FILENO, "\n", 1 ); // file might be closed
} // if
va_end( args );
#ifdef __DEBUG__
backtrace( signal_p ? 4 : 2 );
#endif // __DEBUG__
abort(); // call the real abort
// CONTROL NEVER REACHES HERE!
} // abort
static void debugprt( const char fmt[], ... ) __attribute__(( format(printf, 1, 2), unused ));
static void debugprt( const char fmt[] __attribute__(( unused )), ... ) {
va_list args;
va_start( args, fmt );
char buf[BufSize];
int len = vsnprintf( buf, BufSize, fmt, args );
int unused __attribute__(( unused )) = write( STDERR_FILENO, buf, len ); // file might be closed
va_end( args );
} // debugprt
static inline __attribute__((always_inline)) bool Pow2( unsigned long int value ) {
// clears all bits below value, rounding value down to the next lower multiple of value
return (value & (value - 1)) == 0;
} // Pow2
static inline __attribute__((always_inline)) unsigned long int Floor( unsigned long int value, unsigned long int alignment ) {
assert( Pow2( alignment ) );
// clears all bits above or equal to alignment, getting (value % alignment), the phase of value with regards to alignment
return value & -alignment;
} // Floor
static inline __attribute__((always_inline)) unsigned long int Ceiling( unsigned long int value, unsigned long int alignment ) {
assert( Pow2( alignment ) );
// "negate, round down, negate" is the same as round up
return -Floor( -value, alignment );
} // Ceiling
// std::min and std::lower_bound do not always inline, so substitute hand-coded versions.
#define Min( x, y ) (x < y ? x : y)
static inline __attribute__((always_inline)) size_t Bsearchl( unsigned int key, const unsigned int vals[], size_t dimension ) {
size_t l = 0, m, h = dimension;
while ( l < h ) {
m = (l + h) / 2;
if ( (unsigned int &)(vals[m]) < key ) { // cast away const
l = m + 1;
} else {
h = m;
} // if
} // while
return l;
} // Bsearchl
// reference parameters
#define Fai( change, Inc ) __atomic_fetch_add( (&(change)), (Inc), __ATOMIC_SEQ_CST )
#define Tas( lock ) __atomic_test_and_set( (&(lock)), __ATOMIC_ACQUIRE )
#define Clr( lock ) __atomic_clear( (&(lock)), __ATOMIC_RELEASE )
#define Fas( change, assn ) __atomic_exchange_n( (&(change)), (assn), __ATOMIC_SEQ_CST )
#define Cas( change, comp, assn ) ({decltype(comp) __temp = (comp); __atomic_compare_exchange_n( (&(change)), (&(__temp)), (assn), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ); })
#define Casv( change, comp, assn ) __atomic_compare_exchange_n( (&(change)), (comp), (assn), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST )
// pause to prevent excess processor bus usage
#if defined( __i386 ) || defined( __x86_64 )
#define Pause() __asm__ __volatile__ ( "pause" : : : )
#elif defined(__ARM_ARCH)
#define Pause() __asm__ __volatile__ ( "YIELD" : : : )
#else
#error unsupported architecture
#endif
typedef volatile uintptr_t SpinLock_t;
static inline __attribute__((always_inline)) void spin_acquire( volatile SpinLock_t * lock ) {
enum { SPIN_START = 4, SPIN_END = 64 * 1024, };
unsigned int spin = SPIN_START;
for ( unsigned int i = 1;; i += 1 ) {
if ( *lock == 0 && Tas( *lock ) == 0 ) break; // Fence
for ( volatile unsigned int s = 0; s < spin; s += 1 ) Pause(); // exponential spin
spin += spin; // powers of 2
//if ( i % 64 == 0 ) spin += spin; // slowly increase by powers of 2
if ( spin > SPIN_END ) spin = SPIN_END; // cap spinning
} // for
} // spin_lock
static inline __attribute__((always_inline)) void spin_release( volatile SpinLock_t * lock ) {
Clr( *lock ); // Fence
} // spin_unlock
//######################### Back Trace #########################
// Need to compile with -rdynamic to get symbol names in back trace.
#ifdef __DEBUG__
#include <execinfo.h> // backtrace, backtrace_symbols
#include <cxxabi.h> // __cxa_demangle
static void backtrace( int start ) {
enum {
Frames = 50, // maximum number of stack frames
Last = 2, // skip last N stack frames
};
void * array[Frames];
size_t size = ::backtrace( array, Frames );
char ** messages = ::backtrace_symbols( array, size ); // does not demangle names
char helpText[256];
int len;
*index( messages[0], '(' ) = '\0'; // find executable name
len = snprintf( helpText, 256, "Stack back trace for: %s\n", messages[0] );
debugprt( helpText, len );
// skip stack frames after (below) main
for ( unsigned int i = start; i < size - Last && messages != nullptr; i += 1 ) {
char * mangled_name = nullptr, * offset_begin = nullptr, * offset_end = nullptr;
for ( char * p = messages[i]; *p; ++p ) { // find parantheses and +offset
if ( *p == '(' ) {
mangled_name = p;
} else if ( *p == '+' ) {
offset_begin = p;
} else if ( *p == ')' ) {
offset_end = p;
break;
} // if
} // for
// if line contains symbol, attempt to demangle
int frameNo = i - start;
if ( mangled_name && offset_begin && offset_end && mangled_name < offset_begin ) {
*mangled_name++ = '\0'; // delimit strings
*offset_begin++ = '\0';
*offset_end++ = '\0';
int status;
char * real_name = __cxxabiv1::__cxa_demangle( mangled_name, 0, 0, &status );
// bug in __cxa_demangle for single-character lower-case non-mangled names
if ( status == 0 ) { // demangling successful ?
len = snprintf( helpText, 256, "(%d) %s %s+%s%s\n",
frameNo, messages[i], real_name, offset_begin, offset_end );
} else { // otherwise, output mangled name
len = snprintf( helpText, 256, "(%d) %s %s(/*unknown*/)+%s%s\n",
frameNo, messages[i], mangled_name, offset_begin, offset_end );
} // if
free( real_name );
} else { // otherwise, print the whole line
len = snprintf( helpText, 256, "(%d) %s\n", frameNo, messages[i] );
} // if
debugprt( helpText, len );
} // for
free( messages );
} // backtrace
#endif // __DEBUG__
//####################### SIGSEGV/SIGBUS ####################
#ifdef __DEBUG__
#include <signal.h> // get_nprocs
#define __SIGCXT__ ucontext_t *
#define __SIGPARMS__ int sig __attribute__(( unused )), siginfo_t * sfp __attribute__(( unused )), __SIGCXT__ cxt __attribute__(( unused ))
static void signal( int sig, void (*handler)(__SIGPARMS__), int flags ) { // name clash with uSignal statement
struct sigaction act;
act.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler;
sigemptyset( &act.sa_mask );
sigaddset( &act.sa_mask, SIGALRM ); // disabled during signal handler
sigaddset( &act.sa_mask, SIGUSR1 );
sigaddset( &act.sa_mask, SIGSEGV );
sigaddset( &act.sa_mask, SIGBUS );
sigaddset( &act.sa_mask, SIGILL );
sigaddset( &act.sa_mask, SIGFPE );
sigaddset( &act.sa_mask, SIGHUP ); // revert to default on second delivery
sigaddset( &act.sa_mask, SIGTERM );
sigaddset( &act.sa_mask, SIGINT );
act.sa_flags = flags;
if ( sigaction( sig, &act, nullptr ) == -1 ) {
char helpText[256];
int len = snprintf( helpText, 256, "signal( sig:%d, handler:%p, flags:%d ), problem installing signal handler, error(%d) %s.\n",
sig, handler, flags, errno, strerror( errno ) );
if ( write( STDERR_FILENO, helpText, len ) ) {
_exit( EXIT_FAILURE );
} // if
} // signal
}
static void sigSegvBusHandler( __SIGPARMS__ ) {
signal_p = true;
if ( sfp->si_addr == nullptr ) {
abort( "Null pointer (nullptr) dereference." );
} else if ( sfp->si_addr ==
#if __WORDSIZE == 32
(void *)0xffff'ffff
#else
(void *)0xffff'ffff'ffff'ffff
#endif // __WORDSIZE == 32
) {
abort( "Using a scrubbed pointer address %p.\n"
"Possible cause is using uninitialized storage or using storage after it has been freed.",
sfp->si_addr );
} else {
abort( "%s at memory location %p.\n"
"Possible cause is reading outside the address space or writing to a protected area within the address space with an invalid pointer or subscript.",
(sig == SIGSEGV ? "Segment fault" : "Bus error"), sfp->si_addr );
} // if
} // sigSegvBusHandler
#endif // __DEBUG__
//####################### Heap Statistics ####################
#ifdef __STATISTICS__
enum { CntTriples = 13 }; // number of counter triples
struct HeapStatistics {
enum { MALLOC, AALLOC, CALLOC, MEMALIGN, AMEMALIGN, CMEMALIGN, RESIZE, REALLOC, FREE };
union {
// Statistic counters are unsigned long long int => use 64-bit counters on both 32 and 64 bit architectures.
// On 32-bit architectures, the 64-bit counters are simulated with multi-precise 32-bit computations.
struct { // minimum qualification
unsigned long long int malloc_calls, malloc_0_calls;
unsigned long long int malloc_storage_request, malloc_storage_alloc;
unsigned long long int aalloc_calls, aalloc_0_calls;
unsigned long long int aalloc_storage_request, aalloc_storage_alloc;
unsigned long long int calloc_calls, calloc_0_calls;
unsigned long long int calloc_storage_request, calloc_storage_alloc;
unsigned long long int memalign_calls, memalign_0_calls;
unsigned long long int memalign_storage_request, memalign_storage_alloc;
unsigned long long int amemalign_calls, amemalign_0_calls;
unsigned long long int amemalign_storage_request, amemalign_storage_alloc;
unsigned long long int cmemalign_calls, cmemalign_0_calls;
unsigned long long int cmemalign_storage_request, cmemalign_storage_alloc;
unsigned long long int resize_calls, resize_0_calls;
unsigned long long int resize_storage_request, resize_storage_alloc;
unsigned long long int realloc_calls, realloc_0_calls;
unsigned long long int realloc_storage_request, realloc_storage_alloc;
unsigned long long int realloc_copy, realloc_smaller;
unsigned long long int realloc_align, realloc_0_fill;
unsigned long long int free_calls, free_null_0_calls;
unsigned long long int free_storage_request, free_storage_alloc;
unsigned long long int remote_pushes, remote_pulls;
unsigned long long int remote_storage_request, remote_storage_alloc;
unsigned long long int mmap_calls, mmap_0_calls; // no zero calls
unsigned long long int mmap_storage_request, mmap_storage_alloc;
unsigned long long int munmap_calls, munmap_0_calls; // no zero calls
unsigned long long int munmap_storage_request, munmap_storage_alloc;
};
struct { // overlay for iteration
unsigned long long int calls, calls_0;
unsigned long long int request, alloc;
} counters[CntTriples];
};
}; // HeapStatistics
static_assert( sizeof(HeapStatistics) == CntTriples * sizeof(HeapStatistics::counters[0]),
"Heap statistics counter-triplets does not match with array size" );
static void HeapStatisticsCtor( HeapStatistics & stats ) {
memset( &stats, '\0', sizeof(stats) ); // very fast
// for ( unsigned int i = 0; i < CntTriples; i += 1 ) {
// stats.counters[i].calls = stats.counters[i].calls_0 = stats.counters[i].request = stats.counters[i].alloc = 0;
// } // for
} // HeapStatisticsCtor
static HeapStatistics & operator+=( HeapStatistics & lhs, const HeapStatistics & rhs ) {
for ( unsigned int i = 0; i < CntTriples; i += 1 ) {
lhs.counters[i].calls += rhs.counters[i].calls;
lhs.counters[i].calls_0 += rhs.counters[i].calls_0;
lhs.counters[i].request += rhs.counters[i].request;
lhs.counters[i].alloc += rhs.counters[i].alloc;
} // for
return lhs;
} // ::operator+=
#endif // __STATISTICS__
//####################### Heap Structure ####################
enum {
// The minimum allocation alignment in bytes, which must be <= smallest free bucket.
__ALIGN__ = __BIGGEST_ALIGNMENT__, // magic CPP variable
}; // enum
struct Heap {
struct FreeHeader; // forward declaration
struct Storage {
struct Header { // header
union Kind {
struct RealHeader { // 4-byte word => 8-byte header, 8-byte word => 16-byte header
union {
// 2nd low-order bit => zero filled, 3rd low-order bit => mmapped
FreeHeader * home; // allocated block points back to home locations (must overlay alignment)
size_t blockSize; // size for munmap (must overlay alignment)
Storage * next; // freed block points to next freed block of same size
};
size_t size; // allocation size in bytes
} real; // RealHeader
struct FakeHeader {
uintptr_t alignment; // 1st low-order bit => fake header & alignment
uintptr_t offset;
} fake; // FakeHeader
} kind; // Kind
} header; // Header
char pad[__ALIGN__ - sizeof( Header )];
char data[0]; // storage
}; // Storage
static_assert( __ALIGN__ >= sizeof( Storage ), "minimum alignment < sizeof( Storage )" );
struct CALIGN FreeHeader {
#ifdef __OWNERSHIP__
#ifdef __REMOTESPIN__
SpinLock_t remoteLock;
#endif // __REMOTESPIN__
Storage * remoteList; // other thread remote list
#endif // __OWNERSHIP__
Storage * freeList; // thread free list
Heap * homeManager; // heap owner (free storage to bucket, from bucket to heap)
size_t blockSize; // size of allocations on this list
#if defined( __STATISTICS__ )
size_t allocations, reuses;
#endif // __STATISTICS__
}; // FreeHeader
// Recursive definitions: HeapManager needs size of bucket array and bucket area needs sizeof HeapManager storage.
// Break recursion by hardcoding number of buckets and statically checking number is correct after bucket array defined.
enum { NoBucketSizes = 96 }; // number of bucket sizes
FreeHeader freeLists[NoBucketSizes]; // buckets for different allocation sizes
void * heapBuffer; // start of free storage in buffer
size_t heapReserve; // amount of remaining free storage in buffer
#if defined( __STATISTICS__ ) || defined( __DEBUG__ )
Heap * nextHeapManager; // intrusive link of existing heaps; traversed to collect statistics or check unfreed storage
#endif // __STATISTICS__ || __DEBUG__
Heap * nextFreeHeapManager; // intrusive link of free heaps from terminated threads; reused by new threads
#ifdef __DEBUG__
ptrdiff_t allocUnfreed; // running total of allocations minus frees; can be negative
#endif // __DEBUG__
#ifdef __STATISTICS__
HeapStatistics stats; // local statistic table for this heap
#endif // __STATISTICS__
}; // Heap
// Size of array must harmonize with NoBucketSizes and individual bucket sizes must be multiple of 16.
// Smaller multiples of 16 and powers of 2 are common allocation sizes, so make them generate the minimum required bucket size.
static const unsigned int CALIGN bucketSizes[] = { // different bucket sizes
// There is no 0-sized bucket becasue it is better to create a 16 byte bucket for rare malloc(0), which can be
// reused later by a 16-byte allocation.
16 + sizeof(Heap::Storage), 32 + sizeof(Heap::Storage), 48 + sizeof(Heap::Storage), 64 + sizeof(Heap::Storage), // 4
80 + sizeof(Heap::Storage), 96 + sizeof(Heap::Storage), 112 + sizeof(Heap::Storage), 128 + sizeof(Heap::Storage), // 4
160, 192, 224, 256 + sizeof(Heap::Storage), // 4
320, 384, 448, 512 + sizeof(Heap::Storage), // 4
640, 768, 896, 1'024 + sizeof(Heap::Storage), // 4
1'536, 2'048 + sizeof(Heap::Storage), // 2
2'560, 3'072, 3'584, 4'096 + sizeof(Heap::Storage), // 4
6'144, 8'192 + sizeof(Heap::Storage), // 2
9'216, 10'240, 11'264, 12'288, 13'312, 14'336, 15'360, 16'384 + sizeof(Heap::Storage), // 8
18'432, 20'480, 22'528, 24'576, 26'624, 28'672, 30'720, 32'768 + sizeof(Heap::Storage), // 8
36'864, 40'960, 45'056, 49'152, 53'248, 57'344, 61'440, 65'536 + sizeof(Heap::Storage), // 8
73'728, 81'920, 90'112, 98'304, 106'496, 114'688, 122'880, 131'072 + sizeof(Heap::Storage), // 8
147'456, 163'840, 180'224, 196'608, 212'992, 229'376, 245'760, 262'144 + sizeof(Heap::Storage), // 8
294'912, 327'680, 360'448, 393'216, 425'984, 458'752, 491'520, 524'288 + sizeof(Heap::Storage), // 8
655'360, 786'432, 917'504, 1'048'576 + sizeof(Heap::Storage), // 4
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
2'621'440, 3'145'728, 3'670'016, 4'194'304 + sizeof(Heap::Storage), // 4
6'291'456, 8'388'608 + sizeof(Heap::Storage), 12'582'912, 16'777'216 + sizeof(Heap::Storage), // 4
};
static_assert( Heap::NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array is wrong" );
#ifdef __FASTLOOKUP__
enum { LookupSizes = 65'536 + sizeof(Heap::Storage) }; // number of fast lookup sizes
static unsigned char CALIGN lookup[LookupSizes]; // O(1) lookup for small sizes
#endif // __FASTLOOKUP__
// Manipulate sticky bits stored in unused 3 low-order bits of an address.
// bit0 => alignment => fake header
// bit1 => zero filled (calloc)
// bit2 => mapped allocation versus sbrk
#define StickyBits( header ) (((header)->kind.real.blockSize & 0x7))
#define ClearStickyBits( addr ) (decltype(addr))((uintptr_t)(addr) & ~7)
#define MarkAlignmentBit( alignment ) ((alignment) | 1)
#define AlignmentBit( header ) ((((header)->kind.fake.alignment) & 1))
#define ClearAlignmentBit( header ) (((header)->kind.fake.alignment) & ~1)
#define ZeroFillBit( header ) ((((header)->kind.real.blockSize) & 2))
#define ClearZeroFillBit( header ) ((((header)->kind.real.blockSize) &= ~2))
#define MarkZeroFilledBit( header ) ((header)->kind.real.blockSize |= 2)
#define MmappedBit( header ) ((((header)->kind.real.blockSize) & 4))
#define MarkMmappedBit( size ) ((size) | 4)
enum {
// The default extension heap amount in units of bytes. When the current heap reaches the brk address, the brk
// address is extended by the extension amount.
__DEFAULT_HEAP_EXPANSION__ = 8 * 1024 * 1024,
// The mmap crossover point during allocation. Allocations less than this amount are allocated from buckets; values
// greater than or equal to this value are mmap from the operating system.
__DEFAULT_MMAP_START__ = 8 * 1024 * 1024 + sizeof(Heap::Storage),
// The default unfreed storage amount in units of bytes. When the program ends it subtracts this amount from
// the malloc/free counter to adjust for storage the program does not free.
__DEFAULT_HEAP_UNFREED__ = 0
}; // enum
static void heapManagerCtor();
static void heapManagerDtor();
namespace { // hide static members
// Used solely to detect when a thread terminates. Thread creation is handled by a fastpath dynamic check.
struct ThreadManager {
volatile bool trigger; // used to trigger allocation of thread-local object, otherwise unused
~ThreadManager() { heapManagerDtor(); } // called automagically when thread terminates
}; // ThreadManager
struct HeapMaster {
SpinLock_t extLock; // protects allocation-buffer extension
SpinLock_t mgrLock; // protects freeHeapManagersList, heapManagersList, heapManagersStorage, heapManagersStorageEnd
void * heapBegin; // start of heap
void * heapEnd; // logical end of heap
size_t heapRemaining; // amount of storage not allocated in the current chunk
size_t pageSize; // architecture pagesize
size_t heapExpand; // sbrk advance
size_t mmapStart; // cross over point for mmap
size_t maxBucketsUsed; // maximum number of buckets in use
Heap * heapManagersList; // heap-list head
Heap * freeHeapManagersList; // free-list head
// Heap superblocks are not linked; heaps in superblocks are linked via intrusive links.
Heap * heapManagersStorage; // next heap to use in heap superblock
Heap * heapManagersStorageEnd; // logical heap outside of superblock's end
#ifdef __DEBUG__
ptrdiff_t allocUnfreed; // running total of allocations minus frees; can be negative
#endif // __DEBUG__
#ifdef __STATISTICS__
HeapStatistics stats; // global stats for thread-local heaps to add there counters when exiting
unsigned long long int nremainder, remainder; // counts mostly unusable storage at the end of a thread's reserve block
unsigned long long int threads_started, threads_exited; // counts threads that have started and exited
unsigned long long int reused_heap, new_heap; // counts reusability of heaps
unsigned long long int sbrk_calls;
unsigned long long int sbrk_storage;
int stats_fd;
#endif // __STATISTICS__
static void heapMasterCtor();
static Heap * getHeap();
}; // HeapMaster
} // namespace
static volatile bool heapMasterBootFlag = false; // trigger for first heap
static HeapMaster heapMaster; // program global
// Thread-local storage is allocated lazily when the storage is accessed.
static thread_local size_t PAD1 CALIGN TLSMODEL __attribute__(( unused )); // protect prior false sharing
static thread_local ThreadManager threadManager CALIGN TLSMODEL;
static thread_local Heap * heapManager CALIGN TLSMODEL = (Heap *)1; // singleton
static thread_local size_t PAD2 CALIGN TLSMODEL __attribute__(( unused )); // protect further false sharing
// declare helper functions for HeapMaster
extern "C" void noMemory( void ); // forward, called by "builtin_new" when malloc returns 0
void HeapMaster::heapMasterCtor() {
// Singleton pattern to initialize heap master
heapMaster.pageSize = sysconf( _SC_PAGESIZE );
heapMaster.extLock = 0;
heapMaster.mgrLock = 0;
char * end = (char *)sbrk( 0 );
heapMaster.heapBegin = heapMaster.heapEnd = sbrk( (char *)Ceiling( (long unsigned int)end, __ALIGN__ ) - end ); // move start of heap to multiple of alignment
heapMaster.heapRemaining = 0;
heapMaster.heapExpand = malloc_expansion();
heapMaster.mmapStart = malloc_mmap_start();
// find the closest bucket size less than or equal to the mmapStart size
heapMaster.maxBucketsUsed = Bsearchl( heapMaster.mmapStart, bucketSizes, Heap::NoBucketSizes ); // binary search
assert( (heapMaster.mmapStart >= heapMaster.pageSize) && (bucketSizes[Heap::NoBucketSizes - 1] >= heapMaster.mmapStart) );
assert( heapMaster.maxBucketsUsed < Heap::NoBucketSizes ); // subscript failure ?
assert( heapMaster.mmapStart <= bucketSizes[heapMaster.maxBucketsUsed] ); // search failure ?
heapMaster.heapManagersList = nullptr;
heapMaster.freeHeapManagersList = nullptr;
heapMaster.heapManagersStorage = nullptr;
heapMaster.heapManagersStorageEnd = nullptr;
#ifdef __STATISTICS__
HeapStatisticsCtor( heapMaster.stats ); // clear statistic counters
heapMaster.nremainder = heapMaster.remainder = 0;
heapMaster.threads_started = heapMaster.threads_exited = 0;
heapMaster.reused_heap = heapMaster.new_heap = 0;
heapMaster.sbrk_calls = heapMaster.sbrk_storage = 0;
heapMaster.stats_fd = STDERR_FILENO;
#endif // __STATISTICS__
#ifdef __DEBUG__
LLDEBUG( debugprt( "MCtor %jd set to zero\n", heapMaster.allocUnfreed ) );
heapMaster.allocUnfreed = 0;
signal( SIGSEGV, sigSegvBusHandler, SA_SIGINFO | SA_ONSTACK ); // Invalid memory reference (default: Core)
signal( SIGBUS, sigSegvBusHandler, SA_SIGINFO | SA_ONSTACK ); // Bus error, bad memory access (default: Core)
#endif // __DEBUG__
#ifdef __FASTLOOKUP__
for ( unsigned int i = 0, idx = 0; i < LookupSizes; i += 1 ) {
if ( i > bucketSizes[idx] ) idx += 1;
lookup[i] = idx;
always_assert( i <= bucketSizes[idx] || // overflow buckets ?
i > bucketSizes[idx - 1] ); // overlapping bucket sizes ?
} // for
#endif // __FASTLOOKUP__
std::set_new_handler( noMemory ); // do not throw exception as the default
heapMasterBootFlag = true;
} // HeapMaster::heapMasterCtor
#define NO_MEMORY_MSG "**** Error **** insufficient heap memory available to allocate %zd new bytes."
Heap * HeapMaster::getHeap() {
Heap * heap;
if ( heapMaster.freeHeapManagersList ) { // free heap for reused ?
heap = heapMaster.freeHeapManagersList;
heapMaster.freeHeapManagersList = heap->nextFreeHeapManager;
#ifdef __STATISTICS__
heapMaster.reused_heap += 1;
#endif // __STATISTICS__
} else { // free heap not found, create new
// Heap size is about 12K, FreeHeader (128 bytes because of cache alignment) * NoBucketSizes (91) => 128 heaps *
// 12K ~= 120K byte superblock. Where 128-heap superblock handles a medium sized multi-processor server.
size_t remaining = heapMaster.heapManagersStorageEnd - heapMaster.heapManagersStorage; // remaining free heaps in superblock
if ( ! heapMaster.heapManagersStorage || remaining == 0 ) {
// Each block of heaps is a multiple of the number of cores on the computer.
int dimension = get_nprocs(); // get_nprocs_conf does not work
size_t size = dimension * sizeof( Heap );
heapMaster.heapManagersStorage = (Heap *)mmap( 0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
if ( UNLIKELY( heapMaster.heapManagersStorage == MAP_FAILED ) ) { // failed ?
if ( errno == ENOMEM ) abort( NO_MEMORY_MSG, size ); // no memory
// Do not call strerror( errno ) as it may call malloc.
abort( "**** Error **** attempt to allocate block of heaps of size %zu bytes and mmap failed with errno %d.", size, errno );
} // if
heapMaster.heapManagersStorageEnd = &heapMaster.heapManagersStorage[dimension]; // outside array
} // if
heap = heapMaster.heapManagersStorage;
heapMaster.heapManagersStorage = heapMaster.heapManagersStorage + 1; // bump next heap
#if defined( __STATISTICS__ ) || defined( __DEBUG__ )
heap->nextHeapManager = heapMaster.heapManagersList;
#endif // __STATISTICS__ || __DEBUG__
heapMaster.heapManagersList = heap;
#ifdef __STATISTICS__
heapMaster.new_heap += 1;
#endif // __STATISTICS__
for ( unsigned int j = 0; j < Heap::NoBucketSizes; j += 1 ) { // initialize free lists
heap->freeLists[j] = (Heap::FreeHeader){
#ifdef __OWNERSHIP__
#ifdef __REMOTESPIN__
.remoteLock = 0,
#endif // __REMOTESPIN__
.remoteList = nullptr,
#endif // __OWNERSHIP__
.freeList = nullptr,
.homeManager = heap,
.blockSize = bucketSizes[j],
#if defined( __STATISTICS__ )
.allocations = 0,
.reuses = 0,
#endif // __STATISTICS__
};
} // for
heap->heapBuffer = nullptr;
heap->heapReserve = 0;
heap->nextFreeHeapManager = nullptr;
#ifdef __DEBUG__
heap->allocUnfreed = 0;
#endif // __DEBUG__
} // if
return heap;
} // HeapMaster::getHeap
static void heapManagerCtor() {
if ( UNLIKELY( ! heapMasterBootFlag ) ) HeapMaster::heapMasterCtor();
spin_acquire( &heapMaster.mgrLock ); // protect heapMaster counters
// get storage for heap manager
heapManager = HeapMaster::getHeap();
#ifdef __STATISTICS__
HeapStatisticsCtor( heapManager->stats ); // heap local
heapMaster.threads_started += 1;
#endif // __STATISTICS__
spin_release( &heapMaster.mgrLock );
// Trigger thread_local storage implicit allocation, which(causes a dynamic allocation but not a recursive entry
// because heapManager is set above.
threadManager.trigger = true; // any value works
} // heapManagerCtor
static void heapManagerDtor() {
spin_acquire( &heapMaster.mgrLock ); // protect heapMaster counters
// push heap onto list of free heaps for reusability
heapManager->nextFreeHeapManager = heapMaster.freeHeapManagersList;
heapMaster.freeHeapManagersList = heapManager;
#ifdef __DEBUG__
LLDEBUG( debugprt( "HDtor %p %jd %jd\n", heapManager, heapManager->allocUnfreed, heapMaster.allocUnfreed ) );
#endif // __DEBUG__
#ifdef __STATISTICS__
heapMaster.stats += heapManager->stats; // retain this heap's statistics
HeapStatisticsCtor( heapManager->stats ); // reset heap counters for next usage
heapMaster.threads_exited += 1;
#endif // __STATISTICS__
// SKULLDUGGERY: The thread heap ends BEFORE the last free(s) occurs from the thread-local storage allocations for
// the thread. This final allocation must be handled in doFree for this thread and its terminated heap. However,
// this heap has just been put on the heap freelist, and hence there is a race returning the thread-local storage
// and a new thread using this heap. The current thread detects it is executing its last free in doFree via
// heapManager being null. The trick is for this thread to place the last free onto the current heap's remote-list as
// the free-storage header points are this heap. Now, even if other threads are pushing to the remote list, it is safe
// because of the locking.
heapManager = nullptr; // => heap not in use
spin_release( &heapMaster.mgrLock );
} // heapManagerDtor
//####################### Memory Allocation Routines Helpers ####################
NOWARNING( __attribute__(( constructor( 100 ) )) static void startup( void ) {, prio-ctor-dtor ) // singleton => called once at start of program
if ( ! heapMasterBootFlag ) { heapManagerCtor(); } // sanity check
#ifdef __DEBUG__
heapManager->allocUnfreed = 0; // clear prior allocation counts
#endif // __DEBUG__
} // startup
NOWARNING( __attribute__(( destructor( 100 ) )) static void shutdown( void ) {, prio-ctor-dtor ) // singleton => called once at end of program
if ( getenv( "MALLOC_STATS" ) ) { // check for external printing
malloc_stats();
#ifdef __STATISTICS__
char helpText[64];
int len = snprintf( helpText, sizeof(helpText), "\nFree Bucket Usage: (bucket size / allocations / reuses)\n" );
int unused __attribute__(( unused )) = write( STDERR_FILENO, helpText, len ); // file might be closed
size_t th = 0, total = 0;
for ( Heap * heap = heapMaster.heapManagersList; heap; heap = heap->nextHeapManager, th += 1 ) {
enum { Columns = 8 };
len = snprintf( helpText, sizeof(helpText), "Heap %zd\n", th );
unused = write( STDERR_FILENO, helpText, len ); // file might be closed
for ( size_t b = 0, c = 0; b < Heap::NoBucketSizes; b += 1 ) {
if ( heap->freeLists[b].allocations != 0 ) {
total += heap->freeLists[b].blockSize * heap->freeLists[b].allocations;
len = snprintf( helpText, sizeof(helpText), "%zd %zd %zd, ",
heap->freeLists[b].blockSize, heap->freeLists[b].allocations, heap->freeLists[b].reuses );
unused = write( STDERR_FILENO, helpText, len ); // file might be closed
if ( ++c % Columns == 0 )
unused = write( STDERR_FILENO, "\n", 1 ); // file might be closed
} // if
} // for
unused = write( STDERR_FILENO, "\n", 1 ); // file might be closed
} // for
len = snprintf( helpText, sizeof(helpText), "Total bucket storage %zd\n", total );
unused = write( STDERR_FILENO, helpText, len ); // file might be closed
#endif // __STATISTICS__
} // if
#ifdef __DEBUG__
// allocUnfreed is set to 0 when a heap is created and it accumulates any unfreed storage during its multiple thread
// usages. At the end, add up each heap allocUnfreed value across all heaps to get the total unfreed storage.
ptrdiff_t allocUnfreed = heapMaster.allocUnfreed;
LLDEBUG( debugprt( "shutdown1 %jd\n", heapMaster.allocUnfreed ) );
for ( Heap * heap = heapMaster.heapManagersList; heap; heap = heap->nextHeapManager ) {
LLDEBUG( debugprt( "shutdown2 %p %jd\n", heap, heap->allocUnfreed ) );
allocUnfreed += heap->allocUnfreed;
} // for
allocUnfreed -= malloc_unfreed(); // subtract any user specified unfreed storage
LLDEBUG( debugprt( "shutdown3 %td %zd\n", allocUnfreed, malloc_unfreed() ) );
if ( allocUnfreed > 0 ) {
// DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT.
char helpText[BufSize];
int len = snprintf( helpText, sizeof(helpText), "**** Warning **** (UNIX pid:%ld) : program terminating with %td(%#tx) bytes of storage allocated but not freed.\n"
"Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n",
(long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid
int unused __attribute__(( unused )) = write( STDERR_FILENO, helpText, len ); // file might be closed
} // if
#endif // __DEBUG__
} // shutdown
#ifdef __STATISTICS__
#define prtFmt \
"\nPID: %d Heap%s statistics: (storage request / allocation)\n" \
" malloc >0 calls %'llu; 0 calls %'llu; storage %'llu / %'llu bytes\n" \
" aalloc >0 calls %'llu; 0 calls %'llu; storage %'llu / %'llu bytes\n" \
" calloc >0 calls %'llu; 0 calls %'llu; storage %'llu / %'llu bytes\n" \
" memalign >0 calls %'llu; 0 calls %'llu; storage %'llu / %'llu bytes\n" \
" amemalign >0 calls %'llu; 0 calls %'llu; storage %'llu / %'llu bytes\n" \
" cmemalign >0 calls %'llu; 0 calls %'llu; storage %'llu / %'llu bytes\n" \
" resize >0 calls %'llu; 0 calls %'llu; storage %'llu / %'llu bytes\n" \
" realloc >0 calls %'llu; 0 calls %'llu; storage %'llu / %'llu bytes\n" \
" copies %'llu; smaller %'llu; alignment %'llu; 0 fill %'llu\n" \
" free !null calls %'llu; null/0 calls %'llu; storage %'llu / %'llu bytes\n" \
" remote pushes %'llu; pulls %'llu; storage %'llu / %'llu bytes\n" \
" sbrk calls %'llu; storage %'llu bytes\n" \
" mmap calls %'llu; storage %'llu / %'llu bytes\n" \
" munmap calls %'llu; storage %'llu / %'llu bytes\n" \
" remainder calls %'llu; storage %'llu bytes\n" \
" threads started %'llu; exited %'llu\n" \
" heaps new %'llu; reused %'llu\n"
// Use "write" because streams may be shutdown when calls are made.
static int printStats( HeapStatistics & stats, const char * title = "" ) { // see malloc_stats
char helpText[sizeof(prtFmt) + 1024]; // space for message and values
int len = snprintf( helpText, sizeof(helpText), prtFmt, getpid(), title,
stats.malloc_calls, stats.malloc_0_calls, stats.malloc_storage_request, stats.malloc_storage_alloc,
stats.aalloc_calls, stats.aalloc_0_calls, stats.aalloc_storage_request, stats.aalloc_storage_alloc,
stats.calloc_calls, stats.calloc_0_calls, stats.calloc_storage_request, stats.calloc_storage_alloc,
stats.memalign_calls, stats.memalign_0_calls, stats.memalign_storage_request, stats.memalign_storage_alloc,
stats.amemalign_calls, stats.amemalign_0_calls, stats.amemalign_storage_request, stats.amemalign_storage_alloc,
stats.cmemalign_calls, stats.cmemalign_0_calls, stats.cmemalign_storage_request, stats.cmemalign_storage_alloc,
stats.resize_calls, stats.resize_0_calls, stats.resize_storage_request, stats.resize_storage_alloc,
stats.realloc_calls, stats.realloc_0_calls, stats.realloc_storage_request, stats.realloc_storage_alloc,
stats.realloc_copy, stats.realloc_smaller, stats.realloc_align, stats.realloc_0_fill,
stats.free_calls, stats.free_null_0_calls, stats.free_storage_request, stats.free_storage_alloc,
stats.remote_pushes, stats.remote_pulls, stats.remote_storage_request, stats.remote_storage_alloc,
heapMaster.sbrk_calls, heapMaster.sbrk_storage,
stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc,
stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc,
heapMaster.nremainder, heapMaster.remainder,
heapMaster.threads_started, heapMaster.threads_exited,
heapMaster.new_heap, heapMaster.reused_heap
);
return write( heapMaster.stats_fd, helpText, len );
} // printStats
#define prtFmtXML \
"<malloc version=\"1\">\n" \
"<heap nr=\"0\">\n" \
"<sizes>\n" \
"</sizes>\n" \
"<total type=\"malloc\" >0 count=\"%'llu;\" 0 count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"aalloc\" >0 count=\"%'llu;\" 0 count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"calloc\" >0 count=\"%'llu;\" 0 count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"memalign\" >0 count=\"%'llu;\" 0 count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"amemalign\" >0 count=\"%'llu;\" 0 count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"cmemalign\" >0 count=\"%'llu;\" 0 count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"resize\" >0 count=\"%'llu;\" 0 count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"realloc\" >0 count=\"%'llu;\" 0 count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\" \" copy count=\"%'llu;\" smaller count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"free\" !null=\"%'llu;\" 0 null/0=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"remote\" pushes=\"%'llu;\" 0 pulls=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"sbrk\" count=\"%'llu;\" size=\"%'llu\"/> bytes\n" \
"<total type=\"mmap\" count=\"%'llu;\" size=\"%'llu / %'llu\" / > bytes\n" \
"<total type=\"munmap\" count=\"%'llu;\" size=\"%'llu / %'llu\"/> bytes\n" \
"<total type=\"remainder\" count=\"%'llu;\" size=\"%'llu\"/> bytes\n" \
"<total type=\"threads\" started=\"%'llu;\" exited=\"%'llu\"/>\n" \
"<total type=\"heaps\" new=\"%'llu;\" reused=\"%'llu\"/>\n" \
"</malloc>"
static int printStatsXML( HeapStatistics & stats, FILE * stream ) { // see malloc_info
char helpText[sizeof(prtFmtXML) + 1024]; // space for message and values
int len = snprintf( helpText, sizeof(helpText), prtFmtXML,
stats.malloc_calls, stats.malloc_0_calls, stats.malloc_storage_request, stats.malloc_storage_alloc,
stats.aalloc_calls, stats.aalloc_0_calls, stats.aalloc_storage_request, stats.aalloc_storage_alloc,
stats.calloc_calls, stats.calloc_0_calls, stats.calloc_storage_request, stats.calloc_storage_alloc,
stats.memalign_calls, stats.memalign_0_calls, stats.memalign_storage_request, stats.memalign_storage_alloc,
stats.amemalign_calls, stats.amemalign_0_calls, stats.amemalign_storage_request, stats.amemalign_storage_alloc,
stats.cmemalign_calls, stats.cmemalign_0_calls, stats.cmemalign_storage_request, stats.cmemalign_storage_alloc,
stats.resize_calls, stats.resize_0_calls, stats.resize_storage_request, stats.resize_storage_alloc,
stats.realloc_calls, stats.realloc_0_calls, stats.realloc_storage_request, stats.realloc_storage_alloc,
stats.realloc_copy, stats.realloc_smaller, stats.realloc_align, stats.realloc_0_fill,
stats.free_calls, stats.free_null_0_calls, stats.free_storage_request, stats.free_storage_alloc,
stats.remote_pushes, stats.remote_pulls, stats.remote_storage_request, stats.remote_storage_alloc,
heapMaster.sbrk_calls, heapMaster.sbrk_storage,
stats.mmap_calls, stats.mmap_storage_request, stats.mmap_storage_alloc,
stats.munmap_calls, stats.munmap_storage_request, stats.munmap_storage_alloc,
heapMaster.nremainder, heapMaster.remainder,
heapMaster.threads_started, heapMaster.threads_exited,
heapMaster.new_heap, heapMaster.reused_heap
);
return write( fileno( stream ), helpText, len );
} // printStatsXML
static HeapStatistics & collectStats( HeapStatistics & stats ) {
spin_acquire( &heapMaster.mgrLock );
// Accumulate the heap master and all active thread heaps.
stats += heapMaster.stats;
for ( Heap * heap = heapMaster.heapManagersList; heap; heap = heap->nextHeapManager ) {
stats += heap->stats; // calls HeapStatistics +=
} // for
spin_release(&heapMaster.mgrLock);
return stats;
} // collectStats
static void clearStats() {
spin_acquire( &heapMaster.mgrLock );
// Zero the heap master and all active thread heaps.
HeapStatisticsCtor( heapMaster.stats );
for ( Heap * heap = heapMaster.heapManagersList; heap; heap = heap->nextHeapManager ) {
HeapStatisticsCtor( heap->stats );
} // for
spin_release(&heapMaster.mgrLock);
} // clearStats
#endif // __STATISTICS__
extern "C" void noMemory( void ) {
abort( "**** Error **** heap memory exhausted at %zu bytes.\n"
"Possible cause is very large memory allocation and/or large amount of unfreed storage allocated by the program or system/library routines.",
((char *)(sbrk( 0 )) - (char *)(heapMaster.heapBegin)) );
} // noMemory
static inline __attribute__((always_inline)) bool setMmapStart( size_t value ) { // true => mmapped, false => sbrk
if ( value < heapMaster.pageSize || bucketSizes[Heap::NoBucketSizes - 1] < value ) return false;
heapMaster.mmapStart = value; // set global
// find the closest bucket size less than or equal to the mmapStart size
heapMaster.maxBucketsUsed = Bsearchl( heapMaster.mmapStart, bucketSizes, Heap::NoBucketSizes ); // binary search
assert( heapMaster.maxBucketsUsed < Heap::NoBucketSizes ); // subscript failure ?
assert( heapMaster.mmapStart <= bucketSizes[heapMaster.maxBucketsUsed] ); // search failure ?
return true;
} // setMmapStart
// <-------+----------------------------------------------------> bsize (bucket size)
// |header |addr
//==================================================================================
// alignment/offset |
// <-----------------<------------+-----------------------------> bsize (bucket size)
// |fake-header | addr
#define HeaderAddr( addr ) ((Heap::Storage::Header *)( (char *)addr - sizeof(Heap::Storage) ))
#define RealHeader( header ) ((Heap::Storage::Header *)((char *)header - header->kind.fake.offset))
// <-------<<--------------------- dsize ---------------------->> bsize (bucket size)
// |header |addr
//==================================================================================
// alignment/offset |
// <------------------------------<<---------- dsize --------->>> bsize (bucket size)
// |fake-header |addr
#define DataStorage( bsize, addr, header ) (bsize - ( (char *)addr - (char *)header ))
static inline __attribute__((always_inline)) void checkAlign( size_t alignment ) {
if ( UNLIKELY( alignment < __ALIGN__ || ! Pow2( alignment ) ) ) {
abort( "**** Error **** alignment %zu for memory allocation is less than %d and/or not a power of 2.", alignment, __ALIGN__ );
} // if
} // checkAlign
static inline __attribute__((always_inline)) void checkHeader( bool check, const char name[], void * addr ) {
if ( UNLIKELY( check ) ) { // bad address ?
abort( "**** Error **** attempt to %s storage %p with address outside the heap range %p<->%p.\n"
"Possible cause is duplicate free on same block or overwriting of memory.",
name, addr, heapMaster.heapBegin, heapMaster.heapEnd );
} // if