xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
1 /**
2  * Contains the garbage collector implementation.
3  *
4  * Copyright: D Language Foundation 2001 - 2021.
5  * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6  * Authors:   Walter Bright, David Friedman, Sean Kelly
7  */
8 module core.internal.gc.impl.conservative.gc;
9 
10 // D Programming Language Garbage Collector implementation
11 
12 /************** Debugging ***************************/
13 
14 //debug = PRINTF;               // turn on printf's
15 //debug = PARALLEL_PRINTF;      // turn on printf's
16 //debug = COLLECT_PRINTF;       // turn on printf's
17 //debug = MARK_PRINTF;          // turn on printf's
18 //debug = PRINTF_TO_FILE;       // redirect printf's ouptut to file "gcx.log"
19 //debug = LOGGING;              // log allocations / frees
20 //debug = MEMSTOMP;             // stomp on memory
21 //debug = SENTINEL;             // add underrun/overrrun protection
22                                 // NOTE: this needs to be enabled globally in the makefiles
23                                 // (-debug=SENTINEL) to pass druntime's unittests.
24 //debug = PTRCHECK;             // more pointer checking
25 //debug = PTRCHECK2;            // thorough but slow pointer checking
26 //debug = INVARIANT;            // enable invariants
27 //debug = PROFILE_API;          // profile API calls for config.profile > 1
28 //debug = GC_RECURSIVE_LOCK;    // check for recursive locking on the same thread
29 
30 /***************************************************/
31 version = COLLECT_PARALLEL;  // parallel scanning
32 version (Posix)
33     version = COLLECT_FORK;
34 
35 import core.internal.gc.bits;
36 import core.internal.gc.os;
37 import core.gc.config;
38 import core.gc.gcinterface;
39 
40 import core.internal.container.treap;
41 import core.internal.spinlock;
42 import core.internal.gc.pooltable;
43 
44 import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
45 import core.stdc.string : memcpy, memset, memmove;
46 import core.bitop;
47 import core.thread;
48 static import core.memory;
49 
50 version (GNU) import gcc.builtins;
51 
52 debug (PRINTF_TO_FILE) import core.stdc.stdio : sprintf, fprintf, fopen, fflush, FILE;
53 else                   import core.stdc.stdio : sprintf, printf; // needed to output profiling results
54 
55 import core.time;
56 alias currTime = MonoTime.currTime;
57 
58 // Track total time spent preparing for GC,
59 // marking, sweeping and recovering pages.
60 __gshared Duration prepTime;
61 __gshared Duration markTime;
62 __gshared Duration sweepTime;
63 __gshared Duration pauseTime;
64 __gshared Duration maxPauseTime;
65 __gshared Duration maxCollectionTime;
66 __gshared size_t numCollections;
67 __gshared size_t maxPoolMemory;
68 
69 __gshared long numMallocs;
70 __gshared long numFrees;
71 __gshared long numReallocs;
72 __gshared long numExtends;
73 __gshared long numOthers;
74 __gshared long mallocTime; // using ticks instead of MonoTime for better performance
75 __gshared long freeTime;
76 __gshared long reallocTime;
77 __gshared long extendTime;
78 __gshared long otherTime;
79 __gshared long lockTime;
80 
81 ulong bytesAllocated;   // thread local counter
82 
83 private
84 {
85     extern (C)
86     {
87         // to allow compilation of this module without access to the rt package,
88         //  make these functions available from rt.lifetime
89         void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow;
90         int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, const scope void[] segment) nothrow;
91 
92         // Declared as an extern instead of importing core.exception
93         // to avoid inlining - see issue 13725.
94         void onInvalidMemoryOperationError(void* pretend_sideffect = null) @trusted pure nothrow @nogc;
95         void onOutOfMemoryErrorNoGC() @trusted nothrow @nogc;
96 
97         version (COLLECT_FORK)
98             version (OSX)
99                 pid_t __fork() nothrow;
100     }
101 
102     enum
103     {
104         OPFAIL = ~cast(size_t)0
105     }
106 }
107 
108 alias GC gc_t;
109 
110 /* ============================ GC =============================== */
111 
112 // register GC in C constructor (_STI_)
113 extern(C) pragma(crt_constructor) void _d_register_conservative_gc()
114 {
115     import core.gc.registry;
116     registerGCFactory("conservative", &initialize);
117 }
118 
119 extern(C) pragma(crt_constructor) void _d_register_precise_gc()
120 {
121     import core.gc.registry;
122     registerGCFactory("precise", &initialize_precise);
123 }
124 
125 private GC initialize()
126 {
127     import core.lifetime : emplace;
128 
129     auto gc = cast(ConservativeGC) cstdlib.malloc(__traits(classInstanceSize, ConservativeGC));
130     if (!gc)
131         onOutOfMemoryErrorNoGC();
132 
133     return emplace(gc);
134 }
135 
136 private GC initialize_precise()
137 {
138     ConservativeGC.isPrecise = true;
139     return initialize();
140 }
141 
142 class ConservativeGC : GC
143 {
144     // For passing to debug code (not thread safe)
145     __gshared size_t line;
146     __gshared char*  file;
147 
148     Gcx *gcx;                   // implementation
149 
150     static gcLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy);
151     static bool _inFinalizer;
152     __gshared bool isPrecise = false;
153 
154     /*
155      * Lock the GC.
156      *
157      * Throws: InvalidMemoryOperationError on recursive locking during finalization.
158      */
159     static void lockNR() @safe @nogc nothrow
160     {
161         if (_inFinalizer)
162             onInvalidMemoryOperationError();
163         gcLock.lock();
164     }
165 
166     /*
167      * Initialize the GC based on command line configuration.
168      *
169      * Throws:
170      *  OutOfMemoryError if failed to initialize GC due to not enough memory.
171      */
172     this()
173     {
174         //config is assumed to have already been initialized
175 
176         gcx = cast(Gcx*)cstdlib.calloc(1, Gcx.sizeof);
177         if (!gcx)
178             onOutOfMemoryErrorNoGC();
179         gcx.initialize();
180 
181         if (config.initReserve)
182             gcx.reserve(config.initReserve);
183         if (config.disable)
184             gcx.disabled++;
185     }
186 
187 
188     ~this()
189     {
190         version (linux)
191         {
192             //debug(PRINTF) printf("Thread %x ", pthread_self());
193             //debug(PRINTF) printf("GC.Dtor()\n");
194         }
195 
196         if (gcx)
197         {
198             gcx.Dtor();
199             cstdlib.free(gcx);
200             gcx = null;
201         }
202         // TODO: cannot free as memory is overwritten and
203         //  the monitor is still read in rt_finalize (called by destroy)
204         // cstdlib.free(cast(void*) this);
205     }
206 
207 
208     /**
209      * Enables the GC if disable() was previously called. Must be called
210      * for each time disable was called in order to enable the GC again.
211      */
212     void enable()
213     {
214         static void go(Gcx* gcx) nothrow
215         {
216             assert(gcx.disabled > 0);
217             gcx.disabled--;
218         }
219         runLocked!(go, otherTime, numOthers)(gcx);
220     }
221 
222 
223     /**
224      * Disable the GC. The GC may still run if it deems necessary.
225      */
226     void disable()
227     {
228         static void go(Gcx* gcx) nothrow
229         {
230             gcx.disabled++;
231         }
232         runLocked!(go, otherTime, numOthers)(gcx);
233     }
234 
235     debug (GC_RECURSIVE_LOCK) static bool lockedOnThisThread;
236 
237     /**
238      * Run a function inside a lock/unlock set.
239      *
240      * Params:
241      *  func = The function to run.
242      *  args = The function arguments.
243      */
244     auto runLocked(alias func, Args...)(auto ref Args args)
245     {
246         debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0);
247         debug(GC_RECURSIVE_LOCK)
248         {
249             if (lockedOnThisThread)
250                 onInvalidMemoryOperationError();
251             lockedOnThisThread = true;
252         }
253         lockNR();
254         scope (failure) gcLock.unlock();
255         debug(PROFILE_API) immutable tm2 = (config.profile > 1 ? currTime.ticks : 0);
256 
257         static if (is(typeof(func(args)) == void))
258             func(args);
259         else
260             auto res = func(args);
261 
262         debug(PROFILE_API) if (config.profile > 1)
263             lockTime += tm2 - tm;
264         gcLock.unlock();
265         debug(GC_RECURSIVE_LOCK)
266         {
267             if (!lockedOnThisThread)
268                 onInvalidMemoryOperationError();
269             lockedOnThisThread = false;
270         }
271 
272         static if (!is(typeof(func(args)) == void))
273             return res;
274     }
275 
276     /**
277      * Run a function in an lock/unlock set that keeps track of
278      * how much time was spend inside this function (in ticks)
279      * and how many times this fuction was called.
280      *
281      * Params:
282      *  func = The function to run.
283      *  time = The variable keeping track of the time (in ticks).
284      *  count = The variable keeping track of how many times this fuction was called.
285      *  args = The function arguments.
286      */
287     auto runLocked(alias func, alias time, alias count, Args...)(auto ref Args args)
288     {
289         debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0);
290         debug(GC_RECURSIVE_LOCK)
291         {
292             if (lockedOnThisThread)
293                 onInvalidMemoryOperationError();
294             lockedOnThisThread = true;
295         }
296         lockNR();
297         scope (failure) gcLock.unlock();
298         debug(PROFILE_API) immutable tm2 = (config.profile > 1 ? currTime.ticks : 0);
299 
300         static if (is(typeof(func(args)) == void))
301             func(args);
302         else
303             auto res = func(args);
304 
305         debug(PROFILE_API) if (config.profile > 1)
306         {
307             count++;
308             immutable now = currTime.ticks;
309             lockTime += tm2 - tm;
310             time += now - tm2;
311         }
312         gcLock.unlock();
313         debug(GC_RECURSIVE_LOCK)
314         {
315             if (!lockedOnThisThread)
316                 onInvalidMemoryOperationError();
317             lockedOnThisThread = false;
318         }
319 
320         static if (!is(typeof(func(args)) == void))
321             return res;
322     }
323 
324 
325     /**
326      * Returns a bit field representing all block attributes set for the memory
327      * referenced by p.
328      *
329      * Params:
330      *  p = A pointer to the base of a valid memory block or to null.
331      *
332      * Returns:
333      *  A bit field containing any bits set for the memory block referenced by
334      *  p or zero on error.
335      */
336     uint getAttr(void* p) nothrow
337     {
338         if (!p)
339         {
340             return 0;
341         }
342 
343         static uint go(Gcx* gcx, void* p) nothrow
344         {
345             Pool* pool = gcx.findPool(p);
346             uint  oldb = 0;
347 
348             if (pool)
349             {
350                 p = sentinel_sub(p);
351                 if (p != pool.findBase(p))
352                     return 0;
353                 auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
354 
355                 oldb = pool.getBits(biti);
356             }
357             return oldb;
358         }
359 
360         return runLocked!(go, otherTime, numOthers)(gcx, p);
361     }
362 
363     /**
364      * Sets the specified bits for the memory references by p.
365      *
366      * If p was not allocated by the GC, points inside a block, or is null, no
367      * action will be taken.
368      *
369      * Params:
370      *  p = A pointer to the base of a valid memory block or to null.
371      *  mask = A bit field containing any bits to set for this memory block.
372      *
373      * Returns:
374      *  The result of a call to getAttr after the specified bits have been
375      *  set.
376      */
377     uint setAttr(void* p, uint mask) nothrow
378     {
379         if (!p)
380         {
381             return 0;
382         }
383 
384         static uint go(Gcx* gcx, void* p, uint mask) nothrow
385         {
386             Pool* pool = gcx.findPool(p);
387             uint  oldb = 0;
388 
389             if (pool)
390             {
391                 p = sentinel_sub(p);
392                 if (p != pool.findBase(p))
393                     return 0;
394                 auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
395 
396                 oldb = pool.getBits(biti);
397                 pool.setBits(biti, mask);
398             }
399             return oldb;
400         }
401 
402         return runLocked!(go, otherTime, numOthers)(gcx, p, mask);
403     }
404 
405 
406     /**
407      * Clears the specified bits for the memory references by p.
408      *
409      * If p was not allocated by the GC, points inside a block, or is null, no
410      * action will be taken.
411      *
412      * Params:
413      *  p = A pointer to the base of a valid memory block or to null.
414      *  mask = A bit field containing any bits to clear for this memory block.
415      *
416      * Returns:
417      *  The result of a call to getAttr after the specified bits have been
418      *  cleared
419      */
420     uint clrAttr(void* p, uint mask) nothrow
421     {
422         if (!p)
423         {
424             return 0;
425         }
426 
427         static uint go(Gcx* gcx, void* p, uint mask) nothrow
428         {
429             Pool* pool = gcx.findPool(p);
430             uint  oldb = 0;
431 
432             if (pool)
433             {
434                 p = sentinel_sub(p);
435                 if (p != pool.findBase(p))
436                     return 0;
437                 auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
438 
439                 oldb = pool.getBits(biti);
440                 pool.clrBits(biti, mask);
441             }
442             return oldb;
443         }
444 
445         return runLocked!(go, otherTime, numOthers)(gcx, p, mask);
446     }
447 
448     /**
449      * Requests an aligned block of managed memory from the garbage collector.
450      *
451      * Params:
452      *  size = The desired allocation size in bytes.
453      *  bits = A bitmask of the attributes to set on this block.
454      *  ti = TypeInfo to describe the memory.
455      *
456      * Returns:
457      *  A reference to the allocated memory or null if no memory was requested.
458      *
459      * Throws:
460      *  OutOfMemoryError on allocation failure
461      */
462     void *malloc(size_t size, uint bits = 0, const TypeInfo ti = null) nothrow
463     {
464         if (!size)
465         {
466             return null;
467         }
468 
469         size_t localAllocSize = void;
470 
471         auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti);
472 
473         if (!(bits & BlkAttr.NO_SCAN))
474         {
475             memset(p + size, 0, localAllocSize - size);
476         }
477 
478         return p;
479     }
480 
481 
482     //
483     // Implementation for malloc and calloc.
484     //
485     private void *mallocNoSync(size_t size, uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow
486     {
487         assert(size != 0);
488 
489         debug(PRINTF)
490             printf("GC::malloc(gcx = %p, size = %d bits = %x, ti = %s)\n", gcx, size, bits, debugTypeName(ti).ptr);
491 
492         assert(gcx);
493         //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self());
494 
495         auto p = gcx.alloc(size + SENTINEL_EXTRA, alloc_size, bits, ti);
496         if (!p)
497             onOutOfMemoryErrorNoGC();
498 
499         debug (SENTINEL)
500         {
501             p = sentinel_add(p);
502             sentinel_init(p, size);
503             alloc_size = size;
504         }
505         gcx.leakDetector.log_malloc(p, size);
506         bytesAllocated += alloc_size;
507 
508         debug(PRINTF) printf("  => p = %p\n", p);
509         return p;
510     }
511 
512     BlkInfo qalloc( size_t size, uint bits, const scope TypeInfo ti) nothrow
513     {
514 
515         if (!size)
516         {
517             return BlkInfo.init;
518         }
519 
520         BlkInfo retval;
521 
522         retval.base = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, retval.size, ti);
523 
524         if (!(bits & BlkAttr.NO_SCAN))
525         {
526             memset(retval.base + size, 0, retval.size - size);
527         }
528 
529         retval.attr = bits;
530         return retval;
531     }
532 
533 
534     /**
535      * Requests an aligned block of managed memory from the garbage collector,
536      * which is initialized with all bits set to zero.
537      *
538      * Params:
539      *  size = The desired allocation size in bytes.
540      *  bits = A bitmask of the attributes to set on this block.
541      *  ti = TypeInfo to describe the memory.
542      *
543      * Returns:
544      *  A reference to the allocated memory or null if no memory was requested.
545      *
546      * Throws:
547      *  OutOfMemoryError on allocation failure.
548      */
549     void *calloc(size_t size, uint bits = 0, const TypeInfo ti = null) nothrow
550     {
551         if (!size)
552         {
553             return null;
554         }
555 
556         size_t localAllocSize = void;
557 
558         auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti);
559 
560         memset(p, 0, size);
561         if (!(bits & BlkAttr.NO_SCAN))
562         {
563             memset(p + size, 0, localAllocSize - size);
564         }
565 
566         return p;
567     }
568 
569     /**
570      * Request that the GC reallocate a block of memory, attempting to adjust
571      * the size in place if possible. If size is 0, the memory will be freed.
572      *
573      * If p was not allocated by the GC, points inside a block, or is null, no
574      * action will be taken.
575      *
576      * Params:
577      *  p = A pointer to the root of a valid memory block or to null.
578      *  size = The desired allocation size in bytes.
579      *  bits = A bitmask of the attributes to set on this block.
580      *  ti = TypeInfo to describe the memory.
581      *
582      * Returns:
583      *  A reference to the allocated memory on success or null if size is
584      *  zero.
585      *
586      * Throws:
587      *  OutOfMemoryError on allocation failure.
588      */
589     void *realloc(void *p, size_t size, uint bits = 0, const TypeInfo ti = null) nothrow
590     {
591         size_t localAllocSize = void;
592         auto oldp = p;
593 
594         p = runLocked!(reallocNoSync, mallocTime, numMallocs)(p, size, bits, localAllocSize, ti);
595 
596         if (p && !(bits & BlkAttr.NO_SCAN))
597         {
598             memset(p + size, 0, localAllocSize - size);
599         }
600 
601         return p;
602     }
603 
604 
605     //
606     // The implementation of realloc.
607     //
608     // bits will be set to the resulting bits of the new block
609     //
610     private void *reallocNoSync(void *p, size_t size, ref uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow
611     {
612         if (!size)
613         {
614             if (p)
615                 freeNoSync(p);
616             alloc_size = 0;
617             return null;
618         }
619         if (!p)
620             return mallocNoSync(size, bits, alloc_size, ti);
621 
622         debug(PRINTF) printf("GC::realloc(p = %p, size = %llu)\n", p, cast(ulong)size);
623 
624         Pool *pool = gcx.findPool(p);
625         if (!pool)
626             return null;
627 
628         size_t psize;
629         size_t biti;
630 
631         debug(SENTINEL)
632         {
633             void* q = p;
634             p = sentinel_sub(p);
635             bool alwaysMalloc = true;
636         }
637         else
638         {
639             alias q = p;
640             enum alwaysMalloc = false;
641         }
642 
643         void* doMalloc()
644         {
645             if (!bits)
646                 bits = pool.getBits(biti);
647 
648             void* p2 = mallocNoSync(size, bits, alloc_size, ti);
649             debug (SENTINEL)
650                 psize = sentinel_size(q, psize);
651             if (psize < size)
652                 size = psize;
653             //debug(PRINTF) printf("\tcopying %d bytes\n",size);
654             memcpy(p2, q, size);
655             freeNoSync(q);
656             return p2;
657         }
658 
659         if (pool.isLargeObject)
660         {
661             auto lpool = cast(LargeObjectPool*) pool;
662             auto psz = lpool.getPages(p);     // get allocated size
663             if (psz == 0)
664                 return null;      // interior pointer
665             psize = psz * PAGESIZE;
666 
667             alias pagenum = biti; // happens to be the same, but rename for clarity
668             pagenum = lpool.pagenumOf(p);
669 
670             if (size <= PAGESIZE / 2 || alwaysMalloc)
671                 return doMalloc(); // switching from large object pool to small object pool
672 
673             auto newsz = lpool.numPages(size);
674             if (newsz == psz)
675             {
676                 // nothing to do
677             }
678             else if (newsz < psz)
679             {
680                 // Shrink in place
681                 debug (MEMSTOMP) memset(p + size, 0xF2, psize - size);
682                 lpool.freePages(pagenum + newsz, psz - newsz);
683                 lpool.mergeFreePageOffsets!(false, true)(pagenum + newsz, psz - newsz);
684                 lpool.bPageOffsets[pagenum] = cast(uint) newsz;
685             }
686             else if (pagenum + newsz <= pool.npages)
687             {
688                 // Attempt to expand in place (TODO: merge with extend)
689                 if (lpool.pagetable[pagenum + psz] != Bins.B_FREE)
690                     return doMalloc();
691 
692                 auto newPages = newsz - psz;
693                 auto freesz = lpool.bPageOffsets[pagenum + psz];
694                 if (freesz < newPages)
695                     return doMalloc(); // free range too small
696 
697                 debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize);
698                 debug (PRINTF) printFreeInfo(pool);
699                 memset(&lpool.pagetable[pagenum + psz], Bins.B_PAGEPLUS, newPages);
700                 lpool.bPageOffsets[pagenum] = cast(uint) newsz;
701                 for (auto offset = psz; offset < newsz; offset++)
702                     lpool.bPageOffsets[pagenum + offset] = cast(uint) offset;
703                 if (freesz > newPages)
704                     lpool.setFreePageOffsets(pagenum + newsz, freesz - newPages);
705                 gcx.usedLargePages += newPages;
706                 lpool.freepages -= newPages;
707                 debug (PRINTF) printFreeInfo(pool);
708             }
709             else
710                 return doMalloc(); // does not fit into current pool
711 
712             alloc_size = newsz * PAGESIZE;
713         }
714         else
715         {
716             psize = (cast(SmallObjectPool*) pool).getSize(p);   // get allocated bin size
717             if (psize == 0)
718                 return null;    // interior pointer
719             biti = cast(size_t)(p - pool.baseAddr) >> Pool.ShiftBy.Small;
720             if (pool.freebits.test (biti))
721                 return null;
722 
723             // allocate if new size is bigger or less than half
724             if (psize < size || psize > size * 2 || alwaysMalloc)
725                 return doMalloc();
726 
727             alloc_size = psize;
728             if (isPrecise)
729                 pool.setPointerBitmapSmall(p, size, psize, bits, ti);
730         }
731 
732         if (bits)
733         {
734             pool.clrBits(biti, ~BlkAttr.NONE);
735             pool.setBits(biti, bits);
736         }
737         return p;
738     }
739 
740 
741     size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow
742     {
743         return runLocked!(extendNoSync, extendTime, numExtends)(p, minsize, maxsize, ti);
744     }
745 
746 
747     //
748     // Implementation of extend.
749     //
750     private size_t extendNoSync(void* p, size_t minsize, size_t maxsize, const TypeInfo ti = null) nothrow
751     in
752     {
753         assert(minsize <= maxsize);
754     }
755     do
756     {
757         debug(PRINTF) printf("GC::extend(p = %p, minsize = %zu, maxsize = %zu)\n", p, minsize, maxsize);
758         debug (SENTINEL)
759         {
760             return 0;
761         }
762         else
763         {
764             auto pool = gcx.findPool(p);
765             if (!pool || !pool.isLargeObject)
766                 return 0;
767 
768             auto lpool = cast(LargeObjectPool*) pool;
769             size_t pagenum = lpool.pagenumOf(p);
770             if (lpool.pagetable[pagenum] != Bins.B_PAGE)
771                 return 0;
772 
773             size_t psz = lpool.bPageOffsets[pagenum];
774             assert(psz > 0);
775 
776             auto minsz = lpool.numPages(minsize);
777             auto maxsz = lpool.numPages(maxsize);
778 
779             if (pagenum + psz >= lpool.npages)
780                 return 0;
781             if (lpool.pagetable[pagenum + psz] != Bins.B_FREE)
782                 return 0;
783 
784             size_t freesz = lpool.bPageOffsets[pagenum + psz];
785             if (freesz < minsz)
786                 return 0;
787             size_t sz = freesz > maxsz ? maxsz : freesz;
788             debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE);
789             memset(lpool.pagetable + pagenum + psz, Bins.B_PAGEPLUS, sz);
790             lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz);
791             for (auto offset = psz; offset < psz + sz; offset++)
792                 lpool.bPageOffsets[pagenum + offset] = cast(uint) offset;
793             if (freesz > sz)
794                 lpool.setFreePageOffsets(pagenum + psz + sz, freesz - sz);
795             lpool.freepages -= sz;
796             gcx.usedLargePages += sz;
797             return (psz + sz) * PAGESIZE;
798         }
799     }
800 
801 
802     /**
803      * Requests that at least size bytes of memory be obtained from the operating
804      * system and marked as free.
805      *
806      * Params:
807      *  size = The desired size in bytes.
808      *
809      * Returns:
810      *  The actual number of bytes reserved or zero on error.
811      */
812     size_t reserve(size_t size) nothrow
813     {
814         if (!size)
815         {
816             return 0;
817         }
818 
819         return runLocked!(reserveNoSync, otherTime, numOthers)(size);
820     }
821 
822 
823     //
824     // Implementation of reserve
825     //
826     private size_t reserveNoSync(size_t size) nothrow
827     {
828         assert(size != 0);
829         assert(gcx);
830 
831         return gcx.reserve(size);
832     }
833 
834 
835     /**
836      * Deallocates the memory referenced by p.
837      *
838      * If p was not allocated by the GC, points inside a block, is null, or
839      * if free is called from a finalizer, no action will be taken.
840      *
841      * Params:
842      *  p = A pointer to the root of a valid memory block or to null.
843      */
844     void free(void *p) nothrow
845     {
846         if (!p || _inFinalizer)
847         {
848             return;
849         }
850 
851         return runLocked!(freeNoSync, freeTime, numFrees)(p);
852     }
853 
854 
855     //
856     // Implementation of free.
857     //
858     private void freeNoSync(void *p) nothrow @nogc
859     {
860         debug(PRINTF) printf("Freeing %p\n", cast(size_t) p);
861         assert (p);
862 
863         Pool*  pool;
864         size_t pagenum;
865         Bins   bin;
866         size_t biti;
867 
868         // Find which page it is in
869         pool = gcx.findPool(p);
870         if (!pool)                              // if not one of ours
871             return;                             // ignore
872 
873         pagenum = pool.pagenumOf(p);
874 
875         debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]);
876         debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]);
877 
878         bin = pool.pagetable[pagenum];
879 
880         // Verify that the pointer is at the beginning of a block,
881         //  no action should be taken if p is an interior pointer
882         if (bin > Bins.B_PAGE) // B_PAGEPLUS or B_FREE
883             return;
884         size_t off = (sentinel_sub(p) - pool.baseAddr);
885         size_t base = baseOffset(off, bin);
886         if (off != base)
887             return;
888 
889         sentinel_Invariant(p);
890         auto q = p;
891         p = sentinel_sub(p);
892         size_t ssize;
893 
894         if (pool.isLargeObject)              // if large alloc
895         {
896             biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Large;
897             assert(bin == Bins.B_PAGE);
898             auto lpool = cast(LargeObjectPool*) pool;
899 
900             // Free pages
901             size_t npages = lpool.bPageOffsets[pagenum];
902             auto size = npages * PAGESIZE;
903             ssize = sentinel_size(q, size);
904             debug (MEMSTOMP) memset(p, 0xF2, size);
905             lpool.freePages(pagenum, npages);
906             lpool.mergeFreePageOffsets!(true, true)(pagenum, npages);
907         }
908         else
909         {
910             biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Small;
911             if (pool.freebits.test (biti))
912                 return;
913             // Add to free list
914             List *list = cast(List*)p;
915 
916             auto size = binsize[bin];
917             ssize = sentinel_size(q, size);
918             debug (MEMSTOMP) memset(p, 0xF2, size);
919 
920             // in case the page hasn't been recovered yet, don't add the object to the free list
921             if (!gcx.recoverPool[bin] || pool.binPageChain[pagenum] == Pool.PageRecovered)
922             {
923                 list.next = gcx.bucket[bin];
924                 list.pool = pool;
925                 gcx.bucket[bin] = list;
926             }
927             pool.freebits.set(biti);
928         }
929         pool.clrBits(biti, ~BlkAttr.NONE);
930 
931         gcx.leakDetector.log_free(sentinel_add(p), ssize);
932     }
933 
934 
935     /**
936      * Determine the base address of the block containing p.  If p is not a gc
937      * allocated pointer, return null.
938      *
939      * Params:
940      *  p = A pointer to the root or the interior of a valid memory block or to
941      *      null.
942      *
943      * Returns:
944      *  The base address of the memory block referenced by p or null on error.
945      */
946     void* addrOf(void *p) nothrow
947     {
948         if (!p)
949         {
950             return null;
951         }
952 
953         return runLocked!(addrOfNoSync, otherTime, numOthers)(p);
954     }
955 
956 
957     //
958     // Implementation of addrOf.
959     //
960     void* addrOfNoSync(void *p) nothrow @nogc
961     {
962         if (!p)
963         {
964             return null;
965         }
966 
967         auto q = gcx.findBase(p);
968         if (q)
969             q = sentinel_add(q);
970         return q;
971     }
972 
973 
974     /**
975      * Determine the allocated size of pointer p.  If p is an interior pointer
976      * or not a gc allocated pointer, return 0.
977      *
978      * Params:
979      *  p = A pointer to the root of a valid memory block or to null.
980      *
981      * Returns:
982      *  The size in bytes of the memory block referenced by p or zero on error.
983      */
984     size_t sizeOf(void *p) nothrow
985     {
986         if (!p)
987         {
988             return 0;
989         }
990 
991         return runLocked!(sizeOfNoSync, otherTime, numOthers)(p);
992     }
993 
994 
995     //
996     // Implementation of sizeOf.
997     //
998     private size_t sizeOfNoSync(void *p) nothrow @nogc
999     {
1000         assert (p);
1001 
1002         debug (SENTINEL)
1003         {
1004             p = sentinel_sub(p);
1005             size_t size = gcx.findSize(p);
1006             return size ? size - SENTINEL_EXTRA : 0;
1007         }
1008         else
1009         {
1010             size_t size = gcx.findSize(p);
1011             return size;
1012         }
1013     }
1014 
1015 
1016     /**
1017      * Determine the base address of the block containing p.  If p is not a gc
1018      * allocated pointer, return null.
1019      *
1020      * Params:
1021      *  p = A pointer to the root or the interior of a valid memory block or to
1022      *      null.
1023      *
1024      * Returns:
1025      *  Information regarding the memory block referenced by p or BlkInfo.init
1026      *  on error.
1027      */
1028     BlkInfo query(void *p) nothrow
1029     {
1030         if (!p)
1031         {
1032             BlkInfo i;
1033             return  i;
1034         }
1035 
1036         return runLocked!(queryNoSync, otherTime, numOthers)(p);
1037     }
1038 
1039     //
1040     // Implementation of query
1041     //
1042     BlkInfo queryNoSync(void *p) nothrow
1043     {
1044         assert(p);
1045 
1046         BlkInfo info = gcx.getInfo(p);
1047         debug(SENTINEL)
1048         {
1049             if (info.base)
1050             {
1051                 info.base = sentinel_add(info.base);
1052                 info.size = *sentinel_psize(info.base);
1053             }
1054         }
1055         return info;
1056     }
1057 
1058 
1059     /**
1060      * Performs certain checks on a pointer. These checks will cause asserts to
1061      * fail unless the following conditions are met:
1062      *  1) The poiinter belongs to this memory pool.
1063      *  2) The pointer points to the start of an allocated piece of memory.
1064      *  3) The pointer is not on a free list.
1065      *
1066      * Params:
1067      *  p = The pointer to be checked.
1068      */
1069     void check(void *p) nothrow
1070     {
1071         if (!p)
1072         {
1073             return;
1074         }
1075 
1076         return runLocked!(checkNoSync, otherTime, numOthers)(p);
1077     }
1078 
1079 
1080     //
1081     // Implementation of check
1082     //
1083     private void checkNoSync(void *p) nothrow
1084     {
1085         assert(p);
1086 
1087         sentinel_Invariant(p);
1088         debug (PTRCHECK)
1089         {
1090             Pool*  pool;
1091             size_t pagenum;
1092             Bins   bin;
1093 
1094             p = sentinel_sub(p);
1095             pool = gcx.findPool(p);
1096             assert(pool);
1097             pagenum = pool.pagenumOf(p);
1098             bin = pool.pagetable[pagenum];
1099             assert(bin <= Bins.B_PAGE);
1100             assert(p == cast(void*)baseOffset(cast(size_t)p, bin));
1101 
1102             debug (PTRCHECK2)
1103             {
1104                 if (bin < Bins.B_PAGE)
1105                 {
1106                     // Check that p is not on a free list
1107                     List *list;
1108 
1109                     for (list = gcx.bucket[bin]; list; list = list.next)
1110                     {
1111                         assert(cast(void*)list != p);
1112                     }
1113                 }
1114             }
1115         }
1116     }
1117 
1118 
1119     /**
1120      * Add p to list of roots. If p is null, no operation is performed.
1121      *
1122      * Params:
1123      *  p = A pointer into a GC-managed memory block or null.
1124      */
1125     void addRoot(void *p) nothrow @nogc
1126     {
1127         if (!p)
1128         {
1129             return;
1130         }
1131 
1132         gcx.addRoot(p);
1133     }
1134 
1135 
1136     /**
1137      * Remove p from list of roots. If p is null or is not a value
1138      * previously passed to addRoot() then no operation is performed.
1139      *
1140      * Params:
1141      *  p = A pointer into a GC-managed memory block or null.
1142      */
1143     void removeRoot(void *p) nothrow @nogc
1144     {
1145         if (!p)
1146         {
1147             return;
1148         }
1149 
1150         gcx.removeRoot(p);
1151     }
1152 
1153     /**
1154      * Returns an iterator allowing roots to be traversed via a foreach loop.
1155      */
1156     @property RootIterator rootIter() @nogc
1157     {
1158         return &gcx.rootsApply;
1159     }
1160 
1161 
1162     /**
1163      * Add range to scan for roots. If p is null or sz is 0, no operation is performed.
1164      *
1165      * Params:
1166      *  p  = A pointer to a valid memory address or to null.
1167      *  sz = The size in bytes of the block to add.
1168      *  ti = TypeInfo to describe the memory.
1169      */
1170     void addRange(void *p, size_t sz, const TypeInfo ti = null) nothrow @nogc
1171     {
1172         if (!p || !sz)
1173         {
1174             return;
1175         }
1176 
1177         gcx.addRange(p, p + sz, ti);
1178     }
1179 
1180 
1181     /**
1182      * Remove range from list of ranges. If p is null or does not represent
1183      * a value previously passed to addRange() then no operation is
1184      * performed.
1185      *
1186      * Params:
1187      *  p  = A pointer to a valid memory address or to null.
1188      */
1189     void removeRange(void *p) nothrow @nogc
1190     {
1191         if (!p)
1192         {
1193             return;
1194         }
1195 
1196         gcx.removeRange(p);
1197     }
1198 
1199 
1200     /**
1201      * Returns an iterator allowing ranges to be traversed via a foreach loop.
1202      */
1203     @property RangeIterator rangeIter() @nogc
1204     {
1205         return &gcx.rangesApply;
1206     }
1207 
1208 
1209     /**
1210      * Run all finalizers in the code segment.
1211      *
1212      * Params:
1213      *  segment = address range of a code segment
1214      */
1215     void runFinalizers(const scope void[] segment) nothrow
1216     {
1217         static void go(Gcx* gcx, const scope void[] segment) nothrow
1218         {
1219             gcx.runFinalizers(segment);
1220         }
1221         return runLocked!(go, otherTime, numOthers)(gcx, segment);
1222     }
1223 
1224 
1225     bool inFinalizer() nothrow @nogc
1226     {
1227         return _inFinalizer;
1228     }
1229 
1230 
1231     void collect() nothrow
1232     {
1233         fullCollect();
1234     }
1235 
1236 
1237     void collectNoStack() nothrow
1238     {
1239         fullCollectNoStack();
1240     }
1241 
1242 
1243     /**
1244      * Begins a full collection, scanning all stack segments for roots.
1245      *
1246      * Returns:
1247      *  The number of pages freed.
1248      */
1249     size_t fullCollect() nothrow
1250     {
1251         debug(PRINTF) printf("GC.fullCollect()\n");
1252 
1253         // Since a finalizer could launch a new thread, we always need to lock
1254         // when collecting.
1255         static size_t go(Gcx* gcx) nothrow
1256         {
1257             return gcx.fullcollect(false, true); // standard stop the world
1258         }
1259         immutable result = runLocked!go(gcx);
1260 
1261         version (none)
1262         {
1263             GCStats stats;
1264 
1265             getStats(stats);
1266             debug(PRINTF) printf("heapSize = %zx, freeSize = %zx\n",
1267                 stats.heapSize, stats.freeSize);
1268         }
1269 
1270         gcx.leakDetector.log_collect();
1271         return result;
1272     }
1273 
1274 
1275     /**
1276      * Begins a full collection while ignoring all stack segments for roots.
1277      */
1278     void fullCollectNoStack() nothrow
1279     {
1280         // Since a finalizer could launch a new thread, we always need to lock
1281         // when collecting.
1282         static size_t go(Gcx* gcx) nothrow
1283         {
1284             return gcx.fullcollect(true, true, true); // standard stop the world
1285         }
1286         runLocked!go(gcx);
1287     }
1288 
1289 
1290     /**
1291      * Minimize free space usage.
1292      */
1293     void minimize() nothrow
1294     {
1295         static void go(Gcx* gcx) nothrow
1296         {
1297             gcx.minimize();
1298         }
1299         runLocked!(go, otherTime, numOthers)(gcx);
1300     }
1301 
1302 
1303     core.memory.GC.Stats stats() @safe nothrow @nogc
1304     {
1305         typeof(return) ret;
1306 
1307         runLocked!(getStatsNoSync, otherTime, numOthers)(ret);
1308 
1309         return ret;
1310     }
1311 
1312 
1313     core.memory.GC.ProfileStats profileStats() nothrow @trusted
1314     {
1315         typeof(return) ret;
1316 
1317         ret.numCollections = numCollections;
1318         ret.totalCollectionTime = prepTime + markTime + sweepTime;
1319         ret.totalPauseTime = pauseTime;
1320         ret.maxCollectionTime = maxCollectionTime;
1321         ret.maxPauseTime = maxPauseTime;
1322 
1323         return ret;
1324     }
1325 
1326 
1327     ulong allocatedInCurrentThread() nothrow
1328     {
1329         return bytesAllocated;
1330     }
1331 
1332 
1333     //
1334     // Implementation of getStats
1335     //
1336     private void getStatsNoSync(out core.memory.GC.Stats stats) @trusted nothrow @nogc
1337     {
1338         // This function is trusted for two reasons: `pool.pagetable` is a pointer,
1339         // which is being sliced in the below foreach, and so is `binPageChain`,
1340         // also sliced a bit later in this function.
1341         // However, both usages are safe as long as the assumption that `npools`
1342         // defines the limit for `pagetable`'s length holds true (see allocation).
1343         // The slicing happens at __LINE__ + 4 and __LINE__ + 24.
1344         // `@trusted` delegates are not used to prevent any performance issue.
1345         foreach (pool; gcx.pooltable[])
1346         {
1347             foreach (bin; pool.pagetable[0 .. pool.npages])
1348             {
1349                 if (bin == Bins.B_FREE)
1350                     stats.freeSize += PAGESIZE;
1351                 else
1352                     stats.usedSize += PAGESIZE;
1353             }
1354         }
1355 
1356         size_t freeListSize;
1357         foreach (n; 0 .. Bins.B_PAGE)
1358         {
1359             immutable sz = binsize[n];
1360             for (List *list = gcx.bucket[n]; list; list = list.next)
1361                 freeListSize += sz;
1362 
1363             foreach (pool; gcx.pooltable[])
1364             {
1365                 if (pool.isLargeObject)
1366                     continue;
1367                 for (uint pn = pool.recoverPageFirst[n]; pn < pool.npages; pn = pool.binPageChain[pn])
1368                 {
1369                     const bitbase = pn * PAGESIZE / 16;
1370                     const top = PAGESIZE - sz + 1; // ensure <size> bytes available even if unaligned
1371                     for (size_t u = 0; u < top; u += sz)
1372                         if (pool.freebits.test(bitbase + u / 16))
1373                             freeListSize += sz;
1374                 }
1375             }
1376         }
1377 
1378         stats.usedSize -= freeListSize;
1379         stats.freeSize += freeListSize;
1380         stats.allocatedInCurrentThread = bytesAllocated;
1381     }
1382 }
1383 
1384 
1385 /* ============================ Gcx =============================== */
1386 
1387 enum
1388 {   PAGESIZE =    4096,
1389 }
1390 
1391 
1392 enum Bins : ubyte
1393 {
1394     B_16,
1395     B_32,
1396     B_48,
1397     B_64,
1398     B_96,
1399     B_128,
1400     B_176,
1401     B_256,
1402     B_368,
1403     B_512,
1404     B_816,
1405     B_1024,
1406     B_1360,
1407     B_2048,
1408     B_NUMSMALL,
1409 
1410     B_PAGE = B_NUMSMALL,// start of large alloc
1411     B_PAGEPLUS,         // continuation of large alloc
1412     B_FREE,             // free page
1413     B_MAX,
1414 }
1415 
1416 struct List
1417 {
1418     List *next;
1419     Pool *pool;
1420 }
1421 
1422 // non power of two sizes optimized for small remainder within page (<= 64 bytes)
1423 immutable short[Bins.B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ];
1424 immutable short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] binbase = calcBinBase();
1425 
1426 short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] calcBinBase()
1427 {
1428     short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] bin;
1429 
1430     foreach (i, size; binsize)
1431     {
1432         short end = (PAGESIZE / size) * size;
1433         short bsz = size / 16;
1434         foreach (off; 0..PAGESIZE/16)
1435         {
1436             // add the remainder to the last bin, so no check during scanning
1437             //  is needed if a false pointer targets that area
1438             const base = (off - off % bsz) * 16;
1439             bin[i][off] = cast(short)(base < end ? base : end - size);
1440         }
1441     }
1442     return bin;
1443 }
1444 
1445 size_t baseOffset(size_t offset, Bins bin) @nogc nothrow
1446 {
1447     assert(bin <= Bins.B_PAGE);
1448     return (offset & ~(PAGESIZE - 1)) + binbase[bin][(offset & (PAGESIZE - 1)) >> 4];
1449 }
1450 
1451 alias PageBits = GCBits.wordtype[PAGESIZE / 16 / GCBits.BITS_PER_WORD];
1452 static assert(PAGESIZE % (GCBits.BITS_PER_WORD * 16) == 0);
1453 
1454 // bitmask with bits set at base offsets of objects
1455 immutable PageBits[Bins.B_NUMSMALL] baseOffsetBits = (){
1456     PageBits[Bins.B_NUMSMALL] bits;
1457     foreach (bin; 0 .. Bins.B_NUMSMALL)
1458     {
1459         size_t size = binsize[bin];
1460         const top = PAGESIZE - size + 1; // ensure <size> bytes available even if unaligned
1461         for (size_t u = 0; u < top; u += size)
1462         {
1463             size_t biti = u / 16;
1464             size_t off = biti / GCBits.BITS_PER_WORD;
1465             size_t mod = biti % GCBits.BITS_PER_WORD;
1466             bits[bin][off] |= GCBits.BITS_1 << mod;
1467         }
1468     }
1469     return bits;
1470 }();
1471 
1472 private void set(ref PageBits bits, size_t i) @nogc pure nothrow
1473 {
1474     assert(i < PageBits.sizeof * 8);
1475     bts(bits.ptr, i);
1476 }
1477 
1478 /* ============================ Gcx =============================== */
1479 
1480 struct Gcx
1481 {
1482     auto rootsLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
1483     auto rangesLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
1484     Treap!Root roots;
1485     Treap!Range ranges;
1486     bool minimizeAfterNextCollection = false;
1487     version (COLLECT_FORK)
1488     {
1489         private pid_t markProcPid = 0;
1490         bool shouldFork;
1491     }
1492 
1493     debug(INVARIANT) bool initialized;
1494     debug(INVARIANT) bool inCollection;
1495     uint disabled; // turn off collections if >0
1496 
1497     PoolTable!Pool pooltable;
1498 
1499     List*[Bins.B_NUMSMALL] bucket; // free list for each small size
1500 
1501     // run a collection when reaching those thresholds (number of used pages)
1502     float smallCollectThreshold, largeCollectThreshold;
1503     uint usedSmallPages, usedLargePages;
1504     // total number of mapped pages
1505     uint mappedPages;
1506 
1507     debug (LOGGING)
1508         LeakDetector leakDetector;
1509     else
1510         alias leakDetector = LeakDetector;
1511 
1512     SmallObjectPool*[Bins.B_NUMSMALL] recoverPool;
1513     version (Posix) __gshared Gcx* instance;
1514 
1515     void initialize()
1516     {
1517         (cast(byte*)&this)[0 .. Gcx.sizeof] = 0;
1518         leakDetector.initialize(&this);
1519         roots.initialize(0x243F6A8885A308D3UL);
1520         ranges.initialize(0x13198A2E03707344UL);
1521         smallCollectThreshold = largeCollectThreshold = 0.0f;
1522         usedSmallPages = usedLargePages = 0;
1523         mappedPages = 0;
1524         //printf("gcx = %p, self = %x\n", &this, self);
1525         version (Posix)
1526         {
1527             import core.sys.posix.pthread : pthread_atfork;
1528             instance = &this;
1529             __gshared atforkHandlersInstalled = false;
1530             if (!atforkHandlersInstalled)
1531             {
1532                 pthread_atfork(
1533                     &_d_gcx_atfork_prepare,
1534                     &_d_gcx_atfork_parent,
1535                     &_d_gcx_atfork_child);
1536                 atforkHandlersInstalled = true;
1537             }
1538         }
1539         debug(INVARIANT) initialized = true;
1540         version (COLLECT_FORK)
1541             shouldFork = config.fork;
1542 
1543     }
1544 
1545     void Dtor()
1546     {
1547         if (config.profile)
1548         {
1549             printf("\tNumber of collections:  %llu\n", cast(ulong)numCollections);
1550             printf("\tTotal GC prep time:  %lld milliseconds\n",
1551                    prepTime.total!("msecs"));
1552             printf("\tTotal mark time:  %lld milliseconds\n",
1553                    markTime.total!("msecs"));
1554             printf("\tTotal sweep time:  %lld milliseconds\n",
1555                    sweepTime.total!("msecs"));
1556             long maxPause = maxPauseTime.total!("msecs");
1557             printf("\tMax Pause Time:  %lld milliseconds\n", maxPause);
1558             long gcTime = (sweepTime + markTime + prepTime).total!("msecs");
1559             printf("\tGrand total GC time:  %lld milliseconds\n", gcTime);
1560             long pauseTime = (markTime + prepTime).total!("msecs");
1561 
1562             char[30] apitxt = void;
1563             apitxt[0] = 0;
1564             debug(PROFILE_API) if (config.profile > 1)
1565             {
1566                 static Duration toDuration(long dur)
1567                 {
1568                     return MonoTime(dur) - MonoTime(0);
1569                 }
1570 
1571                 printf("\n");
1572                 printf("\tmalloc:  %llu calls, %lld ms\n", cast(ulong)numMallocs, toDuration(mallocTime).total!"msecs");
1573                 printf("\trealloc: %llu calls, %lld ms\n", cast(ulong)numReallocs, toDuration(reallocTime).total!"msecs");
1574                 printf("\tfree:    %llu calls, %lld ms\n", cast(ulong)numFrees, toDuration(freeTime).total!"msecs");
1575                 printf("\textend:  %llu calls, %lld ms\n", cast(ulong)numExtends, toDuration(extendTime).total!"msecs");
1576                 printf("\tother:   %llu calls, %lld ms\n", cast(ulong)numOthers, toDuration(otherTime).total!"msecs");
1577                 printf("\tlock time: %lld ms\n", toDuration(lockTime).total!"msecs");
1578 
1579                 long apiTime = mallocTime + reallocTime + freeTime + extendTime + otherTime + lockTime;
1580                 printf("\tGC API: %lld ms\n", toDuration(apiTime).total!"msecs");
1581                 sprintf(apitxt.ptr, " API%5ld ms", toDuration(apiTime).total!"msecs");
1582             }
1583 
1584             printf("GC summary:%5lld MB,%5lld GC%5lld ms, Pauses%5lld ms <%5lld ms%s\n",
1585                    cast(long) maxPoolMemory >> 20, cast(ulong)numCollections, gcTime,
1586                    pauseTime, maxPause, apitxt.ptr);
1587         }
1588 
1589         version (Posix)
1590             instance = null;
1591         version (COLLECT_PARALLEL)
1592             stopScanThreads();
1593 
1594         debug(INVARIANT) initialized = false;
1595 
1596         foreach (Pool* pool; this.pooltable[])
1597         {
1598             mappedPages -= pool.npages;
1599             pool.Dtor();
1600             cstdlib.free(pool);
1601         }
1602         assert(!mappedPages);
1603         pooltable.Dtor();
1604 
1605         roots.removeAll();
1606         ranges.removeAll();
1607         toscanConservative.reset();
1608         toscanPrecise.reset();
1609     }
1610 
1611 
1612     void Invariant() const { }
1613 
1614     debug(INVARIANT)
1615     invariant()
1616     {
1617         if (initialized)
1618         {
1619             //printf("Gcx.invariant(): this = %p\n", &this);
1620             pooltable.Invariant();
1621             for (size_t p = 0; p < pooltable.length; p++)
1622                 if (pooltable.pools[p].isLargeObject)
1623                     (cast(LargeObjectPool*)(pooltable.pools[p])).Invariant();
1624                 else
1625                     (cast(SmallObjectPool*)(pooltable.pools[p])).Invariant();
1626 
1627             if (!inCollection)
1628                 (cast()rangesLock).lock();
1629             foreach (range; ranges)
1630             {
1631                 assert(range.pbot);
1632                 assert(range.ptop);
1633                 assert(range.pbot <= range.ptop);
1634             }
1635             if (!inCollection)
1636                 (cast()rangesLock).unlock();
1637 
1638             for (size_t i = 0; i < Bins.B_NUMSMALL; i++)
1639             {
1640                 size_t j = 0;
1641                 List* prev, pprev, ppprev; // keep a short history to inspect in the debugger
1642                 for (auto list = cast(List*)bucket[i]; list; list = list.next)
1643                 {
1644                     auto pool = list.pool;
1645                     auto biti = cast(size_t)(cast(void*)list - pool.baseAddr) >> Pool.ShiftBy.Small;
1646                     assert(pool.freebits.test(biti));
1647                     ppprev = pprev;
1648                     pprev = prev;
1649                     prev = list;
1650                 }
1651             }
1652         }
1653     }
1654 
1655     @property bool collectInProgress() const nothrow
1656     {
1657         version (COLLECT_FORK)
1658             return markProcPid != 0;
1659         else
1660             return false;
1661     }
1662 
1663 
1664     /**
1665      *
1666      */
1667     void addRoot(void *p) nothrow @nogc
1668     {
1669         rootsLock.lock();
1670         scope (failure) rootsLock.unlock();
1671         roots.insert(Root(p));
1672         rootsLock.unlock();
1673     }
1674 
1675 
1676     /**
1677      *
1678      */
1679     void removeRoot(void *p) nothrow @nogc
1680     {
1681         rootsLock.lock();
1682         scope (failure) rootsLock.unlock();
1683         roots.remove(Root(p));
1684         rootsLock.unlock();
1685     }
1686 
1687 
1688     /**
1689      *
1690      */
1691     int rootsApply(scope int delegate(ref Root) nothrow dg) nothrow
1692     {
1693         rootsLock.lock();
1694         scope (failure) rootsLock.unlock();
1695         auto ret = roots.opApply(dg);
1696         rootsLock.unlock();
1697         return ret;
1698     }
1699 
1700 
1701     /**
1702      *
1703      */
1704     void addRange(void *pbot, void *ptop, const TypeInfo ti) nothrow @nogc
1705     {
1706         //debug(PRINTF) printf("Thread %x ", pthread_self());
1707         debug(PRINTF) printf("%p.Gcx::addRange(%p, %p)\n", &this, pbot, ptop);
1708         rangesLock.lock();
1709         scope (failure) rangesLock.unlock();
1710         ranges.insert(Range(pbot, ptop));
1711         rangesLock.unlock();
1712     }
1713 
1714 
1715     /**
1716      *
1717      */
1718     void removeRange(void *pbot) nothrow @nogc
1719     {
1720         //debug(PRINTF) printf("Thread %x ", pthread_self());
1721         debug(PRINTF) printf("Gcx.removeRange(%p)\n", pbot);
1722         rangesLock.lock();
1723         scope (failure) rangesLock.unlock();
1724         ranges.remove(Range(pbot, pbot)); // only pbot is used, see Range.opCmp
1725         rangesLock.unlock();
1726 
1727         // debug(PRINTF) printf("Wrong thread\n");
1728         // This is a fatal error, but ignore it.
1729         // The problem is that we can get a Close() call on a thread
1730         // other than the one the range was allocated on.
1731         //assert(zero);
1732     }
1733 
1734     /**
1735      *
1736      */
1737     int rangesApply(scope int delegate(ref Range) nothrow dg) nothrow
1738     {
1739         rangesLock.lock();
1740         scope (failure) rangesLock.unlock();
1741         auto ret = ranges.opApply(dg);
1742         rangesLock.unlock();
1743         return ret;
1744     }
1745 
1746 
1747     /**
1748      *
1749      */
1750     void runFinalizers(const scope void[] segment) nothrow
1751     {
1752         ConservativeGC._inFinalizer = true;
1753         scope (failure) ConservativeGC._inFinalizer = false;
1754 
1755         foreach (pool; this.pooltable[])
1756         {
1757             if (!pool.finals.nbits) continue;
1758 
1759             if (pool.isLargeObject)
1760             {
1761                 auto lpool = cast(LargeObjectPool*) pool;
1762                 lpool.runFinalizers(segment);
1763             }
1764             else
1765             {
1766                 auto spool = cast(SmallObjectPool*) pool;
1767                 spool.runFinalizers(segment);
1768             }
1769         }
1770         ConservativeGC._inFinalizer = false;
1771     }
1772 
1773     Pool* findPool(void* p) pure nothrow @nogc
1774     {
1775         return pooltable.findPool(p);
1776     }
1777 
1778     /**
1779      * Find base address of block containing pointer p.
1780      * Returns null if not a gc'd pointer
1781      */
1782     void* findBase(void *p) nothrow @nogc
1783     {
1784         Pool *pool;
1785 
1786         pool = findPool(p);
1787         if (pool)
1788             return pool.findBase(p);
1789         return null;
1790     }
1791 
1792 
1793     /**
1794      * Find size of pointer p.
1795      * Returns 0 if not a gc'd pointer
1796      */
1797     size_t findSize(void *p) nothrow @nogc
1798     {
1799         Pool* pool = findPool(p);
1800         if (pool)
1801             return pool.slGetSize(p);
1802         return 0;
1803     }
1804 
1805     /**
1806      *
1807      */
1808     BlkInfo getInfo(void* p) nothrow
1809     {
1810         Pool* pool = findPool(p);
1811         if (pool)
1812             return pool.slGetInfo(p);
1813         return BlkInfo();
1814     }
1815 
1816     /**
1817      * Computes the bin table using CTFE.
1818      */
1819     static Bins[2049] ctfeBins() nothrow
1820     {
1821         Bins[2049] ret;
1822         size_t p = 0;
1823         for (Bins b = Bins.B_16; b <= Bins.B_2048; b++)
1824             for ( ; p <= binsize[b]; p++)
1825                 ret[p] = b;
1826 
1827         return ret;
1828     }
1829 
1830     static immutable Bins[2049] binTable = ctfeBins();
1831 
1832     /**
1833      * Allocate a new pool of at least size bytes.
1834      * Sort it into pooltable[].
1835      * Mark all memory in the pool as B_FREE.
1836      * Return the actual number of bytes reserved or 0 on error.
1837      */
1838     size_t reserve(size_t size) nothrow
1839     {
1840         size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
1841 
1842         // Assume reserve() is for small objects.
1843         Pool*  pool = newPool(npages, false);
1844 
1845         if (!pool)
1846             return 0;
1847         return pool.npages * PAGESIZE;
1848     }
1849 
1850     /**
1851      * Update the thresholds for when to collect the next time
1852      */
1853     void updateCollectThresholds() nothrow
1854     {
1855         static float max(float a, float b) nothrow
1856         {
1857             return a >= b ? a : b;
1858         }
1859 
1860         // instantly increases, slowly decreases
1861         static float smoothDecay(float oldVal, float newVal) nothrow
1862         {
1863             // decay to 63.2% of newVal over 5 collections
1864             // http://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
1865             enum alpha = 1.0 / (5 + 1);
1866             immutable decay = (newVal - oldVal) * alpha + oldVal;
1867             return max(newVal, decay);
1868         }
1869 
1870         immutable smTarget = usedSmallPages * config.heapSizeFactor;
1871         smallCollectThreshold = smoothDecay(smallCollectThreshold, smTarget);
1872         immutable lgTarget = usedLargePages * config.heapSizeFactor;
1873         largeCollectThreshold = smoothDecay(largeCollectThreshold, lgTarget);
1874     }
1875 
1876     /**
1877      * Minimizes physical memory usage by returning free pools to the OS.
1878      */
1879     void minimize() nothrow
1880     {
1881         debug(PRINTF) printf("Minimizing.\n");
1882 
1883         foreach (pool; pooltable.minimize())
1884         {
1885             debug(PRINTF) printFreeInfo(pool);
1886             mappedPages -= pool.npages;
1887             pool.Dtor();
1888             cstdlib.free(pool);
1889         }
1890 
1891         debug(PRINTF) printf("Done minimizing.\n");
1892     }
1893 
1894     private @property bool lowMem() const nothrow
1895     {
1896         return isLowOnMem(cast(size_t)mappedPages * PAGESIZE);
1897     }
1898 
1899     void* alloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti) nothrow
1900     {
1901         return size <= PAGESIZE/2 ? smallAlloc(size, alloc_size, bits, ti)
1902                                   : bigAlloc(size, alloc_size, bits, ti);
1903     }
1904 
1905     void* smallAlloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti) nothrow
1906     {
1907         immutable bin = binTable[size];
1908         alloc_size = binsize[bin];
1909 
1910         void* p = bucket[bin];
1911         if (p)
1912             goto L_hasBin;
1913 
1914         if (recoverPool[bin])
1915             recoverNextPage(bin);
1916 
1917         bool tryAlloc() nothrow
1918         {
1919             if (!bucket[bin])
1920             {
1921                 bucket[bin] = allocPage(bin);
1922                 if (!bucket[bin])
1923                     return false;
1924             }
1925             p = bucket[bin];
1926             return true;
1927         }
1928 
1929         if (!tryAlloc())
1930         {
1931             if (!lowMem && (disabled || usedSmallPages < smallCollectThreshold))
1932             {
1933                 // disabled or threshold not reached => allocate a new pool instead of collecting
1934                 if (!newPool(1, false))
1935                 {
1936                     // out of memory => try to free some memory
1937                     fullcollect(false, true); // stop the world
1938                     if (lowMem)
1939                         minimize();
1940                     recoverNextPage(bin);
1941                 }
1942             }
1943             else if (usedSmallPages > 0)
1944             {
1945                 fullcollect();
1946                 if (lowMem)
1947                     minimize();
1948                 recoverNextPage(bin);
1949             }
1950             // tryAlloc will succeed if a new pool was allocated above, if it fails allocate a new pool now
1951             if (!tryAlloc() && (!newPool(1, false) || !tryAlloc()))
1952                 // out of luck or memory
1953                 onOutOfMemoryErrorNoGC();
1954         }
1955         assert(p !is null);
1956     L_hasBin:
1957         // Return next item from free list
1958         bucket[bin] = (cast(List*)p).next;
1959         auto pool = (cast(List*)p).pool;
1960 
1961         auto biti = (p - pool.baseAddr) >> pool.shiftBy;
1962         assert(pool.freebits.test(biti));
1963         if (collectInProgress)
1964             pool.mark.setLocked(biti); // be sure that the child is aware of the page being used
1965         pool.freebits.clear(biti);
1966         if (bits)
1967             pool.setBits(biti, bits);
1968         //debug(PRINTF) printf("\tmalloc => %p\n", p);
1969         debug (MEMSTOMP) memset(p, 0xF0, alloc_size);
1970 
1971         if (ConservativeGC.isPrecise)
1972         {
1973             debug(SENTINEL)
1974                 pool.setPointerBitmapSmall(sentinel_add(p), size - SENTINEL_EXTRA, size - SENTINEL_EXTRA, bits, ti);
1975             else
1976                 pool.setPointerBitmapSmall(p, size, alloc_size, bits, ti);
1977         }
1978         return p;
1979     }
1980 
1981     /**
1982      * Allocate a chunk of memory that is larger than a page.
1983      * Return null if out of memory.
1984      */
1985     void* bigAlloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti = null) nothrow
1986     {
1987         debug(PRINTF) printf("In bigAlloc.  Size:  %d\n", size);
1988 
1989         LargeObjectPool* pool;
1990         size_t pn;
1991         immutable npages = LargeObjectPool.numPages(size);
1992         if (npages == size_t.max)
1993             onOutOfMemoryErrorNoGC(); // size just below size_t.max requested
1994 
1995         bool tryAlloc() nothrow
1996         {
1997             foreach (p; this.pooltable[])
1998             {
1999                 if (!p.isLargeObject || p.freepages < npages)
2000                     continue;
2001                 auto lpool = cast(LargeObjectPool*) p;
2002                 if ((pn = lpool.allocPages(npages)) == OPFAIL)
2003                     continue;
2004                 pool = lpool;
2005                 return true;
2006             }
2007             return false;
2008         }
2009 
2010         bool tryAllocNewPool() nothrow
2011         {
2012             pool = cast(LargeObjectPool*) newPool(npages, true);
2013             if (!pool) return false;
2014             pn = pool.allocPages(npages);
2015             assert(pn != OPFAIL);
2016             return true;
2017         }
2018 
2019         if (!tryAlloc())
2020         {
2021             if (!lowMem && (disabled || usedLargePages < largeCollectThreshold))
2022             {
2023                 // disabled or threshold not reached => allocate a new pool instead of collecting
2024                 if (!tryAllocNewPool())
2025                 {
2026                     // disabled but out of memory => try to free some memory
2027                     minimizeAfterNextCollection = true;
2028                     fullcollect(false, true);
2029                 }
2030             }
2031             else if (usedLargePages > 0)
2032             {
2033                 minimizeAfterNextCollection = true;
2034                 fullcollect();
2035             }
2036             // If alloc didn't yet succeed retry now that we collected/minimized
2037             if (!pool && !tryAlloc() && !tryAllocNewPool())
2038                 // out of luck or memory
2039                 return null;
2040         }
2041         assert(pool);
2042 
2043         debug(PRINTF) printFreeInfo(&pool.base);
2044         if (collectInProgress)
2045             pool.mark.setLocked(pn);
2046         usedLargePages += npages;
2047 
2048         debug(PRINTF) printFreeInfo(&pool.base);
2049 
2050         auto p = pool.baseAddr + pn * PAGESIZE;
2051         debug(PRINTF) printf("Got large alloc:  %p, pt = %d, np = %d\n", p, pool.pagetable[pn], npages);
2052         debug (MEMSTOMP) memset(p, 0xF1, size);
2053         alloc_size = npages * PAGESIZE;
2054         //debug(PRINTF) printf("\tp = %p\n", p);
2055 
2056         if (bits)
2057             pool.setBits(pn, bits);
2058 
2059         if (ConservativeGC.isPrecise)
2060         {
2061             // an array of classes is in fact an array of pointers
2062             immutable(void)* rtinfo;
2063             if (!ti)
2064                 rtinfo = rtinfoHasPointers;
2065             else if ((bits & BlkAttr.APPENDABLE) && (typeid(ti) is typeid(TypeInfo_Class)))
2066                 rtinfo = rtinfoHasPointers;
2067             else
2068                 rtinfo = ti.rtInfo();
2069             pool.rtinfo[pn] = cast(immutable(size_t)*)rtinfo;
2070         }
2071 
2072         return p;
2073     }
2074 
2075 
2076     /**
2077      * Allocate a new pool with at least npages in it.
2078      * Sort it into pooltable[].
2079      * Return null if failed.
2080      */
2081     Pool *newPool(size_t npages, bool isLargeObject) nothrow
2082     {
2083         //debug(PRINTF) printf("************Gcx::newPool(npages = %d)****************\n", npages);
2084 
2085         // Minimum of POOLSIZE
2086         size_t minPages = config.minPoolSize / PAGESIZE;
2087         if (npages < minPages)
2088             npages = minPages;
2089         else if (npages > minPages)
2090         {   // Give us 150% of requested size, so there's room to extend
2091             auto n = npages + (npages >> 1);
2092             if (n < size_t.max/PAGESIZE)
2093                 npages = n;
2094         }
2095 
2096         // Allocate successively larger pools up to 8 megs
2097         if (this.pooltable.length)
2098         {
2099             size_t n;
2100 
2101             n = config.minPoolSize + config.incPoolSize * this.pooltable.length;
2102             if (n > config.maxPoolSize)
2103                 n = config.maxPoolSize;                 // cap pool size
2104             n /= PAGESIZE; // convert bytes to pages
2105             if (npages < n)
2106                 npages = n;
2107         }
2108 
2109         //printf("npages = %d\n", npages);
2110 
2111         auto pool = cast(Pool *)cstdlib.calloc(1, isLargeObject ? LargeObjectPool.sizeof : SmallObjectPool.sizeof);
2112         if (pool)
2113         {
2114             pool.initialize(npages, isLargeObject);
2115             if (collectInProgress)
2116                 pool.mark.setAll();
2117             if (!pool.baseAddr || !pooltable.insert(pool))
2118             {
2119                 pool.Dtor();
2120                 cstdlib.free(pool);
2121                 return null;
2122             }
2123         }
2124 
2125         mappedPages += npages;
2126 
2127         if (config.profile)
2128         {
2129             if (cast(size_t)mappedPages * PAGESIZE > maxPoolMemory)
2130                 maxPoolMemory = cast(size_t)mappedPages * PAGESIZE;
2131         }
2132         return pool;
2133     }
2134 
2135     /**
2136     * Allocate a page of bin's.
2137     * Returns:
2138     *           head of a single linked list of new entries
2139     */
2140     List* allocPage(Bins bin) nothrow
2141     {
2142         //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin);
2143         foreach (Pool* pool; this.pooltable[])
2144         {
2145             if (pool.isLargeObject)
2146                 continue;
2147             if (List* p = (cast(SmallObjectPool*)pool).allocPage(bin))
2148             {
2149                 ++usedSmallPages;
2150                 return p;
2151             }
2152         }
2153         return null;
2154     }
2155 
2156     static struct ScanRange(bool precise)
2157     {
2158         void* pbot;
2159         void* ptop;
2160         static if (precise)
2161         {
2162             void** pbase;      // start of memory described by ptrbitmap
2163             size_t* ptrbmp;    // bits from is_pointer or rtinfo
2164             size_t bmplength;  // number of valid bits
2165         }
2166     }
2167 
2168     static struct ToScanStack(RANGE)
2169     {
2170     nothrow:
2171         @disable this(this);
2172         auto stackLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
2173 
2174         void reset()
2175         {
2176             _length = 0;
2177             if (_p)
2178             {
2179                 os_mem_unmap(_p, _cap * RANGE.sizeof);
2180                 _p = null;
2181             }
2182             _cap = 0;
2183         }
2184         void clear()
2185         {
2186             _length = 0;
2187         }
2188 
2189         void push(RANGE rng)
2190         {
2191             if (_length == _cap) grow();
2192             _p[_length++] = rng;
2193         }
2194 
2195         RANGE pop()
2196         in { assert(!empty); }
2197         do
2198         {
2199             return _p[--_length];
2200         }
2201 
2202         bool popLocked(ref RANGE rng)
2203         {
2204             if (_length == 0)
2205                 return false;
2206 
2207             stackLock.lock();
2208             scope(exit) stackLock.unlock();
2209             if (_length == 0)
2210                 return false;
2211             rng = _p[--_length];
2212             return true;
2213         }
2214 
2215         ref inout(RANGE) opIndex(size_t idx) inout
2216         in { assert(idx < _length); }
2217         do
2218         {
2219             return _p[idx];
2220         }
2221 
2222         @property size_t length() const { return _length; }
2223         @property bool empty() const { return !length; }
2224 
2225     private:
2226         void grow()
2227         {
2228             pragma(inline, false);
2229 
2230             enum initSize = 64 * 1024; // Windows VirtualAlloc granularity
2231             immutable ncap = _cap ? 2 * _cap : initSize / RANGE.sizeof;
2232             auto p = cast(RANGE*)os_mem_map(ncap * RANGE.sizeof);
2233             if (p is null) onOutOfMemoryErrorNoGC();
2234             if (_p !is null)
2235             {
2236                 p[0 .. _length] = _p[0 .. _length];
2237                 os_mem_unmap(_p, _cap * RANGE.sizeof);
2238             }
2239             _p = p;
2240             _cap = ncap;
2241         }
2242 
2243         size_t _length;
2244         RANGE* _p;
2245         size_t _cap;
2246     }
2247 
2248     ToScanStack!(ScanRange!false) toscanConservative;
2249     ToScanStack!(ScanRange!true) toscanPrecise;
2250 
2251     template scanStack(bool precise)
2252     {
2253         static if (precise)
2254             alias scanStack = toscanPrecise;
2255         else
2256             alias scanStack = toscanConservative;
2257     }
2258 
2259     /**
2260      * Search a range of memory values and mark any pointers into the GC pool.
2261      */
2262     private void mark(bool precise, bool parallel, bool shared_mem)(ScanRange!precise rng) scope nothrow
2263     {
2264         alias toscan = scanStack!precise;
2265 
2266         debug(MARK_PRINTF)
2267             printf("marking range: [%p..%p] (%#llx)\n", pbot, ptop, cast(long)(ptop - pbot));
2268 
2269         // limit the amount of ranges added to the toscan stack
2270         enum FANOUT_LIMIT = 32;
2271         size_t stackPos;
2272         ScanRange!precise[FANOUT_LIMIT] stack = void;
2273 
2274         size_t pcache = 0;
2275 
2276         // let dmd allocate a register for this.pools
2277         auto pools = pooltable.pools;
2278         const highpool = pooltable.length - 1;
2279         const minAddr = pooltable.minAddr;
2280         size_t memSize = pooltable.maxAddr - minAddr;
2281         Pool* pool = null;
2282 
2283         // properties of allocation pointed to
2284         ScanRange!precise tgt = void;
2285 
2286         for (;;)
2287         {
2288             auto p = *cast(void**)(rng.pbot);
2289 
2290             debug(MARK_PRINTF) printf("\tmark %p: %p\n", rng.pbot, p);
2291 
2292             if (cast(size_t)(p - minAddr) < memSize &&
2293                 (cast(size_t)p & ~cast(size_t)(PAGESIZE-1)) != pcache)
2294             {
2295                 static if (precise) if (rng.pbase)
2296                 {
2297                     size_t bitpos = cast(void**)rng.pbot - rng.pbase;
2298                     while (bitpos >= rng.bmplength)
2299                     {
2300                         bitpos -= rng.bmplength;
2301                         rng.pbase += rng.bmplength;
2302                     }
2303                     if (!core.bitop.bt(rng.ptrbmp, bitpos))
2304                     {
2305                         debug(MARK_PRINTF) printf("\t\tskipping non-pointer\n");
2306                         goto LnextPtr;
2307                     }
2308                 }
2309 
2310                 if (!pool || p < pool.baseAddr || p >= pool.topAddr)
2311                 {
2312                     size_t low = 0;
2313                     size_t high = highpool;
2314                     while (true)
2315                     {
2316                         size_t mid = (low + high) >> 1;
2317                         pool = pools[mid];
2318                         if (p < pool.baseAddr)
2319                             high = mid - 1;
2320                         else if (p >= pool.topAddr)
2321                             low = mid + 1;
2322                         else break;
2323 
2324                         if (low > high)
2325                             goto LnextPtr;
2326                     }
2327                 }
2328                 size_t offset = cast(size_t)(p - pool.baseAddr);
2329                 size_t biti = void;
2330                 size_t pn = offset / PAGESIZE;
2331                 size_t bin = pool.pagetable[pn]; // not Bins to avoid multiple size extension instructions
2332 
2333                 debug(MARK_PRINTF)
2334                     printf("\t\tfound pool %p, base=%p, pn = %lld, bin = %d\n", pool, pool.baseAddr, cast(long)pn, bin);
2335 
2336                 // Adjust bit to be at start of allocated memory block
2337                 if (bin < Bins.B_PAGE)
2338                 {
2339                     // We don't care abou setting pointsToBase correctly
2340                     // because it's ignored for small object pools anyhow.
2341                     auto offsetBase = baseOffset(offset, cast(Bins)bin);
2342                     biti = offsetBase >> Pool.ShiftBy.Small;
2343                     //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
2344 
2345                     if (!pool.mark.testAndSet!shared_mem(biti) && !pool.noscan.test(biti))
2346                     {
2347                         tgt.pbot = pool.baseAddr + offsetBase;
2348                         tgt.ptop = tgt.pbot + binsize[bin];
2349                         static if (precise)
2350                         {
2351                             tgt.pbase = cast(void**)pool.baseAddr;
2352                             tgt.ptrbmp = pool.is_pointer.data;
2353                             tgt.bmplength = size_t.max; // no repetition
2354                         }
2355                         goto LaddRange;
2356                     }
2357                 }
2358                 else if (bin == Bins.B_PAGE)
2359                 {
2360                     biti = offset >> Pool.ShiftBy.Large;
2361                     //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
2362 
2363                     pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
2364                     tgt.pbot = cast(void*)pcache;
2365 
2366                     // For the NO_INTERIOR attribute.  This tracks whether
2367                     // the pointer is an interior pointer or points to the
2368                     // base address of a block.
2369                     if (tgt.pbot != sentinel_sub(p) && pool.nointerior.nbits && pool.nointerior.test(biti))
2370                         goto LnextPtr;
2371 
2372                     if (!pool.mark.testAndSet!shared_mem(biti) && !pool.noscan.test(biti))
2373                     {
2374                         tgt.ptop = tgt.pbot + (cast(LargeObjectPool*)pool).getSize(pn);
2375                         goto LaddLargeRange;
2376                     }
2377                 }
2378                 else if (bin == Bins.B_PAGEPLUS)
2379                 {
2380                     pn -= pool.bPageOffsets[pn];
2381                     biti = pn * (PAGESIZE >> Pool.ShiftBy.Large);
2382 
2383                     pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
2384                     if (pool.nointerior.nbits && pool.nointerior.test(biti))
2385                         goto LnextPtr;
2386 
2387                     if (!pool.mark.testAndSet!shared_mem(biti) && !pool.noscan.test(biti))
2388                     {
2389                         tgt.pbot = pool.baseAddr + (pn * PAGESIZE);
2390                         tgt.ptop = tgt.pbot + (cast(LargeObjectPool*)pool).getSize(pn);
2391                     LaddLargeRange:
2392                         static if (precise)
2393                         {
2394                             auto rtinfo = pool.rtinfo[biti];
2395                             if (rtinfo is rtinfoNoPointers)
2396                                 goto LnextPtr; // only if inconsistent with noscan
2397                             if (rtinfo is rtinfoHasPointers)
2398                             {
2399                                 tgt.pbase = null; // conservative
2400                             }
2401                             else
2402                             {
2403                                 tgt.ptrbmp = cast(size_t*)rtinfo;
2404                                 size_t element_size = *tgt.ptrbmp++;
2405                                 tgt.bmplength = (element_size + (void*).sizeof - 1) / (void*).sizeof;
2406                                 assert(tgt.bmplength);
2407 
2408                                 debug(SENTINEL)
2409                                     tgt.pbot = sentinel_add(tgt.pbot);
2410                                 if (pool.appendable.test(biti))
2411                                 {
2412                                     // take advantage of knowing array layout in rt.lifetime
2413                                     void* arrtop = tgt.pbot + 16 + *cast(size_t*)tgt.pbot;
2414                                     assert (arrtop > tgt.pbot && arrtop <= tgt.ptop);
2415                                     tgt.pbot += 16;
2416                                     tgt.ptop = arrtop;
2417                                 }
2418                                 else
2419                                 {
2420                                     tgt.ptop = tgt.pbot + element_size;
2421                                 }
2422                                 tgt.pbase = cast(void**)tgt.pbot;
2423                             }
2424                         }
2425                         goto LaddRange;
2426                     }
2427                 }
2428                 else
2429                 {
2430                     // Don't mark bits in B_FREE pages
2431                     assert(bin == Bins.B_FREE);
2432                 }
2433             }
2434         LnextPtr:
2435             rng.pbot += (void*).sizeof;
2436             if (rng.pbot < rng.ptop)
2437                 continue;
2438 
2439         LnextRange:
2440             if (stackPos)
2441             {
2442                 // pop range from local stack and recurse
2443                 rng = stack[--stackPos];
2444             }
2445             else
2446             {
2447                 static if (parallel)
2448                 {
2449                     if (!toscan.popLocked(rng))
2450                         break; // nothing more to do
2451                 }
2452                 else
2453                 {
2454                     if (toscan.empty)
2455                         break; // nothing more to do
2456 
2457                     // pop range from global stack and recurse
2458                     rng = toscan.pop();
2459                 }
2460             }
2461             // printf("  pop [%p..%p] (%#zx)\n", p1, p2, cast(size_t)p2 - cast(size_t)p1);
2462             goto LcontRange;
2463 
2464         LaddRange:
2465             rng.pbot += (void*).sizeof;
2466             if (rng.pbot < rng.ptop)
2467             {
2468                 if (stackPos < stack.length)
2469                 {
2470                     stack[stackPos] = tgt;
2471                     stackPos++;
2472                     continue;
2473                 }
2474                 static if (parallel)
2475                 {
2476                     toscan.stackLock.lock();
2477                     scope(exit) toscan.stackLock.unlock();
2478                 }
2479                 toscan.push(rng);
2480                 // reverse order for depth-first-order traversal
2481                 foreach_reverse (ref range; stack)
2482                     toscan.push(range);
2483                 stackPos = 0;
2484             }
2485         LendOfRange:
2486             // continue with last found range
2487             rng = tgt;
2488 
2489         LcontRange:
2490             pcache = 0;
2491         }
2492     }
2493 
2494     void markConservative(bool shared_mem)(void *pbot, void *ptop) scope nothrow
2495     {
2496         if (pbot < ptop)
2497             mark!(false, false, shared_mem)(ScanRange!false(pbot, ptop));
2498     }
2499 
2500     void markPrecise(bool shared_mem)(void *pbot, void *ptop) scope nothrow
2501     {
2502         if (pbot < ptop)
2503             mark!(true, false, shared_mem)(ScanRange!true(pbot, ptop, null));
2504     }
2505 
2506     version (COLLECT_PARALLEL)
2507     ToScanStack!(void*) toscanRoots;
2508 
2509     version (COLLECT_PARALLEL)
2510     void collectRoots(void *pbot, void *ptop) scope nothrow
2511     {
2512         const minAddr = pooltable.minAddr;
2513         size_t memSize = pooltable.maxAddr - minAddr;
2514 
2515         for (auto p = cast(void**)pbot; cast(void*)p < ptop; p++)
2516         {
2517             auto ptr = *p;
2518             if (cast(size_t)(ptr - minAddr) < memSize)
2519                 toscanRoots.push(ptr);
2520         }
2521     }
2522 
2523     // collection step 1: prepare freebits and mark bits
2524     void prepare() nothrow
2525     {
2526         debug(COLLECT_PRINTF) printf("preparing mark.\n");
2527 
2528         foreach (Pool* pool; this.pooltable[])
2529         {
2530             if (pool.isLargeObject)
2531                 pool.mark.zero();
2532             else
2533                 pool.mark.copy(&pool.freebits);
2534         }
2535     }
2536 
2537     // collection step 2: mark roots and heap
2538     void markAll(alias markFn)(bool nostack) nothrow
2539     {
2540         if (!nostack)
2541         {
2542             debug(COLLECT_PRINTF) printf("\tscan stacks.\n");
2543             // Scan stacks and registers for each paused thread
2544             thread_scanAll(&markFn);
2545         }
2546 
2547         // Scan roots[]
2548         debug(COLLECT_PRINTF) printf("\tscan roots[]\n");
2549         foreach (root; roots)
2550         {
2551             markFn(cast(void*)&root.proot, cast(void*)(&root.proot + 1));
2552         }
2553 
2554         // Scan ranges[]
2555         debug(COLLECT_PRINTF) printf("\tscan ranges[]\n");
2556         //log++;
2557         foreach (range; ranges)
2558         {
2559             debug(COLLECT_PRINTF) printf("\t\t%p .. %p\n", range.pbot, range.ptop);
2560             markFn(range.pbot, range.ptop);
2561         }
2562         //log--;
2563     }
2564 
2565     version (COLLECT_PARALLEL)
2566     void collectAllRoots(bool nostack) nothrow
2567     {
2568         if (!nostack)
2569         {
2570             debug(COLLECT_PRINTF) printf("\tcollect stacks.\n");
2571             // Scan stacks and registers for each paused thread
2572             thread_scanAll(&collectRoots);
2573         }
2574 
2575         // Scan roots[]
2576         debug(COLLECT_PRINTF) printf("\tcollect roots[]\n");
2577         foreach (root; roots)
2578         {
2579             toscanRoots.push(root);
2580         }
2581 
2582         // Scan ranges[]
2583         debug(COLLECT_PRINTF) printf("\tcollect ranges[]\n");
2584         foreach (range; ranges)
2585         {
2586             debug(COLLECT_PRINTF) printf("\t\t%p .. %p\n", range.pbot, range.ptop);
2587             collectRoots(range.pbot, range.ptop);
2588         }
2589     }
2590 
2591     // collection step 3: finalize unreferenced objects, recover full pages with no live objects
2592     size_t sweep() nothrow
2593     {
2594         // Free up everything not marked
2595         debug(COLLECT_PRINTF) printf("\tfree'ing\n");
2596         size_t freedLargePages;
2597         size_t freedSmallPages;
2598         size_t freed;
2599         foreach (Pool* pool; this.pooltable[])
2600         {
2601             size_t pn;
2602 
2603             if (pool.isLargeObject)
2604             {
2605                 auto lpool = cast(LargeObjectPool*)pool;
2606                 size_t numFree = 0;
2607                 size_t npages;
2608                 for (pn = 0; pn < pool.npages; pn += npages)
2609                 {
2610                     npages = pool.bPageOffsets[pn];
2611                     Bins bin = cast(Bins)pool.pagetable[pn];
2612                     if (bin == Bins.B_FREE)
2613                     {
2614                         numFree += npages;
2615                         continue;
2616                     }
2617                     assert(bin == Bins.B_PAGE);
2618                     size_t biti = pn;
2619 
2620                     if (!pool.mark.test(biti))
2621                     {
2622                         void *p = pool.baseAddr + pn * PAGESIZE;
2623                         void* q = sentinel_add(p);
2624                         sentinel_Invariant(q);
2625 
2626                         if (pool.finals.nbits && pool.finals.clear(biti))
2627                         {
2628                             size_t size = npages * PAGESIZE - SENTINEL_EXTRA;
2629                             uint attr = pool.getBits(biti);
2630                             rt_finalizeFromGC(q, sentinel_size(q, size), attr);
2631                         }
2632 
2633                         pool.clrBits(biti, ~BlkAttr.NONE ^ BlkAttr.FINALIZE);
2634 
2635                         debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
2636                         leakDetector.log_free(q, sentinel_size(q, npages * PAGESIZE - SENTINEL_EXTRA));
2637                         pool.pagetable[pn..pn+npages] = Bins.B_FREE;
2638                         if (pn < pool.searchStart) pool.searchStart = pn;
2639                         freedLargePages += npages;
2640                         pool.freepages += npages;
2641                         numFree += npages;
2642 
2643                         debug (MEMSTOMP) memset(p, 0xF3, npages * PAGESIZE);
2644                         // Don't need to update searchStart here because
2645                         // pn is guaranteed to be greater than last time
2646                         // we updated it.
2647 
2648                         pool.largestFree = pool.freepages; // invalidate
2649                     }
2650                     else
2651                     {
2652                         if (numFree > 0)
2653                         {
2654                             lpool.setFreePageOffsets(pn - numFree, numFree);
2655                             numFree = 0;
2656                         }
2657                     }
2658                 }
2659                 if (numFree > 0)
2660                     lpool.setFreePageOffsets(pn - numFree, numFree);
2661             }
2662             else
2663             {
2664                 // reinit chain of pages to rebuild free list
2665                 pool.recoverPageFirst[] = cast(uint)pool.npages;
2666 
2667                 for (pn = 0; pn < pool.npages; pn++)
2668                 {
2669                     Bins bin = cast(Bins)pool.pagetable[pn];
2670 
2671                     if (bin < Bins.B_PAGE)
2672                     {
2673                         auto freebitsdata = pool.freebits.data + pn * PageBits.length;
2674                         auto markdata = pool.mark.data + pn * PageBits.length;
2675 
2676                         // the entries to free are allocated objects (freebits == false)
2677                         // that are not marked (mark == false)
2678                         PageBits toFree;
2679                         static foreach (w; 0 .. PageBits.length)
2680                             toFree[w] = (~freebitsdata[w] & ~markdata[w]);
2681 
2682                         // the page is unchanged if there is nothing to free
2683                         bool unchanged = true;
2684                         static foreach (w; 0 .. PageBits.length)
2685                             unchanged = unchanged && (toFree[w] == 0);
2686                         if (unchanged)
2687                         {
2688                             bool hasDead = false;
2689                             static foreach (w; 0 .. PageBits.length)
2690                                 hasDead = hasDead || (~freebitsdata[w] != baseOffsetBits[bin][w]);
2691                             if (hasDead)
2692                             {
2693                                 // add to recover chain
2694                                 pool.binPageChain[pn] = pool.recoverPageFirst[bin];
2695                                 pool.recoverPageFirst[bin] = cast(uint)pn;
2696                             }
2697                             else
2698                             {
2699                                 pool.binPageChain[pn] = Pool.PageRecovered;
2700                             }
2701                             continue;
2702                         }
2703 
2704                         // the page can be recovered if all of the allocated objects (freebits == false)
2705                         // are freed
2706                         bool recoverPage = true;
2707                         static foreach (w; 0 .. PageBits.length)
2708                             recoverPage = recoverPage && (~freebitsdata[w] == toFree[w]);
2709 
2710                         // We need to loop through each object if any have a finalizer,
2711                         // or, if any of the debug hooks are enabled.
2712                         bool doLoop = false;
2713                         debug (SENTINEL)
2714                             doLoop = true;
2715                         else version (assert)
2716                             doLoop = true;
2717                         else debug (COLLECT_PRINTF) // need output for each object
2718                             doLoop = true;
2719                         else debug (LOGGING)
2720                             doLoop = true;
2721                         else debug (MEMSTOMP)
2722                             doLoop = true;
2723                         else if (pool.finals.data)
2724                         {
2725                             // finalizers must be called on objects that are about to be freed
2726                             auto finalsdata = pool.finals.data + pn * PageBits.length;
2727                             static foreach (w; 0 .. PageBits.length)
2728                                 doLoop = doLoop || (toFree[w] & finalsdata[w]) != 0;
2729                         }
2730 
2731                         if (doLoop)
2732                         {
2733                             immutable size = binsize[bin];
2734                             void *p = pool.baseAddr + pn * PAGESIZE;
2735                             immutable base = pn * (PAGESIZE/16);
2736                             immutable bitstride = size / 16;
2737 
2738                             // ensure that there are at least <size> bytes for every address
2739                             //  below ptop even if unaligned
2740                             void *ptop = p + PAGESIZE - size + 1;
2741                             for (size_t i; p < ptop; p += size, i += bitstride)
2742                             {
2743                                 immutable biti = base + i;
2744 
2745                                 if (!pool.mark.test(biti))
2746                                 {
2747                                     void* q = sentinel_add(p);
2748                                     sentinel_Invariant(q);
2749 
2750                                     if (pool.finals.nbits && pool.finals.test(biti))
2751                                         rt_finalizeFromGC(q, sentinel_size(q, size), pool.getBits(biti));
2752 
2753                                     assert(core.bitop.bt(toFree.ptr, i));
2754 
2755                                     debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p);
2756                                     leakDetector.log_free(q, sentinel_size(q, size));
2757 
2758                                     debug (MEMSTOMP) memset(p, 0xF3, size);
2759                                 }
2760                             }
2761                         }
2762 
2763                         if (recoverPage)
2764                         {
2765                             pool.freeAllPageBits(pn);
2766 
2767                             pool.pagetable[pn] = Bins.B_FREE;
2768                             // add to free chain
2769                             pool.binPageChain[pn] = cast(uint) pool.searchStart;
2770                             pool.searchStart = pn;
2771                             pool.freepages++;
2772                             freedSmallPages++;
2773                         }
2774                         else
2775                         {
2776                             pool.freePageBits(pn, toFree);
2777 
2778                             // add to recover chain
2779                             pool.binPageChain[pn] = pool.recoverPageFirst[bin];
2780                             pool.recoverPageFirst[bin] = cast(uint)pn;
2781                         }
2782                     }
2783                 }
2784             }
2785         }
2786 
2787         assert(freedLargePages <= usedLargePages);
2788         usedLargePages -= freedLargePages;
2789         debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n",
2790                                      freed, freedLargePages, this.pooltable.length);
2791 
2792         assert(freedSmallPages <= usedSmallPages);
2793         usedSmallPages -= freedSmallPages;
2794         debug(COLLECT_PRINTF) printf("\trecovered small pages = %d\n", freedSmallPages);
2795 
2796         return freedLargePages + freedSmallPages;
2797     }
2798 
2799     bool recoverPage(SmallObjectPool* pool, size_t pn, Bins bin) nothrow
2800     {
2801         size_t size = binsize[bin];
2802         size_t bitbase = pn * (PAGESIZE / 16);
2803 
2804         auto freebitsdata = pool.freebits.data + pn * PageBits.length;
2805 
2806         // the page had dead objects when collecting, these cannot have been resurrected
2807         bool hasDead = false;
2808         static foreach (w; 0 .. PageBits.length)
2809             hasDead = hasDead || (freebitsdata[w] != 0);
2810         assert(hasDead);
2811 
2812         // prepend to buckets, but with forward addresses inside the page
2813         assert(bucket[bin] is null);
2814         List** bucketTail = &bucket[bin];
2815 
2816         void* p = pool.baseAddr + pn * PAGESIZE;
2817         const top = PAGESIZE - size + 1; // ensure <size> bytes available even if unaligned
2818         for (size_t u = 0; u < top; u += size)
2819         {
2820             if (!core.bitop.bt(freebitsdata, u / 16))
2821                 continue;
2822             auto elem = cast(List *)(p + u);
2823             elem.pool = &pool.base;
2824             *bucketTail = elem;
2825             bucketTail = &elem.next;
2826         }
2827         *bucketTail = null;
2828         assert(bucket[bin] !is null);
2829         return true;
2830     }
2831 
2832     bool recoverNextPage(Bins bin) nothrow
2833     {
2834         SmallObjectPool* pool = recoverPool[bin];
2835         while (pool)
2836         {
2837             auto pn = pool.recoverPageFirst[bin];
2838             while (pn < pool.npages)
2839             {
2840                 auto next = pool.binPageChain[pn];
2841                 pool.binPageChain[pn] = Pool.PageRecovered;
2842                 pool.recoverPageFirst[bin] = next;
2843                 if (recoverPage(pool, pn, bin))
2844                     return true;
2845                 pn = next;
2846             }
2847             pool = setNextRecoverPool(bin, pool.ptIndex + 1);
2848         }
2849         return false;
2850     }
2851 
2852     private SmallObjectPool* setNextRecoverPool(Bins bin, size_t poolIndex) nothrow
2853     {
2854         Pool* pool;
2855         while (poolIndex < this.pooltable.length &&
2856                ((pool = this.pooltable[poolIndex]).isLargeObject ||
2857                 pool.recoverPageFirst[bin] >= pool.npages))
2858             poolIndex++;
2859 
2860         return recoverPool[bin] = poolIndex < this.pooltable.length ? cast(SmallObjectPool*)pool : null;
2861     }
2862 
2863     version (COLLECT_FORK)
2864     void disableFork() nothrow
2865     {
2866         markProcPid = 0;
2867         shouldFork = false;
2868     }
2869 
2870     version (COLLECT_FORK)
2871     ChildStatus collectFork(bool block) nothrow
2872     {
2873         typeof(return) rc = wait_pid(markProcPid, block);
2874         final switch (rc)
2875         {
2876             case ChildStatus.done:
2877                 debug(COLLECT_PRINTF) printf("\t\tmark proc DONE (block=%d)\n",
2878                                                 cast(int) block);
2879                 markProcPid = 0;
2880                 // process GC marks then sweep
2881                 thread_suspendAll();
2882                 thread_processGCMarks(&isMarked);
2883                 thread_resumeAll();
2884                 break;
2885             case ChildStatus.running:
2886                 debug(COLLECT_PRINTF) printf("\t\tmark proc RUNNING\n");
2887                 if (!block)
2888                     break;
2889                 // Something went wrong, if block is true, wait() should never
2890                 // return RUNNING.
2891                 goto case ChildStatus.error;
2892             case ChildStatus.error:
2893                 debug(COLLECT_PRINTF) printf("\t\tmark proc ERROR\n");
2894                 // Try to keep going without forking
2895                 // and do the marking in this thread
2896                 break;
2897         }
2898         return rc;
2899     }
2900 
2901     version (COLLECT_FORK)
2902     ChildStatus markFork(bool nostack, bool block, bool doParallel) nothrow
2903     {
2904         // Forking is enabled, so we fork() and start a new concurrent mark phase
2905         // in the child. If the collection should not block, the parent process
2906         // tells the caller no memory could be recycled immediately (if this collection
2907         // was triggered by an allocation, the caller should allocate more memory
2908         // to fulfill the request).
2909         // If the collection should block, the parent will wait for the mark phase
2910         // to finish before returning control to the mutator,
2911         // but other threads are restarted and may run in parallel with the mark phase
2912         // (unless they allocate or use the GC themselves, in which case
2913         // the global GC lock will stop them).
2914         // fork now and sweep later
2915         int child_mark() scope
2916         {
2917             if (doParallel)
2918                 markParallel(nostack);
2919             else if (ConservativeGC.isPrecise)
2920                 markAll!(markPrecise!true)(nostack);
2921             else
2922                 markAll!(markConservative!true)(nostack);
2923             return 0;
2924         }
2925 
2926         import core.stdc.stdlib : _Exit;
2927         debug (PRINTF_TO_FILE)
2928         {
2929             fflush(null); // avoid duplicated FILE* output
2930         }
2931         version (OSX)
2932         {
2933             auto pid = __fork(); // avoids calling handlers (from libc source code)
2934         }
2935         else version (linux)
2936         {
2937             // clone() fits better as we don't want to do anything but scanning in the child process.
2938             // no fork-handlers are called, so we can avoid deadlocks due to malloc locks. Probably related:
2939             //  https://sourceware.org/bugzilla/show_bug.cgi?id=4737
2940             import core.sys.linux.sched : clone;
2941             import core.sys.posix.signal : SIGCHLD;
2942             const flags = SIGCHLD; // exit signal
2943             scope int delegate() scope dg = &child_mark;
2944             extern(C) static int wrap_delegate(void* arg)
2945             {
2946                 auto dg = cast(int delegate() scope*)arg;
2947                 return (*dg)();
2948             }
2949             ubyte[256] stackbuf; // enough stack space for clone() to place some info for the child without stomping the parent stack
2950             auto stack = stackbuf.ptr + (isStackGrowingDown ? stackbuf.length : 0);
2951             auto pid = clone(&wrap_delegate, stack, flags, &dg);
2952         }
2953         else
2954         {
2955             fork_needs_lock = false;
2956             auto pid = fork();
2957             fork_needs_lock = true;
2958         }
2959         switch (pid)
2960         {
2961             case -1: // fork() failed, retry without forking
2962                 return ChildStatus.error;
2963             case 0: // child process (not run with clone)
2964                 child_mark();
2965                 _Exit(0);
2966             default: // the parent
2967                 thread_resumeAll();
2968                 if (!block)
2969                 {
2970                     markProcPid = pid;
2971                     return ChildStatus.running;
2972                 }
2973                 ChildStatus r = wait_pid(pid); // block until marking is done
2974                 if (r == ChildStatus.error)
2975                 {
2976                     thread_suspendAll();
2977                     // there was an error
2978                     // do the marking in this thread
2979                     disableFork();
2980                     if (doParallel)
2981                         markParallel(nostack);
2982                     else if (ConservativeGC.isPrecise)
2983                         markAll!(markPrecise!false)(nostack);
2984                     else
2985                         markAll!(markConservative!false)(nostack);
2986                 } else {
2987                     assert(r == ChildStatus.done);
2988                     assert(r != ChildStatus.running);
2989                 }
2990         }
2991         return ChildStatus.done; // waited for the child
2992     }
2993 
2994     /**
2995      * Return number of full pages free'd.
2996      * The collection is done concurrently only if block and isFinal are false.
2997      */
2998     size_t fullcollect(bool nostack = false, bool block = false, bool isFinal = false) nothrow
2999     {
3000         // It is possible that `fullcollect` will be called from a thread which
3001         // is not yet registered in runtime (because allocating `new Thread` is
3002         // part of `thread_attachThis` implementation). In that case it is
3003         // better not to try actually collecting anything
3004 
3005         if (Thread.getThis() is null)
3006             return 0;
3007 
3008         MonoTime start, stop, begin;
3009         begin = start = currTime;
3010 
3011         debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
3012         version (COLLECT_PARALLEL)
3013         {
3014             bool doParallel = config.parallel > 0 && !config.fork;
3015             if (doParallel && !scanThreadData)
3016             {
3017                 if (isFinal) // avoid starting threads for parallel marking
3018                     doParallel = false;
3019                 else
3020                     startScanThreads();
3021             }
3022         }
3023         else
3024             enum doParallel = false;
3025 
3026         //printf("\tpool address range = %p .. %p\n", minAddr, maxAddr);
3027 
3028         version (COLLECT_FORK)
3029             alias doFork = shouldFork;
3030         else
3031             enum doFork = false;
3032 
3033         if (doFork && collectInProgress)
3034         {
3035             version (COLLECT_FORK)
3036             {
3037                 // If there is a mark process running, check if it already finished.
3038                 // If that is the case, we move to the sweep phase.
3039                 // If it's still running, either we block until the mark phase is
3040                 // done (and then sweep to finish the collection), or in case of error
3041                 // we redo the mark phase without forking.
3042                 ChildStatus rc = collectFork(block);
3043                 final switch (rc)
3044                 {
3045                     case ChildStatus.done:
3046                         break;
3047                     case ChildStatus.running:
3048                         return 0;
3049                     case ChildStatus.error:
3050                         disableFork();
3051                         goto Lmark;
3052                 }
3053             }
3054         }
3055         else
3056         {
3057 Lmark:
3058             // lock roots and ranges around suspending threads b/c they're not reentrant safe
3059             rangesLock.lock();
3060             rootsLock.lock();
3061             debug(INVARIANT) inCollection = true;
3062             scope (exit)
3063             {
3064                 debug(INVARIANT) inCollection = false;
3065                 rangesLock.unlock();
3066                 rootsLock.unlock();
3067             }
3068             thread_suspendAll();
3069 
3070             prepare();
3071 
3072             stop = currTime;
3073             prepTime += (stop - start);
3074             start = stop;
3075 
3076             if (doFork && !isFinal && !block) // don't start a new fork during termination
3077             {
3078                 version (COLLECT_FORK)
3079                 {
3080                     auto forkResult = markFork(nostack, block, doParallel);
3081                     final switch (forkResult)
3082                     {
3083                         case ChildStatus.error:
3084                             // fork() failed, retry without forking
3085                             disableFork();
3086                             goto Lmark;
3087                         case ChildStatus.running:
3088                             // update profiling informations
3089                             stop = currTime;
3090                             markTime += (stop - start);
3091                             Duration pause = stop - begin;
3092                             if (pause > maxPauseTime)
3093                                 maxPauseTime = pause;
3094                             pauseTime += pause;
3095                             return 0;
3096                         case ChildStatus.done:
3097                             break;
3098                     }
3099                     // if we get here, forking failed and a standard STW collection got issued
3100                     // threads were suspended again, restart them
3101                     thread_suspendAll();
3102                 }
3103             }
3104             else if (doParallel)
3105             {
3106                 version (COLLECT_PARALLEL)
3107                     markParallel(nostack);
3108             }
3109             else
3110             {
3111                 if (ConservativeGC.isPrecise)
3112                     markAll!(markPrecise!false)(nostack);
3113                 else
3114                     markAll!(markConservative!false)(nostack);
3115             }
3116 
3117             thread_processGCMarks(&isMarked);
3118             thread_resumeAll();
3119             isFinal = false;
3120         }
3121 
3122         // If we get here with the forking GC, the child process has finished the marking phase
3123         // or block == true and we are using standard stop the world collection.
3124         // It is time to sweep
3125 
3126         stop = currTime;
3127         markTime += (stop - start);
3128         Duration pause = stop - begin;
3129         if (pause > maxPauseTime)
3130             maxPauseTime = pause;
3131         pauseTime += pause;
3132         start = stop;
3133 
3134         ConservativeGC._inFinalizer = true;
3135         size_t freedPages = void;
3136         {
3137             scope (failure) ConservativeGC._inFinalizer = false;
3138             freedPages = sweep();
3139             ConservativeGC._inFinalizer = false;
3140         }
3141 
3142         // minimize() should be called only after a call to fullcollect
3143         // terminates with a sweep
3144         if (minimizeAfterNextCollection || lowMem)
3145         {
3146             minimizeAfterNextCollection = false;
3147             minimize();
3148         }
3149 
3150         // init bucket lists
3151         bucket[] = null;
3152         foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL)
3153             setNextRecoverPool(bin, 0);
3154 
3155         stop = currTime;
3156         sweepTime += (stop - start);
3157 
3158         Duration collectionTime = stop - begin;
3159         if (collectionTime > maxCollectionTime)
3160             maxCollectionTime = collectionTime;
3161 
3162         ++numCollections;
3163 
3164         updateCollectThresholds();
3165         if (doFork && isFinal)
3166             return fullcollect(true, true, false);
3167         return freedPages;
3168     }
3169 
3170     /**
3171      * Returns true if the addr lies within a marked block.
3172      *
3173      * Warning! This should only be called while the world is stopped inside
3174      * the fullcollect function after all live objects have been marked, but before sweeping.
3175      */
3176     int isMarked(void *addr) scope nothrow
3177     {
3178         // first, we find the Pool this block is in, then check to see if the
3179         // mark bit is clear.
3180         auto pool = findPool(addr);
3181         if (pool)
3182         {
3183             auto offset = cast(size_t)(addr - pool.baseAddr);
3184             auto pn = offset / PAGESIZE;
3185             auto bins = cast(Bins)pool.pagetable[pn];
3186             size_t biti = void;
3187             if (bins < Bins.B_PAGE)
3188             {
3189                 biti = baseOffset(offset, bins) >> pool.ShiftBy.Small;
3190                 // doesn't need to check freebits because no pointer must exist
3191                 //  to a block that was free before starting the collection
3192             }
3193             else if (bins == Bins.B_PAGE)
3194             {
3195                 biti = pn * (PAGESIZE >> pool.ShiftBy.Large);
3196             }
3197             else if (bins == Bins.B_PAGEPLUS)
3198             {
3199                 pn -= pool.bPageOffsets[pn];
3200                 biti = pn * (PAGESIZE >> pool.ShiftBy.Large);
3201             }
3202             else // bins == Bins.B_FREE
3203             {
3204                 assert(bins == Bins.B_FREE);
3205                 return IsMarked.no;
3206             }
3207             return pool.mark.test(biti) ? IsMarked.yes : IsMarked.no;
3208         }
3209         return IsMarked.unknown;
3210     }
3211 
3212     version (Posix)
3213     {
3214         // A fork might happen while GC code is running in a different thread.
3215         // Because that would leave the GC in an inconsistent state,
3216         // make sure no GC code is running by acquiring the lock here,
3217         // before a fork.
3218         // This must not happen if fork is called from the GC with the lock already held
3219 
3220         __gshared bool fork_needs_lock = true; // racing condition with cocurrent calls of fork?
3221 
3222 
3223         extern(C) static void _d_gcx_atfork_prepare()
3224         {
3225             if (instance && fork_needs_lock)
3226                 ConservativeGC.lockNR();
3227         }
3228 
3229         extern(C) static void _d_gcx_atfork_parent()
3230         {
3231             if (instance && fork_needs_lock)
3232                 ConservativeGC.gcLock.unlock();
3233         }
3234 
3235         extern(C) static void _d_gcx_atfork_child()
3236         {
3237             if (instance && fork_needs_lock)
3238             {
3239                 ConservativeGC.gcLock.unlock();
3240 
3241                 // make sure the threads and event handles are reinitialized in a fork
3242                 version (COLLECT_PARALLEL)
3243                 {
3244                     if (Gcx.instance.scanThreadData)
3245                     {
3246                         cstdlib.free(Gcx.instance.scanThreadData);
3247                         Gcx.instance.numScanThreads = 0;
3248                         Gcx.instance.scanThreadData = null;
3249                         Gcx.instance.busyThreads = 0;
3250 
3251                         memset(&Gcx.instance.evStart, 0, Gcx.instance.evStart.sizeof);
3252                         memset(&Gcx.instance.evDone, 0, Gcx.instance.evDone.sizeof);
3253                     }
3254                 }
3255             }
3256         }
3257     }
3258 
3259     /* ============================ Parallel scanning =============================== */
3260     version (COLLECT_PARALLEL):
3261 
3262     import core.atomic;
3263     import core.cpuid;
3264     import core.sync.event;
3265 
3266     private: // disable invariants for background threads
3267 
3268     static struct ScanThreadData
3269     {
3270         ThreadID tid;
3271     }
3272     uint numScanThreads;
3273     ScanThreadData* scanThreadData;
3274 
3275     Event evStart;
3276     Event evDone;
3277 
3278     shared uint busyThreads;
3279     shared uint stoppedThreads;
3280     bool stopGC;
3281 
3282     void markParallel(bool nostack) nothrow
3283     {
3284         toscanRoots.clear();
3285         collectAllRoots(nostack);
3286         if (toscanRoots.empty)
3287             return;
3288 
3289         void** pbot = toscanRoots._p;
3290         void** ptop = toscanRoots._p + toscanRoots._length;
3291 
3292         debug(PARALLEL_PRINTF) printf("markParallel\n");
3293 
3294         size_t pointersPerThread = toscanRoots._length / (numScanThreads + 1);
3295         if (pointersPerThread > 0)
3296         {
3297             void pushRanges(bool precise)()
3298             {
3299                 alias toscan = scanStack!precise;
3300                 toscan.stackLock.lock();
3301 
3302                 for (int idx = 0; idx < numScanThreads; idx++)
3303                 {
3304                     toscan.push(ScanRange!precise(pbot, pbot + pointersPerThread));
3305                     pbot += pointersPerThread;
3306                 }
3307                 toscan.stackLock.unlock();
3308             }
3309             if (ConservativeGC.isPrecise)
3310                 pushRanges!true();
3311             else
3312                 pushRanges!false();
3313         }
3314         assert(pbot < ptop);
3315 
3316         busyThreads.atomicOp!"+="(1); // main thread is busy
3317 
3318         evStart.set();
3319 
3320         debug(PARALLEL_PRINTF) printf("mark %lld roots\n", cast(ulong)(ptop - pbot));
3321 
3322         if (ConservativeGC.isPrecise)
3323             mark!(true, true, true)(ScanRange!true(pbot, ptop, null));
3324         else
3325             mark!(false, true, true)(ScanRange!false(pbot, ptop));
3326 
3327         busyThreads.atomicOp!"-="(1);
3328 
3329         debug(PARALLEL_PRINTF) printf("waitForScanDone\n");
3330         pullFromScanStack();
3331         debug(PARALLEL_PRINTF) printf("waitForScanDone done\n");
3332     }
3333 
3334     int maxParallelThreads() nothrow
3335     {
3336         auto threads = threadsPerCPU();
3337 
3338         if (threads == 0)
3339         {
3340             // If the GC is called by module ctors no explicit
3341             // import dependency on the GC is generated. So the
3342             // GC module is not correctly inserted into the module
3343             // initialization chain. As it relies on core.cpuid being
3344             // initialized, force this here.
3345             try
3346             {
3347                 foreach (m; ModuleInfo)
3348                     if (m.name == "core.cpuid")
3349                         if (auto ctor = m.ctor())
3350                         {
3351                             ctor();
3352                             threads = threadsPerCPU();
3353                             break;
3354                         }
3355             }
3356             catch (Exception)
3357             {
3358                 assert(false, "unexpected exception iterating ModuleInfo");
3359             }
3360         }
3361         return threads;
3362     }
3363 
3364 
3365     void startScanThreads() nothrow
3366     {
3367         auto threads = maxParallelThreads();
3368         debug(PARALLEL_PRINTF) printf("startScanThreads: %d threads per CPU\n", threads);
3369         if (threads <= 1)
3370             return; // either core.cpuid not initialized or single core
3371 
3372         numScanThreads = threads >= config.parallel ? config.parallel : threads - 1;
3373 
3374         scanThreadData = cast(ScanThreadData*) cstdlib.calloc(numScanThreads, ScanThreadData.sizeof);
3375         if (!scanThreadData)
3376             onOutOfMemoryErrorNoGC();
3377 
3378         evStart.initialize(false, false);
3379         evDone.initialize(false, false);
3380 
3381         version (Posix)
3382         {
3383             import core.sys.posix.signal;
3384             // block all signals, scanBackground inherits this mask.
3385             // see https://issues.dlang.org/show_bug.cgi?id=20256
3386             sigset_t new_mask, old_mask;
3387             sigfillset(&new_mask);
3388             auto sigmask_rc = pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask);
3389             assert(sigmask_rc == 0, "failed to set up GC scan thread sigmask");
3390         }
3391 
3392         for (int idx = 0; idx < numScanThreads; idx++)
3393             scanThreadData[idx].tid = createLowLevelThread(&scanBackground, 0x4000, &stopScanThreads);
3394 
3395         version (Posix)
3396         {
3397             sigmask_rc = pthread_sigmask(SIG_SETMASK, &old_mask, null);
3398             assert(sigmask_rc == 0, "failed to set up GC scan thread sigmask");
3399         }
3400     }
3401 
3402     void stopScanThreads() nothrow
3403     {
3404         if (!numScanThreads)
3405             return;
3406 
3407         debug(PARALLEL_PRINTF) printf("stopScanThreads\n");
3408         int startedThreads = 0;
3409         for (int idx = 0; idx < numScanThreads; idx++)
3410             if (scanThreadData[idx].tid != scanThreadData[idx].tid.init)
3411                 startedThreads++;
3412 
3413         version (Windows)
3414             alias allThreadsDead = thread_DLLProcessDetaching;
3415         else
3416             enum allThreadsDead = false;
3417         stopGC = true;
3418         while (atomicLoad(stoppedThreads) < startedThreads && !allThreadsDead)
3419         {
3420             evStart.set();
3421             evDone.wait(dur!"msecs"(1));
3422         }
3423 
3424         for (int idx = 0; idx < numScanThreads; idx++)
3425         {
3426             if (scanThreadData[idx].tid != scanThreadData[idx].tid.init)
3427             {
3428                 joinLowLevelThread(scanThreadData[idx].tid);
3429                 scanThreadData[idx].tid = scanThreadData[idx].tid.init;
3430             }
3431         }
3432 
3433         evDone.terminate();
3434         evStart.terminate();
3435 
3436         cstdlib.free(scanThreadData);
3437         // scanThreadData = null; // keep non-null to not start again after shutdown
3438         numScanThreads = 0;
3439 
3440         debug(PARALLEL_PRINTF) printf("stopScanThreads done\n");
3441     }
3442 
3443     void scanBackground() nothrow
3444     {
3445         while (!stopGC)
3446         {
3447             evStart.wait();
3448             pullFromScanStack();
3449             evDone.set();
3450         }
3451         stoppedThreads.atomicOp!"+="(1);
3452     }
3453 
3454     void pullFromScanStack() nothrow
3455     {
3456         if (ConservativeGC.isPrecise)
3457             pullFromScanStackImpl!true();
3458         else
3459             pullFromScanStackImpl!false();
3460     }
3461 
3462     void pullFromScanStackImpl(bool precise)() nothrow
3463     {
3464         if (atomicLoad(busyThreads) == 0)
3465             return;
3466 
3467         debug(PARALLEL_PRINTF)
3468             pthread_t threadId = pthread_self();
3469         debug(PARALLEL_PRINTF) printf("scanBackground thread %d start\n", threadId);
3470 
3471         ScanRange!precise rng;
3472         alias toscan = scanStack!precise;
3473 
3474         while (atomicLoad(busyThreads) > 0)
3475         {
3476             if (toscan.empty)
3477             {
3478                 evDone.wait(dur!"msecs"(1));
3479                 continue;
3480             }
3481 
3482             busyThreads.atomicOp!"+="(1);
3483             if (toscan.popLocked(rng))
3484             {
3485                 debug(PARALLEL_PRINTF) printf("scanBackground thread %d scanning range [%p,%lld] from stack\n", threadId,
3486                                               rng.pbot, cast(long) (rng.ptop - rng.pbot));
3487                 mark!(precise, true, true)(rng);
3488             }
3489             busyThreads.atomicOp!"-="(1);
3490         }
3491         debug(PARALLEL_PRINTF) printf("scanBackground thread %d done\n", threadId);
3492     }
3493 }
3494 
3495 /* ============================ Pool  =============================== */
3496 
3497 struct Pool
3498 {
3499     void* baseAddr;
3500     void* topAddr;
3501     size_t ptIndex;     // index in pool table
3502     GCBits mark;        // entries already scanned, or should not be scanned
3503     GCBits freebits;    // entries that are on the free list (all bits set but for allocated objects at their base offset)
3504     GCBits finals;      // entries that need finalizer run on them
3505     GCBits structFinals;// struct entries that need a finalzier run on them
3506     GCBits noscan;      // entries that should not be scanned
3507     GCBits appendable;  // entries that are appendable
3508     GCBits nointerior;  // interior pointers should be ignored.
3509                         // Only implemented for large object pools.
3510     GCBits is_pointer;  // precise GC only: per-word, not per-block like the rest of them (SmallObjectPool only)
3511     size_t npages;
3512     size_t freepages;     // The number of pages not in use.
3513     Bins* pagetable;
3514 
3515     bool isLargeObject;
3516 
3517     enum ShiftBy
3518     {
3519         Small = 4,
3520         Large = 12
3521     }
3522     ShiftBy shiftBy;    // shift count for the divisor used for determining bit indices.
3523 
3524     // This tracks how far back we have to go to find the nearest B_PAGE at
3525     // a smaller address than a B_PAGEPLUS.  To save space, we use a uint.
3526     // This limits individual allocations to 16 terabytes, assuming a 4k
3527     // pagesize. (LargeObjectPool only)
3528     // For B_PAGE and B_FREE, this specifies the number of pages in this block.
3529     // As an optimization, a contiguous range of free pages tracks this information
3530     //  only for the first and the last page.
3531     uint* bPageOffsets;
3532 
3533     // The small object pool uses the same array to keep a chain of
3534     // - pages with the same bin size that are still to be recovered
3535     // - free pages (searchStart is first free page)
3536     // other pages are marked by value PageRecovered
3537     alias binPageChain = bPageOffsets;
3538 
3539     enum PageRecovered = uint.max;
3540 
3541     // first of chain of pages to recover (SmallObjectPool only)
3542     uint[Bins.B_NUMSMALL] recoverPageFirst;
3543 
3544     // precise GC: TypeInfo.rtInfo for allocation (LargeObjectPool only)
3545     immutable(size_t)** rtinfo;
3546 
3547     // This variable tracks a conservative estimate of where the first free
3548     // page in this pool is, so that if a lot of pages towards the beginning
3549     // are occupied, we can bypass them in O(1).
3550     size_t searchStart;
3551     size_t largestFree; // upper limit for largest free chunk in large object pool
3552 
3553     void initialize(size_t npages, bool isLargeObject) nothrow
3554     {
3555         assert(npages >= 256);
3556 
3557         this.isLargeObject = isLargeObject;
3558         size_t poolsize;
3559 
3560         shiftBy = isLargeObject ? ShiftBy.Large : ShiftBy.Small;
3561 
3562         //debug(PRINTF) printf("Pool::Pool(%u)\n", npages);
3563         poolsize = npages * PAGESIZE;
3564         baseAddr = cast(byte *)os_mem_map(poolsize);
3565 
3566         // Some of the code depends on page alignment of memory pools
3567         assert((cast(size_t)baseAddr & (PAGESIZE - 1)) == 0);
3568 
3569         if (!baseAddr)
3570         {
3571             //debug(PRINTF) printf("GC fail: poolsize = x%zx, errno = %d\n", poolsize, errno);
3572             //debug(PRINTF) printf("message = '%s'\n", sys_errlist[errno]);
3573 
3574             npages = 0;
3575             poolsize = 0;
3576         }
3577         //assert(baseAddr);
3578         topAddr = baseAddr + poolsize;
3579         auto nbits = cast(size_t)poolsize >> shiftBy;
3580 
3581         version (COLLECT_FORK)
3582             mark.alloc(nbits, config.fork);
3583         else
3584             mark.alloc(nbits);
3585         if (ConservativeGC.isPrecise)
3586         {
3587             if (isLargeObject)
3588             {
3589                 rtinfo = cast(immutable(size_t)**)cstdlib.malloc(npages * (size_t*).sizeof);
3590                 if (!rtinfo)
3591                     onOutOfMemoryErrorNoGC();
3592                 memset(rtinfo, 0, npages * (size_t*).sizeof);
3593             }
3594             else
3595             {
3596                 is_pointer.alloc(cast(size_t)poolsize/(void*).sizeof);
3597                 is_pointer.clrRange(0, is_pointer.nbits);
3598             }
3599         }
3600 
3601         // pagetable already keeps track of what's free for the large object
3602         // pool.
3603         if (!isLargeObject)
3604         {
3605             freebits.alloc(nbits);
3606             freebits.setRange(0, nbits);
3607         }
3608 
3609         noscan.alloc(nbits);
3610         appendable.alloc(nbits);
3611 
3612         pagetable = cast(Bins*)cstdlib.malloc(npages * Bins.sizeof);
3613         if (!pagetable)
3614             onOutOfMemoryErrorNoGC();
3615 
3616         if (npages > 0)
3617         {
3618             bPageOffsets = cast(uint*)cstdlib.malloc(npages * uint.sizeof);
3619             if (!bPageOffsets)
3620                 onOutOfMemoryErrorNoGC();
3621 
3622             if (isLargeObject)
3623             {
3624                 bPageOffsets[0] = cast(uint)npages;
3625                 bPageOffsets[npages-1] = cast(uint)npages;
3626             }
3627             else
3628             {
3629                 // all pages free
3630                 foreach (n; 0..npages)
3631                     binPageChain[n] = cast(uint)(n + 1);
3632                 recoverPageFirst[] = cast(uint)npages;
3633             }
3634         }
3635 
3636         memset(pagetable, Bins.B_FREE, npages);
3637 
3638         this.npages = npages;
3639         this.freepages = npages;
3640         this.searchStart = 0;
3641         this.largestFree = npages;
3642     }
3643 
3644 
3645     void Dtor() nothrow
3646     {
3647         if (baseAddr)
3648         {
3649             int result;
3650 
3651             if (npages)
3652             {
3653                 result = os_mem_unmap(baseAddr, npages * PAGESIZE);
3654                 assert(result == 0);
3655                 npages = 0;
3656             }
3657 
3658             baseAddr = null;
3659             topAddr = null;
3660         }
3661         if (pagetable)
3662         {
3663             cstdlib.free(pagetable);
3664             pagetable = null;
3665         }
3666 
3667         if (bPageOffsets)
3668         {
3669             cstdlib.free(bPageOffsets);
3670             bPageOffsets = null;
3671         }
3672 
3673         mark.Dtor(config.fork);
3674         if (ConservativeGC.isPrecise)
3675         {
3676             if (isLargeObject)
3677                 cstdlib.free(rtinfo);
3678             else
3679                 is_pointer.Dtor();
3680         }
3681         if (isLargeObject)
3682         {
3683             nointerior.Dtor();
3684         }
3685         else
3686         {
3687             freebits.Dtor();
3688         }
3689         finals.Dtor();
3690         structFinals.Dtor();
3691         noscan.Dtor();
3692         appendable.Dtor();
3693     }
3694 
3695     /**
3696     *
3697     */
3698     uint getBits(size_t biti) nothrow
3699     {
3700         uint bits;
3701 
3702         if (finals.nbits && finals.test(biti))
3703             bits |= BlkAttr.FINALIZE;
3704         if (structFinals.nbits && structFinals.test(biti))
3705             bits |= BlkAttr.STRUCTFINAL;
3706         if (noscan.test(biti))
3707             bits |= BlkAttr.NO_SCAN;
3708         if (nointerior.nbits && nointerior.test(biti))
3709             bits |= BlkAttr.NO_INTERIOR;
3710         if (appendable.test(biti))
3711             bits |= BlkAttr.APPENDABLE;
3712         return bits;
3713     }
3714 
3715     /**
3716      *
3717      */
3718     void clrBits(size_t biti, uint mask) nothrow @nogc
3719     {
3720         immutable dataIndex =  biti >> GCBits.BITS_SHIFT;
3721         immutable bitOffset = biti & GCBits.BITS_MASK;
3722         immutable keep = ~(GCBits.BITS_1 << bitOffset);
3723 
3724         if (mask & BlkAttr.FINALIZE && finals.nbits)
3725             finals.data[dataIndex] &= keep;
3726 
3727         if (structFinals.nbits && (mask & BlkAttr.STRUCTFINAL))
3728             structFinals.data[dataIndex] &= keep;
3729 
3730         if (mask & BlkAttr.NO_SCAN)
3731             noscan.data[dataIndex] &= keep;
3732         if (mask & BlkAttr.APPENDABLE)
3733             appendable.data[dataIndex] &= keep;
3734         if (nointerior.nbits && (mask & BlkAttr.NO_INTERIOR))
3735             nointerior.data[dataIndex] &= keep;
3736     }
3737 
3738     /**
3739      *
3740      */
3741     void setBits(size_t biti, uint mask) nothrow
3742     {
3743         // Calculate the mask and bit offset once and then use it to
3744         // set all of the bits we need to set.
3745         immutable dataIndex = biti >> GCBits.BITS_SHIFT;
3746         immutable bitOffset = biti & GCBits.BITS_MASK;
3747         immutable orWith = GCBits.BITS_1 << bitOffset;
3748 
3749         if (mask & BlkAttr.STRUCTFINAL)
3750         {
3751             if (!structFinals.nbits)
3752                 structFinals.alloc(mark.nbits);
3753             structFinals.data[dataIndex] |= orWith;
3754         }
3755 
3756         if (mask & BlkAttr.FINALIZE)
3757         {
3758             if (!finals.nbits)
3759                 finals.alloc(mark.nbits);
3760             finals.data[dataIndex] |= orWith;
3761         }
3762 
3763         if (mask & BlkAttr.NO_SCAN)
3764         {
3765             noscan.data[dataIndex] |= orWith;
3766         }
3767 //        if (mask & BlkAttr.NO_MOVE)
3768 //        {
3769 //            if (!nomove.nbits)
3770 //                nomove.alloc(mark.nbits);
3771 //            nomove.data[dataIndex] |= orWith;
3772 //        }
3773         if (mask & BlkAttr.APPENDABLE)
3774         {
3775             appendable.data[dataIndex] |= orWith;
3776         }
3777 
3778         if (isLargeObject && (mask & BlkAttr.NO_INTERIOR))
3779         {
3780             if (!nointerior.nbits)
3781                 nointerior.alloc(mark.nbits);
3782             nointerior.data[dataIndex] |= orWith;
3783         }
3784     }
3785 
3786     void freePageBits(size_t pagenum, const scope ref PageBits toFree) nothrow
3787     {
3788         assert(!isLargeObject);
3789         assert(!nointerior.nbits); // only for large objects
3790 
3791         import core.internal.traits : staticIota;
3792         immutable beg = pagenum * (PAGESIZE / 16 / GCBits.BITS_PER_WORD);
3793         foreach (i; staticIota!(0, PageBits.length))
3794         {
3795             immutable w = toFree[i];
3796             if (!w) continue;
3797 
3798             immutable wi = beg + i;
3799             freebits.data[wi] |= w;
3800             noscan.data[wi] &= ~w;
3801             appendable.data[wi] &= ~w;
3802         }
3803 
3804         if (finals.nbits)
3805         {
3806             foreach (i; staticIota!(0, PageBits.length))
3807                 if (toFree[i])
3808                     finals.data[beg + i] &= ~toFree[i];
3809         }
3810 
3811         if (structFinals.nbits)
3812         {
3813             foreach (i; staticIota!(0, PageBits.length))
3814                 if (toFree[i])
3815                     structFinals.data[beg + i] &= ~toFree[i];
3816         }
3817     }
3818 
3819     void freeAllPageBits(size_t pagenum) nothrow
3820     {
3821         assert(!isLargeObject);
3822         assert(!nointerior.nbits); // only for large objects
3823 
3824         immutable beg = pagenum * PageBits.length;
3825         static foreach (i; 0 .. PageBits.length)
3826         {{
3827             immutable w = beg + i;
3828             freebits.data[w] = ~0;
3829             noscan.data[w] = 0;
3830             appendable.data[w] = 0;
3831             if (finals.data)
3832                 finals.data[w] = 0;
3833             if (structFinals.data)
3834                 structFinals.data[w] = 0;
3835         }}
3836     }
3837 
3838     /**
3839      * Given a pointer p in the p, return the pagenum.
3840      */
3841     size_t pagenumOf(void *p) const nothrow @nogc
3842     in
3843     {
3844         assert(p >= baseAddr);
3845         assert(p < topAddr);
3846     }
3847     do
3848     {
3849         return cast(size_t)(p - baseAddr) / PAGESIZE;
3850     }
3851 
3852     public
3853     @property bool isFree() const scope @safe pure nothrow @nogc
3854     {
3855         return npages == freepages;
3856     }
3857 
3858     /**
3859      * Return number of pages necessary for an allocation of the given size
3860      *
3861      * returns size_t.max if more than uint.max pages are requested
3862      * (return type is still size_t to avoid truncation when being used
3863      *  in calculations, e.g. npages * PAGESIZE)
3864      */
3865     static size_t numPages(size_t size) nothrow @nogc
3866     {
3867         version (D_LP64)
3868         {
3869             if (size > PAGESIZE * cast(size_t)uint.max)
3870                 return size_t.max;
3871         }
3872         else
3873         {
3874             if (size > size_t.max - PAGESIZE)
3875                 return size_t.max;
3876         }
3877         return (size + PAGESIZE - 1) / PAGESIZE;
3878     }
3879 
3880     void* findBase(void* p) nothrow @nogc
3881     {
3882         size_t offset = cast(size_t)(p - baseAddr);
3883         size_t pn = offset / PAGESIZE;
3884         Bins   bin = pagetable[pn];
3885 
3886         // Adjust bit to be at start of allocated memory block
3887         if (bin < Bins.B_NUMSMALL)
3888         {
3889             auto baseOff = baseOffset(offset, bin);
3890             const biti = baseOff >> Pool.ShiftBy.Small;
3891             if (freebits.test (biti))
3892                 return null;
3893             return baseAddr + baseOff;
3894         }
3895         if (bin == Bins.B_PAGE)
3896         {
3897             return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
3898         }
3899         if (bin == Bins.B_PAGEPLUS)
3900         {
3901             size_t pageOffset = bPageOffsets[pn];
3902             offset -= pageOffset * PAGESIZE;
3903             pn -= pageOffset;
3904 
3905             return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
3906         }
3907         // we are in a B_FREE page
3908         assert(bin == Bins.B_FREE);
3909         return null;
3910     }
3911 
3912     size_t slGetSize(void* p) nothrow @nogc
3913     {
3914         if (isLargeObject)
3915             return (cast(LargeObjectPool*)&this).getPages(p) * PAGESIZE;
3916         else
3917             return (cast(SmallObjectPool*)&this).getSize(p);
3918     }
3919 
3920     BlkInfo slGetInfo(void* p) nothrow
3921     {
3922         if (isLargeObject)
3923             return (cast(LargeObjectPool*)&this).getInfo(p);
3924         else
3925             return (cast(SmallObjectPool*)&this).getInfo(p);
3926     }
3927 
3928 
3929     void Invariant() const {}
3930 
3931     debug(INVARIANT)
3932     invariant()
3933     {
3934         if (baseAddr)
3935         {
3936             //if (baseAddr + npages * PAGESIZE != topAddr)
3937                 //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr);
3938             assert(baseAddr + npages * PAGESIZE == topAddr);
3939         }
3940 
3941         if (pagetable !is null)
3942         {
3943             for (size_t i = 0; i < npages; i++)
3944             {
3945                 Bins bin = pagetable[i];
3946                 assert(bin < Bins.B_MAX);
3947             }
3948         }
3949     }
3950 
3951     void setPointerBitmapSmall(void* p, size_t s, size_t allocSize, uint attr, const TypeInfo ti) nothrow
3952     {
3953         if (!(attr & BlkAttr.NO_SCAN))
3954             setPointerBitmap(p, s, allocSize, ti, attr);
3955     }
3956 
3957     pragma(inline,false)
3958     void setPointerBitmap(void* p, size_t s, size_t allocSize, const TypeInfo ti, uint attr) nothrow
3959     {
3960         size_t offset = p - baseAddr;
3961         //debug(PRINTF) printGCBits(&pool.is_pointer);
3962 
3963         debug(PRINTF)
3964             printf("Setting a pointer bitmap for %s at %p + %llu\n", debugTypeName(ti).ptr, p, cast(ulong)s);
3965 
3966         if (ti)
3967         {
3968             if (attr & BlkAttr.APPENDABLE)
3969             {
3970                 // an array of classes is in fact an array of pointers
3971                 if (typeid(ti) is typeid(TypeInfo_Class))
3972                     goto L_conservative;
3973                 s = allocSize;
3974             }
3975 
3976             auto rtInfo = cast(const(size_t)*)ti.rtInfo();
3977 
3978             if (rtInfo is rtinfoNoPointers)
3979             {
3980                 debug(PRINTF) printf("\tCompiler generated rtInfo: no pointers\n");
3981                 is_pointer.clrRange(offset/(void*).sizeof, s/(void*).sizeof);
3982             }
3983             else if (rtInfo is rtinfoHasPointers)
3984             {
3985                 debug(PRINTF) printf("\tCompiler generated rtInfo: has pointers\n");
3986                 is_pointer.setRange(offset/(void*).sizeof, s/(void*).sizeof);
3987             }
3988             else
3989             {
3990                 const(size_t)* bitmap = cast (size_t*) rtInfo;
3991                 //first element of rtInfo is the size of the object the bitmap encodes
3992                 size_t element_size = * bitmap;
3993                 bitmap++;
3994                 size_t tocopy;
3995                 if (attr & BlkAttr.APPENDABLE)
3996                 {
3997                     tocopy = s/(void*).sizeof;
3998                     is_pointer.copyRangeRepeating(offset/(void*).sizeof, tocopy, bitmap, element_size/(void*).sizeof);
3999                 }
4000                 else
4001                 {
4002                     tocopy = (s < element_size ? s : element_size)/(void*).sizeof;
4003                     is_pointer.copyRange(offset/(void*).sizeof, tocopy, bitmap);
4004                 }
4005 
4006                 debug(PRINTF) printf("\tSetting bitmap for new object (%s)\n\t\tat %p\t\tcopying from %p + %llu: ",
4007                                      debugTypeName(ti).ptr, p, bitmap, cast(ulong)element_size);
4008                 debug(PRINTF)
4009                     for (size_t i = 0; i < element_size/((void*).sizeof); i++)
4010                         printf("%d", (bitmap[i/(8*size_t.sizeof)] >> (i%(8*size_t.sizeof))) & 1);
4011                 debug(PRINTF) printf("\n");
4012 
4013                 if (tocopy * (void*).sizeof < s) // better safe than sorry: if allocated more, assume pointers inside
4014                 {
4015                     debug(PRINTF) printf("    Appending %d pointer bits\n", s/(void*).sizeof - tocopy);
4016                     is_pointer.setRange(offset/(void*).sizeof + tocopy, s/(void*).sizeof - tocopy);
4017                 }
4018             }
4019 
4020             if (s < allocSize)
4021             {
4022                 offset = (offset + s + (void*).sizeof - 1) & ~((void*).sizeof - 1);
4023                 is_pointer.clrRange(offset/(void*).sizeof, (allocSize - s)/(void*).sizeof);
4024             }
4025         }
4026         else
4027         {
4028         L_conservative:
4029             // limit pointers to actual size of allocation? might fail for arrays that append
4030             // without notifying the GC
4031             s = allocSize;
4032 
4033             debug(PRINTF) printf("Allocating a block without TypeInfo\n");
4034             is_pointer.setRange(offset/(void*).sizeof, s/(void*).sizeof);
4035         }
4036         //debug(PRINTF) printGCBits(&pool.is_pointer);
4037     }
4038 }
4039 
4040 struct LargeObjectPool
4041 {
4042     Pool base;
4043     alias base this;
4044 
4045     debug(INVARIANT)
4046     void Invariant()
4047     {
4048         //base.Invariant();
4049         for (size_t n = 0; n < npages; )
4050         {
4051             uint np = bPageOffsets[n];
4052             assert(np > 0 && np <= npages - n);
4053 
4054             if (pagetable[n] == Bins.B_PAGE)
4055             {
4056                 for (uint p = 1; p < np; p++)
4057                 {
4058                     assert(pagetable[n + p] == Bins.B_PAGEPLUS);
4059                     assert(bPageOffsets[n + p] == p);
4060                 }
4061             }
4062             else if (pagetable[n] == Bins.B_FREE)
4063             {
4064                 for (uint p = 1; p < np; p++)
4065                 {
4066                     assert(pagetable[n + p] == Bins.B_FREE);
4067                 }
4068                 assert(bPageOffsets[n + np - 1] == np);
4069             }
4070             else
4071                 assert(false);
4072             n += np;
4073         }
4074     }
4075 
4076     /**
4077      * Allocate n pages from Pool.
4078      * Returns OPFAIL on failure.
4079      */
4080     size_t allocPages(size_t n) nothrow
4081     {
4082         if (largestFree < n || searchStart + n > npages)
4083             return OPFAIL;
4084 
4085         //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n);
4086         size_t largest = 0;
4087         if (pagetable[searchStart] == Bins.B_PAGEPLUS)
4088         {
4089             searchStart -= bPageOffsets[searchStart]; // jump to B_PAGE
4090             searchStart += bPageOffsets[searchStart];
4091         }
4092         while (searchStart < npages && pagetable[searchStart] == Bins.B_PAGE)
4093             searchStart += bPageOffsets[searchStart];
4094 
4095         for (size_t i = searchStart; i < npages; )
4096         {
4097             assert(pagetable[i] == Bins.B_FREE);
4098 
4099             auto p = bPageOffsets[i];
4100             if (p > n)
4101             {
4102                 setFreePageOffsets(i + n, p - n);
4103                 goto L_found;
4104             }
4105             if (p == n)
4106             {
4107             L_found:
4108                 pagetable[i] = Bins.B_PAGE;
4109                 bPageOffsets[i] = cast(uint) n;
4110                 if (n > 1)
4111                 {
4112                     memset(&pagetable[i + 1], Bins.B_PAGEPLUS, n - 1);
4113                     for (auto offset = 1; offset < n; offset++)
4114                         bPageOffsets[i + offset] = cast(uint) offset;
4115                 }
4116                 freepages -= n;
4117                 return i;
4118             }
4119             if (p > largest)
4120                 largest = p;
4121 
4122             i += p;
4123             while (i < npages && pagetable[i] == Bins.B_PAGE)
4124             {
4125                 // we have the size information, so we skip a whole bunch of pages.
4126                 i += bPageOffsets[i];
4127             }
4128         }
4129 
4130         // not enough free pages found, remember largest free chunk
4131         largestFree = largest;
4132         return OPFAIL;
4133     }
4134 
4135     /**
4136      * Free npages pages starting with pagenum.
4137      */
4138     void freePages(size_t pagenum, size_t npages) nothrow @nogc
4139     {
4140         //memset(&pagetable[pagenum], B_FREE, npages);
4141         if (pagenum < searchStart)
4142             searchStart = pagenum;
4143 
4144         for (size_t i = pagenum; i < npages + pagenum; i++)
4145         {
4146             assert(pagetable[i] < Bins.B_FREE);
4147             pagetable[i] = Bins.B_FREE;
4148         }
4149         freepages += npages;
4150         largestFree = freepages; // invalidate
4151     }
4152 
4153     /**
4154      * Set the first and the last entry of a B_FREE block to the size
4155      */
4156     void setFreePageOffsets(size_t page, size_t num) nothrow @nogc
4157     {
4158         assert(pagetable[page] == Bins.B_FREE);
4159         assert(pagetable[page + num - 1] == Bins.B_FREE);
4160         bPageOffsets[page] = cast(uint)num;
4161         if (num > 1)
4162             bPageOffsets[page + num - 1] = cast(uint)num;
4163     }
4164 
4165     void mergeFreePageOffsets(bool bwd, bool fwd)(size_t page, size_t num) nothrow @nogc
4166     {
4167         static if (bwd)
4168         {
4169             if (page > 0 && pagetable[page - 1] == Bins.B_FREE)
4170             {
4171                 auto sz = bPageOffsets[page - 1];
4172                 page -= sz;
4173                 num += sz;
4174             }
4175         }
4176         static if (fwd)
4177         {
4178             if (page + num < npages && pagetable[page + num] == Bins.B_FREE)
4179                 num += bPageOffsets[page + num];
4180         }
4181         setFreePageOffsets(page, num);
4182     }
4183 
4184     /**
4185      * Get pages of allocation at pointer p in pool.
4186      */
4187     size_t getPages(void *p) const nothrow @nogc
4188     in
4189     {
4190         assert(p >= baseAddr);
4191         assert(p < topAddr);
4192     }
4193     do
4194     {
4195         if (cast(size_t)p & (PAGESIZE - 1)) // check for interior pointer
4196             return 0;
4197         size_t pagenum = pagenumOf(p);
4198         Bins bin = pagetable[pagenum];
4199         if (bin != Bins.B_PAGE)
4200             return 0;
4201         return bPageOffsets[pagenum];
4202     }
4203 
4204     /**
4205     * Get size of allocation at page pn in pool.
4206     */
4207     size_t getSize(size_t pn) const nothrow @nogc
4208     {
4209         assert(pagetable[pn] == Bins.B_PAGE);
4210         return cast(size_t) bPageOffsets[pn] * PAGESIZE;
4211     }
4212 
4213     /**
4214     *
4215     */
4216     BlkInfo getInfo(void* p) nothrow
4217     {
4218         BlkInfo info;
4219 
4220         size_t offset = cast(size_t)(p - baseAddr);
4221         size_t pn = offset / PAGESIZE;
4222         Bins bin = pagetable[pn];
4223 
4224         if (bin == Bins.B_PAGEPLUS)
4225             pn -= bPageOffsets[pn];
4226         else if (bin != Bins.B_PAGE)
4227             return info;           // no info for free pages
4228 
4229         info.base = baseAddr + pn * PAGESIZE;
4230         info.size = getSize(pn);
4231         info.attr = getBits(pn);
4232         return info;
4233     }
4234 
4235     void runFinalizers(const scope void[] segment) nothrow
4236     {
4237         foreach (pn; 0 .. npages)
4238         {
4239             Bins bin = pagetable[pn];
4240             if (bin > Bins.B_PAGE)
4241                 continue;
4242             size_t biti = pn;
4243 
4244             if (!finals.test(biti))
4245                 continue;
4246 
4247             auto p = sentinel_add(baseAddr + pn * PAGESIZE);
4248             size_t size = sentinel_size(p, getSize(pn));
4249             uint attr = getBits(biti);
4250 
4251             if (!rt_hasFinalizerInSegment(p, size, attr, segment))
4252                 continue;
4253 
4254             rt_finalizeFromGC(p, size, attr);
4255 
4256             clrBits(biti, ~BlkAttr.NONE);
4257 
4258             if (pn < searchStart)
4259                 searchStart = pn;
4260 
4261             debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
4262             //log_free(sentinel_add(p));
4263 
4264             size_t n = 1;
4265             for (; pn + n < npages; ++n)
4266                 if (pagetable[pn + n] != Bins.B_PAGEPLUS)
4267                     break;
4268             debug (MEMSTOMP) memset(baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE);
4269             freePages(pn, n);
4270             mergeFreePageOffsets!(true, true)(pn, n);
4271         }
4272     }
4273 }
4274 
4275 
4276 struct SmallObjectPool
4277 {
4278     Pool base;
4279     alias base this;
4280 
4281     debug(INVARIANT)
4282     void Invariant()
4283     {
4284         //base.Invariant();
4285         uint cntRecover = 0;
4286         foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL)
4287         {
4288             for (auto pn = recoverPageFirst[bin]; pn < npages; pn = binPageChain[pn])
4289             {
4290                 assert(pagetable[pn] == bin);
4291                 cntRecover++;
4292             }
4293         }
4294         uint cntFree = 0;
4295         for (auto pn = searchStart; pn < npages; pn = binPageChain[pn])
4296         {
4297             assert(pagetable[pn] == Bins.B_FREE);
4298             cntFree++;
4299         }
4300         assert(cntFree == freepages);
4301         assert(cntFree + cntRecover <= npages);
4302     }
4303 
4304     /**
4305     * Get size of pointer p in pool.
4306     */
4307     size_t getSize(void *p) const nothrow @nogc
4308     in
4309     {
4310         assert(p >= baseAddr);
4311         assert(p < topAddr);
4312     }
4313     do
4314     {
4315         size_t pagenum = pagenumOf(p);
4316         Bins bin = pagetable[pagenum];
4317         assert(bin < Bins.B_PAGE);
4318         if (p != cast(void*)baseOffset(cast(size_t)p, bin)) // check for interior pointer
4319             return 0;
4320         const biti = cast(size_t)(p - baseAddr) >> ShiftBy.Small;
4321         if (freebits.test (biti))
4322             return 0;
4323         return binsize[bin];
4324     }
4325 
4326     BlkInfo getInfo(void* p) nothrow
4327     {
4328         BlkInfo info;
4329         size_t offset = cast(size_t)(p - baseAddr);
4330         size_t pn = offset / PAGESIZE;
4331         Bins   bin = pagetable[pn];
4332 
4333         if (bin >= Bins.B_PAGE)
4334             return info;
4335 
4336         auto base = cast(void*)baseOffset(cast(size_t)p, bin);
4337         const biti = cast(size_t)(base - baseAddr) >> ShiftBy.Small;
4338         if (freebits.test (biti))
4339             return info;
4340 
4341         info.base = base;
4342         info.size = binsize[bin];
4343         offset = info.base - baseAddr;
4344         info.attr = getBits(biti);
4345 
4346         return info;
4347     }
4348 
4349     void runFinalizers(const scope void[] segment) nothrow
4350     {
4351         foreach (pn; 0 .. npages)
4352         {
4353             Bins bin = pagetable[pn];
4354             if (bin >= Bins.B_PAGE)
4355                 continue;
4356 
4357             immutable size = binsize[bin];
4358             auto p = baseAddr + pn * PAGESIZE;
4359             const ptop = p + PAGESIZE - size + 1;
4360             immutable base = pn * (PAGESIZE/16);
4361             immutable bitstride = size / 16;
4362 
4363             bool freeBits;
4364             PageBits toFree;
4365 
4366             for (size_t i; p < ptop; p += size, i += bitstride)
4367             {
4368                 immutable biti = base + i;
4369 
4370                 if (!finals.test(biti))
4371                     continue;
4372 
4373                 auto q = sentinel_add(p);
4374                 uint attr = getBits(biti);
4375                 const ssize = sentinel_size(q, size);
4376                 if (!rt_hasFinalizerInSegment(q, ssize, attr, segment))
4377                     continue;
4378 
4379                 rt_finalizeFromGC(q, ssize, attr);
4380 
4381                 freeBits = true;
4382                 toFree.set(i);
4383 
4384                 debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p);
4385                 //log_free(sentinel_add(p));
4386 
4387                 debug (MEMSTOMP) memset(p, 0xF3, size);
4388             }
4389 
4390             if (freeBits)
4391                 freePageBits(pn, toFree);
4392         }
4393     }
4394 
4395     /**
4396     * Allocate a page of bin's.
4397     * Returns:
4398     *           head of a single linked list of new entries
4399     */
4400     List* allocPage(Bins bin) nothrow
4401     {
4402         if (searchStart >= npages)
4403             return null;
4404 
4405         assert(pagetable[searchStart] == Bins.B_FREE);
4406 
4407     L1:
4408         size_t pn = searchStart;
4409         searchStart = binPageChain[searchStart];
4410         binPageChain[pn] = Pool.PageRecovered;
4411         pagetable[pn] = bin;
4412         freepages--;
4413 
4414         // Convert page to free list
4415         size_t size = binsize[bin];
4416         void* p = baseAddr + pn * PAGESIZE;
4417         auto first = cast(List*) p;
4418 
4419         // ensure 2 <size> bytes blocks are available below ptop, one
4420         //  being set in the loop, and one for the tail block
4421         void* ptop = p + PAGESIZE - 2 * size + 1;
4422         for (; p < ptop; p += size)
4423         {
4424             (cast(List *)p).next = cast(List *)(p + size);
4425             (cast(List *)p).pool = &base;
4426         }
4427         (cast(List *)p).next = null;
4428         (cast(List *)p).pool = &base;
4429         return first;
4430     }
4431 }
4432 
4433 debug(SENTINEL) {} else // no additional capacity with SENTINEL
4434 unittest // bugzilla 14467
4435 {
4436     int[] arr = new int[10];
4437     assert(arr.capacity);
4438     arr = arr[$..$];
4439     assert(arr.capacity);
4440 }
4441 
4442 unittest // bugzilla 15353
4443 {
4444     import core.memory : GC;
4445 
4446     static struct Foo
4447     {
4448         ~this()
4449         {
4450             GC.free(buf); // ignored in finalizer
4451         }
4452 
4453         void* buf;
4454     }
4455     new Foo(GC.malloc(10));
4456     GC.collect();
4457 }
4458 
4459 unittest // bugzilla 15822
4460 {
4461     import core.memory : GC;
4462 
4463     __gshared ubyte[16] buf;
4464     static struct Foo
4465     {
4466         ~this()
4467         {
4468             GC.removeRange(ptr);
4469             GC.removeRoot(ptr);
4470         }
4471 
4472         ubyte* ptr;
4473     }
4474     GC.addRoot(buf.ptr);
4475     GC.addRange(buf.ptr, buf.length);
4476     new Foo(buf.ptr);
4477     GC.collect();
4478 }
4479 
4480 unittest // bugzilla 1180
4481 {
4482     import core.exception;
4483     try
4484     {
4485         size_t x = size_t.max - 100;
4486         byte[] big_buf = new byte[x];
4487     }
4488     catch (OutOfMemoryError)
4489     {
4490     }
4491 }
4492 
4493 /* ============================ PRINTF =============================== */
4494 
4495 debug(PRINTF_TO_FILE)
4496 {
4497     private __gshared MonoTime gcStartTick;
4498     private __gshared FILE* gcx_fh;
4499     private __gshared bool hadNewline = false;
4500     import core.internal.spinlock;
4501     static printLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy);
4502 
4503     private int printf(ARGS...)(const char* fmt, ARGS args) nothrow
4504     {
4505         printLock.lock();
4506         scope(exit) printLock.unlock();
4507 
4508         if (!gcx_fh)
4509             gcx_fh = fopen("gcx.log", "w");
4510         if (!gcx_fh)
4511             return 0;
4512 
4513         int len;
4514         if (MonoTime.ticksPerSecond == 0)
4515         {
4516             len = fprintf(gcx_fh, "before init: ");
4517         }
4518         else if (hadNewline)
4519         {
4520             if (gcStartTick == MonoTime.init)
4521                 gcStartTick = MonoTime.currTime;
4522             immutable timeElapsed = MonoTime.currTime - gcStartTick;
4523             immutable secondsAsDouble = timeElapsed.total!"hnsecs" / cast(double)convert!("seconds", "hnsecs")(1);
4524             len = fprintf(gcx_fh, "%10.6f: ", secondsAsDouble);
4525         }
4526         len += fprintf(gcx_fh, fmt, args);
4527         fflush(gcx_fh);
4528         import core.stdc.string;
4529         hadNewline = fmt && fmt[0] && fmt[strlen(fmt) - 1] == '\n';
4530         return len;
4531     }
4532 }
4533 
4534 debug(PRINTF) void printFreeInfo(Pool* pool) nothrow
4535 {
4536     uint nReallyFree;
4537     foreach (i; 0..pool.npages) {
4538         if (pool.pagetable[i] >= Bins.B_FREE) nReallyFree++;
4539     }
4540 
4541     printf("Pool %p:  %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages);
4542 }
4543 
4544 debug(PRINTF)
4545 void printGCBits(GCBits* bits)
4546 {
4547     for (size_t i = 0; i < bits.nwords; i++)
4548     {
4549         if (i % 32 == 0) printf("\n\t");
4550         printf("%x ", bits.data[i]);
4551     }
4552     printf("\n");
4553 }
4554 
4555 // we can assume the name is always from a literal, so it is zero terminated
4556 debug(PRINTF)
4557 string debugTypeName(const(TypeInfo) ti) nothrow
4558 {
4559     string name;
4560     if (ti is null)
4561         name = "null";
4562     else if (auto ci = cast(TypeInfo_Class)ti)
4563         name = ci.name;
4564     else if (auto si = cast(TypeInfo_Struct)ti)
4565         name = si.mangledName; // .name() might GC-allocate, avoid deadlock
4566     else if (auto ci = cast(TypeInfo_Const)ti)
4567         static if (__traits(compiles,ci.base)) // different whether compiled with object.di or object.d
4568             return debugTypeName(ci.base);
4569         else
4570             return debugTypeName(ci.next);
4571     else
4572         name = typeid(ti).name;
4573     return name;
4574 }
4575 
4576 /* ======================= Leak Detector =========================== */
4577 
4578 debug (LOGGING)
4579 {
4580     struct Log
4581     {
4582         void*  p;
4583         size_t size;
4584         size_t line;
4585         char*  file;
4586         void*  parent;
4587 
4588         void print() nothrow
4589         {
4590             printf("    p = %p, size = %lld, parent = %p ", p, cast(ulong)size, parent);
4591             if (file)
4592             {
4593                 printf("%s(%u)", file, cast(uint)line);
4594             }
4595             printf("\n");
4596         }
4597     }
4598 
4599 
4600     struct LogArray
4601     {
4602         size_t dim;
4603         size_t allocdim;
4604         Log *data;
4605 
4606         void Dtor() nothrow @nogc
4607         {
4608             if (data)
4609                 cstdlib.free(data);
4610             data = null;
4611         }
4612 
4613         void reserve(size_t nentries) nothrow @nogc
4614         {
4615             assert(dim <= allocdim);
4616             if (allocdim - dim < nentries)
4617             {
4618                 allocdim = (dim + nentries) * 2;
4619                 assert(dim + nentries <= allocdim);
4620                 if (!data)
4621                 {
4622                     data = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
4623                     if (!data && allocdim)
4624                         onOutOfMemoryErrorNoGC();
4625                 }
4626                 else
4627                 {   Log *newdata;
4628 
4629                     newdata = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
4630                     if (!newdata && allocdim)
4631                         onOutOfMemoryErrorNoGC();
4632                     memcpy(newdata, data, dim * Log.sizeof);
4633                     cstdlib.free(data);
4634                     data = newdata;
4635                 }
4636             }
4637         }
4638 
4639 
4640         void push(Log log) nothrow @nogc
4641         {
4642             reserve(1);
4643             data[dim++] = log;
4644         }
4645 
4646         void remove(size_t i) nothrow @nogc
4647         {
4648             memmove(data + i, data + i + 1, (dim - i) * Log.sizeof);
4649             dim--;
4650         }
4651 
4652 
4653         size_t find(void *p) nothrow @nogc
4654         {
4655             for (size_t i = 0; i < dim; i++)
4656             {
4657                 if (data[i].p == p)
4658                     return i;
4659             }
4660             return OPFAIL; // not found
4661         }
4662 
4663 
4664         void copy(LogArray *from) nothrow @nogc
4665         {
4666             if (allocdim < from.dim)
4667                 reserve(from.dim - dim);
4668             assert(from.dim <= allocdim);
4669             memcpy(data, from.data, from.dim * Log.sizeof);
4670             dim = from.dim;
4671         }
4672     }
4673 
4674     struct LeakDetector
4675     {
4676         Gcx* gcx;
4677         LogArray current;
4678         LogArray prev;
4679 
4680         private void initialize(Gcx* gc)
4681         {
4682             gcx = gc;
4683             //debug(PRINTF) printf("+log_init()\n");
4684             current.reserve(1000);
4685             prev.reserve(1000);
4686             //debug(PRINTF) printf("-log_init()\n");
4687         }
4688 
4689 
4690         private void log_malloc(void *p, size_t size) nothrow
4691         {
4692             //debug(PRINTF) printf("+log_malloc(p = %p, size = %zd)\n", p, size);
4693             Log log;
4694 
4695             log.p = p;
4696             log.size = size;
4697             log.line = ConservativeGC.line;
4698             log.file = ConservativeGC.file;
4699             log.parent = null;
4700 
4701             ConservativeGC.line = 0;
4702             ConservativeGC.file = null;
4703 
4704             current.push(log);
4705             //debug(PRINTF) printf("-log_malloc()\n");
4706         }
4707 
4708 
4709         private void log_free(void *p, size_t size) nothrow @nogc
4710         {
4711             //debug(PRINTF) printf("+log_free(%p)\n", p);
4712             auto i = current.find(p);
4713             if (i == OPFAIL)
4714             {
4715                 debug(PRINTF) printf("free'ing unallocated memory %p (size %zu)\n", p, size);
4716             }
4717             else
4718                 current.remove(i);
4719             //debug(PRINTF) printf("-log_free()\n");
4720         }
4721 
4722 
4723         private void log_collect() nothrow
4724         {
4725             //debug(PRINTF) printf("+log_collect()\n");
4726             // Print everything in current that is not in prev
4727 
4728             debug(PRINTF) printf("New pointers this cycle: --------------------------------\n");
4729             size_t used = 0;
4730             for (size_t i = 0; i < current.dim; i++)
4731             {
4732                 auto j = prev.find(current.data[i].p);
4733                 if (j == OPFAIL)
4734                     current.data[i].print();
4735                 else
4736                     used++;
4737             }
4738 
4739             debug(PRINTF) printf("All roots this cycle: --------------------------------\n");
4740             for (size_t i = 0; i < current.dim; i++)
4741             {
4742                 void* p = current.data[i].p;
4743                 if (!gcx.findPool(current.data[i].parent))
4744                 {
4745                     auto j = prev.find(current.data[i].p);
4746                     debug(PRINTF) printf(j == OPFAIL ? "N" : " ");
4747                     current.data[i].print();
4748                 }
4749             }
4750 
4751             debug(PRINTF) printf("Used = %d-------------------------------------------------\n", used);
4752             prev.copy(&current);
4753 
4754             debug(PRINTF) printf("-log_collect()\n");
4755         }
4756 
4757 
4758         private void log_parent(void *p, void *parent) nothrow
4759         {
4760             //debug(PRINTF) printf("+log_parent()\n");
4761             auto i = current.find(p);
4762             if (i == OPFAIL)
4763             {
4764                 debug(PRINTF) printf("parent'ing unallocated memory %p, parent = %p\n", p, parent);
4765                 Pool *pool;
4766                 pool = gcx.findPool(p);
4767                 assert(pool);
4768                 size_t offset = cast(size_t)(p - pool.baseAddr);
4769                 size_t biti;
4770                 size_t pn = offset / PAGESIZE;
4771                 Bins bin = pool.pagetable[pn];
4772                 biti = (offset & (PAGESIZE - 1)) >> pool.shiftBy;
4773                 debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti);
4774             }
4775             else
4776             {
4777                 current.data[i].parent = parent;
4778             }
4779             //debug(PRINTF) printf("-log_parent()\n");
4780         }
4781     }
4782 }
4783 else
4784 {
4785     struct LeakDetector
4786     {
4787         static void initialize(Gcx* gcx) nothrow { }
4788         static void log_malloc(void *p, size_t size) nothrow { }
4789         static void log_free(void *p, size_t size) nothrow @nogc {}
4790         static void log_collect() nothrow { }
4791         static void log_parent(void *p, void *parent) nothrow { }
4792     }
4793 }
4794 
4795 /* ============================ SENTINEL =============================== */
4796 
4797 debug (SENTINEL)
4798 {
4799     // pre-sentinel must be smaller than 16 bytes so that the same GC bits
4800     //  are used for the allocated pointer and the user pointer
4801     // so use uint for both 32 and 64 bit platforms, limiting usage to < 4GB
4802     const uint  SENTINEL_PRE = 0xF4F4F4F4;
4803     const ubyte SENTINEL_POST = 0xF5;           // 8 bits
4804     const uint  SENTINEL_EXTRA = 2 * uint.sizeof + 1;
4805 
4806 
4807     inout(uint*)  sentinel_psize(inout void *p) nothrow @nogc { return &(cast(inout uint *)p)[-2]; }
4808     inout(uint*)  sentinel_pre(inout void *p)   nothrow @nogc { return &(cast(inout uint *)p)[-1]; }
4809     inout(ubyte*) sentinel_post(inout void *p)  nothrow @nogc { return &(cast(inout ubyte *)p)[*sentinel_psize(p)]; }
4810 
4811 
4812     void sentinel_init(void *p, size_t size) nothrow @nogc
4813     {
4814         assert(size <= uint.max);
4815         *sentinel_psize(p) = cast(uint)size;
4816         *sentinel_pre(p) = SENTINEL_PRE;
4817         *sentinel_post(p) = SENTINEL_POST;
4818     }
4819 
4820 
4821     void sentinel_Invariant(const void *p) nothrow @nogc
4822     {
4823         debug
4824         {
4825             assert(*sentinel_pre(p) == SENTINEL_PRE);
4826             assert(*sentinel_post(p) == SENTINEL_POST);
4827         }
4828         else if (*sentinel_pre(p) != SENTINEL_PRE || *sentinel_post(p) != SENTINEL_POST)
4829             onInvalidMemoryOperationError(); // also trigger in release build
4830     }
4831 
4832     size_t sentinel_size(const void *p, size_t alloc_size) nothrow @nogc
4833     {
4834         return *sentinel_psize(p);
4835     }
4836 
4837     void *sentinel_add(void *p) nothrow @nogc
4838     {
4839         return p + 2 * uint.sizeof;
4840     }
4841 
4842 
4843     void *sentinel_sub(void *p) nothrow @nogc
4844     {
4845         return p - 2 * uint.sizeof;
4846     }
4847 }
4848 else
4849 {
4850     const uint SENTINEL_EXTRA = 0;
4851 
4852 
4853     void sentinel_init(void *p, size_t size) nothrow @nogc
4854     {
4855     }
4856 
4857 
4858     void sentinel_Invariant(const void *p) nothrow @nogc
4859     {
4860     }
4861 
4862     size_t sentinel_size(const void *p, size_t alloc_size) nothrow @nogc
4863     {
4864         return alloc_size;
4865     }
4866 
4867     void *sentinel_add(void *p) nothrow @nogc
4868     {
4869         return p;
4870     }
4871 
4872 
4873     void *sentinel_sub(void *p) nothrow @nogc
4874     {
4875         return p;
4876     }
4877 }
4878 
4879 debug (MEMSTOMP)
4880 unittest
4881 {
4882     import core.memory;
4883     auto p = cast(size_t*)GC.malloc(size_t.sizeof*3);
4884     assert(*p == cast(size_t)0xF0F0F0F0F0F0F0F0);
4885     p[2] = 0; // First two will be used for free list
4886     GC.free(p);
4887     assert(p[2] == cast(size_t)0xF2F2F2F2F2F2F2F2);
4888 }
4889 
4890 debug (SENTINEL)
4891 unittest
4892 {
4893     import core.memory;
4894     auto p = cast(ubyte*)GC.malloc(1);
4895     assert(p[-1] == 0xF4);
4896     assert(p[ 1] == 0xF5);
4897 
4898     // See also stand-alone tests in test/gc
4899 }
4900 
4901 unittest
4902 {
4903     import core.memory;
4904 
4905     // https://issues.dlang.org/show_bug.cgi?id=9275
4906     GC.removeRoot(null);
4907     GC.removeRoot(cast(void*)13);
4908 }
4909 
4910 // improve predictability of coverage of code that is eventually not hit by other tests
4911 debug (SENTINEL) {} else // cannot extend with SENTINEL
4912 debug (MARK_PRINTF) {} else // takes forever
4913 unittest
4914 {
4915     import core.memory;
4916     auto p = GC.malloc(260 << 20); // new pool has 390 MB
4917     auto q = GC.malloc(65 << 20);  // next chunk (larger than 64MB to ensure the same pool is used)
4918     auto r = GC.malloc(65 << 20);  // another chunk in same pool
4919     assert(p + (260 << 20) == q);
4920     assert(q + (65 << 20) == r);
4921     GC.free(q);
4922     // should trigger "assert(bin == Bins.B_FREE);" in mark due to dangling pointer q:
4923     GC.collect();
4924     // should trigger "break;" in extendNoSync:
4925     size_t sz = GC.extend(p, 64 << 20, 66 << 20); // trigger size after p large enough (but limited)
4926     assert(sz == 325 << 20);
4927     GC.free(p);
4928     GC.free(r);
4929     r = q; // ensure q is not trashed before collection above
4930 
4931     p = GC.malloc(70 << 20); // from the same pool
4932     q = GC.malloc(70 << 20);
4933     r = GC.malloc(70 << 20);
4934     auto s = GC.malloc(70 << 20);
4935     auto t = GC.malloc(70 << 20); // 350 MB of 390 MB used
4936     assert(p + (70 << 20) == q);
4937     assert(q + (70 << 20) == r);
4938     assert(r + (70 << 20) == s);
4939     assert(s + (70 << 20) == t);
4940     GC.free(r); // ensure recalculation of largestFree in nxxt allocPages
4941     auto z = GC.malloc(75 << 20); // needs new pool
4942 
4943     GC.free(p);
4944     GC.free(q);
4945     GC.free(s);
4946     GC.free(t);
4947     GC.free(z);
4948     GC.minimize(); // release huge pool
4949 }
4950 
4951 // https://issues.dlang.org/show_bug.cgi?id=19281
4952 debug (SENTINEL) {} else // cannot allow >= 4 GB with SENTINEL
4953 debug (MEMSTOMP) {} else // might take too long to actually touch the memory
4954 version (D_LP64) unittest
4955 {
4956     static if (__traits(compiles, os_physical_mem))
4957     {
4958         // only run if the system has enough physical memory
4959         size_t sz = 2L^^32;
4960         //import core.stdc.stdio;
4961         //printf("availphys = %lld", os_physical_mem());
4962         if (os_physical_mem() > sz)
4963         {
4964             import core.memory;
4965             GC.collect();
4966             GC.minimize();
4967             auto stats = GC.stats();
4968             auto ptr = GC.malloc(sz, BlkAttr.NO_SCAN);
4969             auto info = GC.query(ptr);
4970             //printf("info.size = %lld", info.size);
4971             assert(info.size >= sz);
4972             GC.free(ptr);
4973             GC.minimize();
4974             auto nstats = GC.stats();
4975             assert(nstats.usedSize == stats.usedSize);
4976             assert(nstats.freeSize == stats.freeSize);
4977             assert(nstats.allocatedInCurrentThread - sz == stats.allocatedInCurrentThread);
4978         }
4979     }
4980 }
4981 
4982 // https://issues.dlang.org/show_bug.cgi?id=19522
4983 unittest
4984 {
4985     import core.memory;
4986 
4987     void test(void* p)
4988     {
4989         assert(GC.getAttr(p) == BlkAttr.NO_SCAN);
4990         assert(GC.setAttr(p + 4, BlkAttr.NO_SCAN) == 0); // interior pointer should fail
4991         assert(GC.clrAttr(p + 4, BlkAttr.NO_SCAN) == 0); // interior pointer should fail
4992         GC.free(p);
4993         assert(GC.query(p).base == null);
4994         assert(GC.query(p).size == 0);
4995         assert(GC.addrOf(p) == null);
4996         assert(GC.sizeOf(p) == 0); // fails
4997         assert(GC.getAttr(p) == 0);
4998         assert(GC.setAttr(p, BlkAttr.NO_SCAN) == 0);
4999         assert(GC.clrAttr(p, BlkAttr.NO_SCAN) == 0);
5000     }
5001     void* large = GC.malloc(10000, BlkAttr.NO_SCAN);
5002     test(large);
5003 
5004     void* small = GC.malloc(100, BlkAttr.NO_SCAN);
5005     test(small);
5006 }
5007 
5008 unittest
5009 {
5010     import core.memory;
5011 
5012     auto now = currTime;
5013     GC.ProfileStats stats1 = GC.profileStats();
5014     GC.collect();
5015     GC.ProfileStats stats2 = GC.profileStats();
5016     auto diff = currTime - now;
5017 
5018     assert(stats2.totalCollectionTime - stats1.totalCollectionTime <= diff);
5019     assert(stats2.totalPauseTime - stats1.totalPauseTime <= stats2.totalCollectionTime - stats1.totalCollectionTime);
5020 
5021     assert(stats2.maxPauseTime >= stats1.maxPauseTime);
5022     assert(stats2.maxCollectionTime >= stats1.maxCollectionTime);
5023 }
5024 
5025 // https://issues.dlang.org/show_bug.cgi?id=20214
5026 unittest
5027 {
5028     import core.memory;
5029     import core.stdc.stdio;
5030 
5031     // allocate from large pool
5032     auto o = GC.malloc(10);
5033     auto p = (cast(void**)GC.malloc(4096 * (void*).sizeof))[0 .. 4096];
5034     auto q = (cast(void**)GC.malloc(4096 * (void*).sizeof))[0 .. 4096];
5035     if (p.ptr + p.length is q.ptr)
5036     {
5037         q[] = o; // fill with pointers
5038 
5039         // shrink, unused area cleared?
5040         auto nq = (cast(void**)GC.realloc(q.ptr, 4000 * (void*).sizeof))[0 .. 4000];
5041         assert(q.ptr is nq.ptr);
5042         assert(q.ptr[4095] !is o);
5043 
5044         GC.free(q.ptr);
5045         // expected to extend in place
5046         auto np = (cast(void**)GC.realloc(p.ptr, 4200 * (void*).sizeof))[0 .. 4200];
5047         assert(p.ptr is np.ptr);
5048         assert(q.ptr[4200] !is o);
5049     }
5050     else
5051     {
5052         // adjacent allocations likely but not guaranteed
5053         printf("unexpected pointers %p and %p\n", p.ptr, q.ptr);
5054     }
5055 }
5056