]> git.llucax.com Git - software/dgc/cdgc.git/blob - rt/gc/cdgc/gc.d
Try to keep the memory usage low more aggressively
[software/dgc/cdgc.git] / rt / gc / cdgc / gc.d
1 /**
2  * This module contains the garbage collector implementation.
3  *
4  * Copyright: Copyright (C) 2001-2007 Digital Mars, www.digitalmars.com.
5  *            All rights reserved.
6  * License:
7  *  This software is provided 'as-is', without any express or implied
8  *  warranty. In no event will the authors be held liable for any damages
9  *  arising from the use of this software.
10  *
11  *  Permission is granted to anyone to use this software for any purpose,
12  *  including commercial applications, and to alter it and redistribute it
13  *  freely, in both source and binary form, subject to the following
14  *  restrictions:
15  *
16  *  o  The origin of this software must not be misrepresented; you must not
17  *     claim that you wrote the original software. If you use this software
18  *     in a product, an acknowledgment in the product documentation would be
19  *     appreciated but is not required.
20  *  o  Altered source versions must be plainly marked as such, and must not
21  *     be misrepresented as being the original software.
22  *  o  This notice may not be removed or altered from any source
23  *     distribution.
24  * Authors:   Walter Bright, David Friedman, Sean Kelly
25  */
26
27 module rt.gc.cdgc.gc;
28
29 // D Programming Language Garbage Collector implementation
30
31 /************** Debugging ***************************/
32
33 //debug = COLLECT_PRINTF;       // turn on printf's
34 //debug = PTRCHECK;             // more pointer checking
35 //debug = PTRCHECK2;            // thorough but slow pointer checking
36
37 /*************** Configuration *********************/
38
39 version = STACKGROWSDOWN;       // growing the stack means subtracting from the stack pointer
40                                 // (use for Intel X86 CPUs)
41                                 // else growing the stack means adding to the stack pointer
42
43 /***************************************************/
44
45 import rt.gc.cdgc.bits: GCBits;
46 import rt.gc.cdgc.stats: GCStats, Stats;
47 import dynarray = rt.gc.cdgc.dynarray;
48 import os = rt.gc.cdgc.os;
49 import opts = rt.gc.cdgc.opts;
50
51 import cstdlib = tango.stdc.stdlib;
52 import cstring = tango.stdc.string;
53 import cstdio = tango.stdc.stdio;
54 debug(COLLECT_PRINTF) alias cstdio.printf printf;
55
56 /*
57  * This is a small optimization that proved it's usefulness. For small chunks
58  * or memory memset() seems to be slower (probably because of the call) that
59  * simply doing a simple loop to set the memory.
60  */
61 void memset(void* dst, int c, size_t n)
62 {
63     // This number (32) has been determined empirically
64     if (n > 32) {
65         cstring.memset(dst, c, n);
66         return;
67     }
68     auto p = cast(ubyte*)(dst);
69     while (n-- > 0)
70         *p++ = c;
71 }
72
73 version (GNU)
74 {
75     // BUG: The following import will likely not work, since the gcc
76     //      subdirectory is elsewhere.  Instead, perhaps the functions
77     //      could be declared directly or some other resolution could
78     //      be found.
79     static import gcc.builtins; // for __builtin_unwind_int
80 }
81
82 struct BlkInfo
83 {
84     void*  base;
85     size_t size;
86     uint   attr;
87 }
88
89 package enum BlkAttr : uint
90 {
91     FINALIZE = 0b0000_0001,
92     NO_SCAN  = 0b0000_0010,
93     NO_MOVE  = 0b0000_0100,
94     ALL_BITS = 0b1111_1111
95 }
96
97 package bool has_pointermap(uint attrs)
98 {
99     return !opts.options.conservative && !(attrs & BlkAttr.NO_SCAN);
100 }
101
102 private size_t round_up(size_t n, size_t to)
103 {
104     return (n + to - 1) / to;
105 }
106
107 private
108 {
109     alias void delegate(Object) DEvent;
110     alias void delegate( void*, void* ) scanFn;
111     enum { OPFAIL = ~cast(size_t)0 }
112
113     extern (C)
114     {
115         version (DigitalMars) version(OSX)
116             oid _d_osx_image_init();
117
118         void* rt_stackBottom();
119         void* rt_stackTop();
120         void rt_finalize( void* p, bool det = true );
121         void rt_attachDisposeEvent(Object h, DEvent e);
122         bool rt_detachDisposeEvent(Object h, DEvent e);
123         void rt_scanStaticData( scanFn scan );
124
125         void thread_init();
126         bool thread_needLock();
127         void thread_suspendAll();
128         void thread_resumeAll();
129         void thread_scanAll( scanFn fn, void* curStackTop = null );
130
131         void onOutOfMemoryError();
132     }
133 }
134
135
136 enum
137 {
138     PAGESIZE =    4096,
139     POOLSIZE =   (4096*256),
140 }
141
142
143 enum
144 {
145     B_16,
146     B_32,
147     B_64,
148     B_128,
149     B_256,
150     B_512,
151     B_1024,
152     B_2048,
153     B_PAGE,             // start of large alloc
154     B_PAGEPLUS,         // continuation of large alloc
155     B_FREE,             // free page
156     B_MAX
157 }
158
159
160 alias ubyte Bins;
161
162
163 struct List
164 {
165     List* next;
166     Pool* pool;
167 }
168
169
170 struct Range
171 {
172     void *pbot;
173     void *ptop;
174     int opCmp(in Range other)
175     {
176         if (pbot < other.pbot)
177             return -1;
178         else
179         return cast(int)(pbot > other.pbot);
180     }
181 }
182
183
184 const uint binsize[B_MAX] = [ 16,32,64,128,256,512,1024,2048,4096 ];
185 const uint notbinsize[B_MAX] = [ ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1),
186                                 ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) ];
187
188
189 /* ============================ GC =============================== */
190
191
192 class GCLock {} // just a dummy so we can get a global lock
193
194
195 struct GC
196 {
197     // global lock
198     ClassInfo lock;
199
200     void* p_cache;
201     size_t size_cache;
202
203     // !=0 means don't scan stack
204     uint no_stack;
205     bool any_changes;
206     void* stack_bottom;
207     uint inited;
208     /// Turn off collections if > 0
209     int disabled;
210
211     // PID of the fork()ed process doing the mark() (0 if is not running)
212     int mark_proc_pid;
213
214     /// min(pool.baseAddr)
215     byte *min_addr;
216     /// max(pool.topAddr)
217     byte *max_addr;
218
219     /// Free list for each size
220     List*[B_MAX] free_list;
221
222     dynarray.DynArray!(void*) roots;
223     dynarray.DynArray!(Range) ranges;
224     dynarray.DynArray!(Pool*) pools;
225
226     Stats stats;
227 }
228
229 // call locked if necessary
230 private T locked(T, alias Code)()
231 {
232     if (thread_needLock())
233         synchronized (gc.lock) return Code();
234     else
235        return Code();
236 }
237
238 private GC* gc;
239
240 bool Invariant()
241 {
242     assert (gc !is null);
243     if (gc.inited) {
244         for (size_t i = 0; i < gc.pools.length; i++) {
245             Pool* pool = gc.pools[i];
246             pool.Invariant();
247             if (i == 0)
248                 assert(gc.min_addr == pool.baseAddr);
249             if (i + 1 < gc.pools.length)
250                 assert(*pool < *gc.pools[i + 1]);
251             else if (i + 1 == gc.pools.length)
252                 assert(gc.max_addr == pool.topAddr);
253         }
254
255         gc.roots.Invariant();
256         gc.ranges.Invariant();
257
258         for (size_t i = 0; i < gc.ranges.length; i++) {
259             assert(gc.ranges[i].pbot);
260             assert(gc.ranges[i].ptop);
261             assert(gc.ranges[i].pbot <= gc.ranges[i].ptop);
262         }
263
264         for (size_t i = 0; i < B_PAGE; i++) {
265             for (List *list = gc.free_list[i]; list; list = list.next) {
266                 auto pool = list.pool;
267                 assert (pool !is null);
268                 auto p = cast(byte*) list;
269                 assert (p >= pool.baseAddr);
270                 assert (p < pool.topAddr);
271                 assert (pool.freebits.test((p - pool.baseAddr) / 16));
272             }
273         }
274     }
275     return true;
276 }
277
278
279 /**
280  * Find Pool that pointer is in.
281  * Return null if not in a Pool.
282  * Assume pools is sorted.
283  */
284 Pool* findPool(void* p)
285 {
286     if (p < gc.min_addr || p >= gc.max_addr)
287         return null;
288     if (gc.pools.length == 0)
289         return null;
290     if (gc.pools.length == 1)
291         return gc.pools[0];
292     /// The pooltable[] is sorted by address, so do a binary search
293     size_t low = 0;
294     size_t high = gc.pools.length - 1;
295     while (low <= high) {
296         size_t mid = (low + high) / 2;
297         auto pool = gc.pools[mid];
298         if (p < pool.baseAddr)
299             high = mid - 1;
300         else if (p >= pool.topAddr)
301             low = mid + 1;
302         else
303             return pool;
304     }
305     // Not found
306     return null;
307 }
308
309
310 /**
311  * Determine the base address of the block containing p.  If p is not a gc
312  * allocated pointer, return null.
313  */
314 BlkInfo getInfo(void* p)
315 {
316     assert (p !is null);
317     Pool* pool = findPool(p);
318     if (pool is null)
319         return BlkInfo.init;
320     BlkInfo info;
321     info.base = pool.findBase(p);
322     if (info.base is null)
323         return BlkInfo.init;
324     info.size = pool.findSize(info.base);
325     size_t bit_i = (info.base - pool.baseAddr) / 16;
326     info.attr = getAttr(pool, bit_i);
327     if (has_pointermap(info.attr)) {
328         info.size -= size_t.sizeof; // PointerMap bitmask
329         // Points to the PointerMap bitmask pointer, not user data
330         if (p >= (info.base + info.size)) {
331             return BlkInfo.init;
332         }
333     }
334     if (opts.options.sentinel) {
335         info.base = sentinel_add(info.base);
336         // points to sentinel data, not user data
337         if (p < info.base || p >= sentinel_post(info.base))
338             return BlkInfo.init;
339         info.size -= SENTINEL_EXTRA;
340     }
341     return info;
342 }
343
344
345 /**
346  * Compute bin for size.
347  */
348 Bins findBin(size_t size)
349 {
350     Bins bin;
351     if (size <= 256)
352     {
353         if (size <= 64)
354         {
355             if (size <= 16)
356                 bin = B_16;
357             else if (size <= 32)
358                 bin = B_32;
359             else
360                 bin = B_64;
361         }
362         else
363         {
364             if (size <= 128)
365                 bin = B_128;
366             else
367                 bin = B_256;
368         }
369     }
370     else
371     {
372         if (size <= 1024)
373         {
374             if (size <= 512)
375                 bin = B_512;
376             else
377                 bin = B_1024;
378         }
379         else
380         {
381             if (size <= 2048)
382                 bin = B_2048;
383             else
384                 bin = B_PAGE;
385         }
386     }
387     return bin;
388 }
389
390
391 /**
392  * Allocate a new pool of at least size bytes.
393  * Sort it into pools.
394  * Mark all memory in the pool as B_FREE.
395  * Return the actual number of bytes reserved or 0 on error.
396  */
397 size_t reserve(size_t size)
398 {
399     assert(size != 0);
400     size_t npages = round_up(size, PAGESIZE);
401     Pool*  pool = newPool(npages);
402
403     if (!pool)
404         return 0;
405     return pool.npages * PAGESIZE;
406 }
407
408
409 /**
410  * Minimizes physical memory usage by returning free pools to the OS.
411  */
412 void minimize()
413 {
414     // Disabled if a parallel collection is in progress because the shared mark
415     // bits of the freed pool might be used by the mark process
416     if (gc.mark_proc_pid != 0)
417         return;
418
419     if (gc.pools.length == 0)
420         return;
421
422     size_t n;
423     size_t pn;
424     Pool* pool;
425
426     for (n = 0; n < gc.pools.length; n++)
427     {
428         pool = gc.pools[n];
429         for (pn = 0; pn < pool.npages; pn++)
430         {
431             if (cast(Bins)pool.pagetable[pn] != B_FREE)
432                 break;
433         }
434         if (pn < pool.npages)
435             continue;
436         pool.Dtor();
437         cstdlib.free(pool);
438         gc.pools.remove_at(n);
439         n--;
440     }
441     gc.min_addr = gc.pools[0].baseAddr;
442     gc.max_addr = gc.pools[gc.pools.length - 1].topAddr;
443 }
444
445
446 /**
447  * Allocate a chunk of memory that is larger than a page.
448  * Return null if out of memory.
449  */
450 void* bigAlloc(size_t npages, out Pool* pool, size_t* pn)
451 {
452     // This code could use some refinement when repeatedly
453     // allocating very large arrays.
454
455     void* find_block()
456     {
457         for (size_t n = 0; n < gc.pools.length; n++)
458         {
459             pool = gc.pools[n];
460             *pn = pool.allocPages(npages);
461             if (*pn != OPFAIL)
462                 return pool.baseAddr + *pn * PAGESIZE;
463         }
464         return null;
465     }
466
467     void* alloc_more()
468     {
469         // Release empty pools to prevent bloat
470         minimize();
471         // Allocate new pool
472         pool = newPool(npages);
473         if (!pool)
474             return null; // let malloc handle the error
475         *pn = pool.allocPages(npages);
476         assert(*pn != OPFAIL);
477         return pool.baseAddr + *pn * PAGESIZE;
478     }
479
480     if (void* p = find_block())
481         return p;
482
483     if (gc.disabled)
484         return alloc_more();
485
486     // Try collecting
487     size_t freedpages = fullcollectshell();
488     if (freedpages >= gc.pools.length * ((POOLSIZE / PAGESIZE) / 4)) {
489         if (void* p = find_block())
490             return p;
491     }
492
493     return alloc_more();
494 }
495
496
497 /**
498  * Allocate a new pool with at least npages in it.
499  * Sort it into pools.
500  * Return null if failed.
501  */
502 Pool *newPool(size_t npages)
503 {
504     // Minimum of POOLSIZE
505     if (npages < POOLSIZE/PAGESIZE)
506         npages = POOLSIZE/PAGESIZE;
507     else if (npages > POOLSIZE/PAGESIZE)
508     {
509         // Give us 150% of requested size, so there's room to extend
510         auto n = npages + (npages >> 1);
511         if (n < size_t.max/PAGESIZE)
512             npages = n;
513     }
514
515     // Allocate successively larger pools up to 8 megs
516     if (gc.pools.length)
517     {
518         size_t n = gc.pools.length;
519         if (n > 8)
520             n = 8;                  // cap pool size at 8 megs
521         n *= (POOLSIZE / PAGESIZE);
522         if (npages < n)
523             npages = n;
524     }
525
526     auto pool = cast(Pool*) cstdlib.calloc(1, Pool.sizeof);
527     if (pool is null)
528         return null;
529     pool.initialize(npages);
530     if (!pool.baseAddr)
531     {
532         pool.Dtor();
533         return null;
534     }
535
536     auto inserted_pool = *gc.pools.insert_sorted!("*a < *b")(pool);
537     if (inserted_pool is null) {
538         pool.Dtor();
539         return null;
540     }
541     assert (inserted_pool is pool);
542     gc.min_addr = gc.pools[0].baseAddr;
543     gc.max_addr = gc.pools[gc.pools.length - 1].topAddr;
544     return pool;
545 }
546
547
548 /**
549  * Allocate a page of bin's.
550  * Returns:
551  *  0       failed
552  */
553 int allocPage(Bins bin)
554 {
555     Pool*  pool;
556     size_t pn;
557
558     for (size_t n = 0; n < gc.pools.length; n++)
559     {
560         pool = gc.pools[n];
561         pn = pool.allocPages(1);
562         if (pn != OPFAIL)
563             goto L1;
564     }
565     return 0;               // failed
566
567   L1:
568     pool.pagetable[pn] = cast(ubyte)bin;
569
570     // Convert page to free list
571     size_t size = binsize[bin];
572     auto list_head = &gc.free_list[bin];
573
574     byte* p = pool.baseAddr + pn * PAGESIZE;
575     byte*  ptop = p + PAGESIZE;
576     size_t bit_i = pn * (PAGESIZE / 16);
577     pool.freebits.set_group(bit_i, PAGESIZE / 16);
578     for (; p < ptop; p += size)
579     {
580         List* l = cast(List *) p;
581         l.next = *list_head;
582         l.pool = pool;
583         *list_head = l;
584     }
585     return 1;
586 }
587
588
589 /**
590  * Search a range of memory values and mark any pointers into the GC pool using
591  * type information (bitmask of pointer locations).
592  */
593 void mark_range(void *pbot, void *ptop, size_t* pm_bitmask)
594 {
595     // TODO: make our own assert because assert uses the GC
596     assert (pbot <= ptop);
597
598     const BITS_PER_WORD = size_t.sizeof * 8;
599
600     void **p1 = cast(void **)pbot;
601     void **p2 = cast(void **)ptop;
602     size_t pcache = 0;
603     bool changes = false;
604
605     size_t type_size = pm_bitmask[0];
606     size_t* pm_bits = pm_bitmask + 1;
607     bool has_type_info = type_size != 1 || pm_bits[0] != 1 || pm_bits[1] != 0;
608
609     //printf("marking range: %p -> %p\n", pbot, ptop);
610     for (; p1 + type_size <= p2; p1 += type_size) {
611         for (size_t n = 0; n < type_size; n++) {
612             // scan bit set for this word
613             if (has_type_info &&
614                     !(pm_bits[n / BITS_PER_WORD] & (1 << (n % BITS_PER_WORD))))
615                 continue;
616
617             void* p = *(p1 + n);
618
619             if (p < gc.min_addr || p >= gc.max_addr)
620                 continue;
621
622             if ((cast(size_t)p & ~(PAGESIZE-1)) == pcache)
623                 continue;
624
625             Pool* pool = findPool(p);
626             if (pool)
627             {
628                 size_t offset = cast(size_t)(p - pool.baseAddr);
629                 size_t bit_i = void;
630                 size_t pn = offset / PAGESIZE;
631                 Bins   bin = cast(Bins)pool.pagetable[pn];
632
633                 // Cache B_PAGE, B_PAGEPLUS and B_FREE lookups
634                 if (bin >= B_PAGE)
635                     pcache = cast(size_t)p & ~(PAGESIZE-1);
636
637                 // Adjust bit to be at start of allocated memory block
638                 if (bin <= B_PAGE)
639                     bit_i = (offset & notbinsize[bin]) / 16;
640                 else if (bin == B_PAGEPLUS)
641                 {
642                     do
643                     {
644                         --pn;
645                     }
646                     while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
647                     bit_i = pn * (PAGESIZE / 16);
648                 }
649                 else // Don't mark bits in B_FREE pages
650                     continue;
651
652                 if (!pool.mark.test(bit_i))
653                 {
654                     pool.mark.set(bit_i);
655                     if (!pool.noscan.test(bit_i))
656                     {
657                         pool.scan.set(bit_i);
658                         changes = true;
659                     }
660                 }
661             }
662         }
663     }
664     if (changes)
665         gc.any_changes = true;
666 }
667
668 /**
669  * Return number of full pages free'd.
670  */
671 size_t fullcollectshell()
672 {
673     gc.stats.collection_started();
674     scope (exit)
675         gc.stats.collection_finished();
676
677     // The purpose of the 'shell' is to ensure all the registers
678     // get put on the stack so they'll be scanned
679     void *sp;
680     size_t result;
681     version (GNU)
682     {
683         gcc.builtins.__builtin_unwind_init();
684         sp = & sp;
685     }
686     else version(LDC)
687     {
688         version(X86)
689         {
690             uint eax,ecx,edx,ebx,ebp,esi,edi;
691             asm
692             {
693                 mov eax[EBP], EAX      ;
694                 mov ecx[EBP], ECX      ;
695                 mov edx[EBP], EDX      ;
696                 mov ebx[EBP], EBX      ;
697                 mov ebp[EBP], EBP      ;
698                 mov esi[EBP], ESI      ;
699                 mov edi[EBP], EDI      ;
700                 mov  sp[EBP], ESP      ;
701             }
702         }
703         else version (X86_64)
704         {
705             ulong rax,rbx,rcx,rdx,rbp,rsi,rdi,r8,r9,r10,r11,r12,r13,r14,r15;
706             asm
707             {
708                 movq rax[RBP], RAX      ;
709                 movq rbx[RBP], RBX      ;
710                 movq rcx[RBP], RCX      ;
711                 movq rdx[RBP], RDX      ;
712                 movq rbp[RBP], RBP      ;
713                 movq rsi[RBP], RSI      ;
714                 movq rdi[RBP], RDI      ;
715                 movq r8 [RBP], R8       ;
716                 movq r9 [RBP], R9       ;
717                 movq r10[RBP], R10      ;
718                 movq r11[RBP], R11      ;
719                 movq r12[RBP], R12      ;
720                 movq r13[RBP], R13      ;
721                 movq r14[RBP], R14      ;
722                 movq r15[RBP], R15      ;
723                 movq  sp[RBP], RSP      ;
724             }
725         }
726         else
727         {
728             static assert( false, "Architecture not supported." );
729         }
730     }
731     else
732     {
733     asm
734     {
735         pushad              ;
736         mov sp[EBP],ESP     ;
737     }
738     }
739     result = fullcollect(sp);
740     version (GNU)
741     {
742         // nothing to do
743     }
744     else version(LDC)
745     {
746         // nothing to do
747     }
748     else
749     {
750     asm
751     {
752         popad               ;
753     }
754     }
755     return result;
756 }
757
758
759 /**
760  *
761  */
762 size_t fullcollect(void *stackTop)
763 {
764     debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
765
766     // If eager allocation is used, we need to check first if there is a mark
767     // process running. If there isn't, we start a new one (see the next code
768     // block). If there is, we check if it's still running or already finished.
769     // If it's still running, we tell the caller process no memory has been
770     // recovered (it will allocated more to fulfill the current request).  If
771     // the mark process is done, we lunch the sweep phase and hope enough
772     // memory is freed (if that not the case, the caller will allocate more
773     // memory and the next time it's exhausted it will run a new collection).
774     if (opts.options.eager_alloc) {
775         if (gc.mark_proc_pid != 0) { // there is a mark process in progress
776             os.WRes r = os.wait_pid(gc.mark_proc_pid, false); // don't block
777             assert (r != os.WRes.ERROR);
778             switch (r) {
779             case os.WRes.DONE:
780                 debug(COLLECT_PRINTF) printf("\t\tmark proc DONE\n");
781                 gc.mark_proc_pid = 0;
782                 return sweep();
783             case os.WRes.RUNNING:
784                 debug(COLLECT_PRINTF) printf("\t\tmark proc RUNNING\n");
785                 return 0;
786             case os.WRes.ERROR:
787                 debug(COLLECT_PRINTF) printf("\t\tmark proc ERROR\n");
788                 disable_fork(); // Try to keep going without forking
789                 break;
790             }
791         }
792     }
793
794     // We always need to stop the world to make threads save the CPU registers
795     // in the stack and prepare themselves for thread_scanAll()
796     thread_suspendAll();
797     gc.stats.world_stopped();
798
799     // If forking is enabled, we fork() and start a new mark phase in the
800     // child. The parent process will tell the caller that no memory could be
801     // recycled if eager allocation is used, allowing the mutator to keep going
802     // almost instantly (at the expense of more memory consumption because
803     // a new allocation will be triggered to fulfill the current request). If
804     // no eager allocation is used, the parent will wait for the mark phase to
805     // finish before returning control to the mutator, but other threads are
806     // restarted and may run in parallel with the mark phase (unless they
807     // allocate or use the GC themselves, in which case the global GC lock will
808     // stop them).
809     if (opts.options.fork) {
810         cstdio.fflush(null); // avoid duplicated FILE* output
811         os.pid_t child_pid = os.fork();
812         assert (child_pid != -1); // don't accept errors in non-release mode
813         switch (child_pid) {
814         case -1: // if fork() fails, fall-back to stop-the-world
815             disable_fork();
816             break;
817         case 0: // child process (i.e. the collectors mark phase)
818             mark(stackTop);
819             cstdlib.exit(0);
820             break; // bogus, will never reach here
821         default: // parent process (i.e. the mutator)
822             // start the world again and wait for the mark phase to finish
823             thread_resumeAll();
824             gc.stats.world_started();
825             if (opts.options.eager_alloc) {
826                 gc.mark_proc_pid = child_pid;
827                 return 0;
828             }
829             os.WRes r = os.wait_pid(child_pid); // block until it finishes
830             assert (r == os.WRes.DONE);
831             debug(COLLECT_PRINTF) printf("\t\tmark proc DONE (block)\n");
832             if (r == os.WRes.DONE)
833                 return sweep();
834             debug(COLLECT_PRINTF) printf("\tmark() proc ERROR\n");
835             // If there was some error, try to keep going without forking
836             disable_fork();
837             // Re-suspend the threads to do the marking in this process
838             thread_suspendAll();
839             gc.stats.world_stopped();
840         }
841
842     }
843
844     // If we reach here, we are using the standard stop-the-world collection,
845     // either because fork was disabled in the first place, or because it was
846     // disabled because of some error.
847     mark(stackTop);
848     thread_resumeAll();
849     gc.stats.world_started();
850
851     return sweep();
852 }
853
854
855 /**
856  *
857  */
858 void mark(void *stackTop)
859 {
860     debug(COLLECT_PRINTF) printf("\tmark()\n");
861
862     gc.any_changes = false;
863
864     for (size_t n = 0; n < gc.pools.length; n++)
865     {
866         Pool* pool = gc.pools[n];
867         pool.mark.copy(&pool.freebits);
868         pool.scan.zero();
869     }
870
871     /// Marks a range of memory in conservative mode.
872     void mark_conservative_range(void* pbot, void* ptop)
873     {
874         mark_range(pbot, ptop, PointerMap.init.bits.ptr);
875     }
876
877     rt_scanStaticData(&mark_conservative_range);
878
879     if (!gc.no_stack)
880     {
881         // Scan stacks and registers for each paused thread
882         thread_scanAll(&mark_conservative_range, stackTop);
883     }
884
885     // Scan roots
886     debug(COLLECT_PRINTF) printf("scan roots[]\n");
887     mark_conservative_range(gc.roots.ptr, gc.roots.ptr + gc.roots.length);
888
889     // Scan ranges
890     debug(COLLECT_PRINTF) printf("scan ranges[]\n");
891     for (size_t n = 0; n < gc.ranges.length; n++)
892     {
893         debug(COLLECT_PRINTF) printf("\t%x .. %x\n", gc.ranges[n].pbot, gc.ranges[n].ptop);
894         mark_conservative_range(gc.ranges[n].pbot, gc.ranges[n].ptop);
895     }
896
897     debug(COLLECT_PRINTF) printf("\tscan heap\n");
898     while (gc.any_changes)
899     {
900         gc.any_changes = false;
901         for (size_t n = 0; n < gc.pools.length; n++)
902         {
903             uint *bbase;
904             uint *b;
905             uint *btop;
906
907             Pool* pool = gc.pools[n];
908
909             bbase = pool.scan.base();
910             btop = bbase + pool.scan.nwords;
911             for (b = bbase; b < btop;)
912             {
913                 Bins   bin;
914                 size_t pn;
915                 size_t u;
916                 size_t bitm;
917                 byte*  o;
918
919                 bitm = *b;
920                 if (!bitm)
921                 {
922                     b++;
923                     continue;
924                 }
925                 *b = 0;
926
927                 o = pool.baseAddr + (b - bbase) * 32 * 16;
928                 if (!(bitm & 0xFFFF))
929                 {
930                     bitm >>= 16;
931                     o += 16 * 16;
932                 }
933                 for (; bitm; o += 16, bitm >>= 1)
934                 {
935                     if (!(bitm & 1))
936                         continue;
937
938                     pn = cast(size_t)(o - pool.baseAddr) / PAGESIZE;
939                     bin = cast(Bins)pool.pagetable[pn];
940                     if (bin < B_PAGE) {
941                         if (opts.options.conservative)
942                             mark_conservative_range(o, o + binsize[bin]);
943                         else {
944                             auto end_of_blk = cast(size_t**)(o +
945                                     binsize[bin] - size_t.sizeof);
946                             size_t* pm_bitmask = *end_of_blk;
947                             mark_range(o, end_of_blk, pm_bitmask);
948                         }
949                     }
950                     else if (bin == B_PAGE || bin == B_PAGEPLUS)
951                     {
952                         if (bin == B_PAGEPLUS)
953                         {
954                             while (pool.pagetable[pn - 1] != B_PAGE)
955                                 pn--;
956                         }
957                         u = 1;
958                         while (pn + u < pool.npages &&
959                                 pool.pagetable[pn + u] == B_PAGEPLUS)
960                             u++;
961
962                         size_t blk_size = u * PAGESIZE;
963                         if (opts.options.conservative)
964                             mark_conservative_range(o, o + blk_size);
965                         else {
966                             auto end_of_blk = cast(size_t**)(o + blk_size -
967                                     size_t.sizeof);
968                             size_t* pm_bitmask = *end_of_blk;
969                             mark_range(o, end_of_blk, pm_bitmask);
970                         }
971                     }
972                 }
973             }
974         }
975     }
976 }
977
978
979 /**
980  *
981  */
982 size_t sweep()
983 {
984     // Free up everything not marked
985     debug(COLLECT_PRINTF) printf("\tsweep\n");
986     gc.p_cache = null;
987     gc.size_cache = 0;
988     size_t freedpages = 0;
989     size_t freed = 0;
990     for (size_t n = 0; n < gc.pools.length; n++)
991     {
992         Pool* pool = gc.pools[n];
993         pool.clear_cache();
994         uint*  bbase = pool.mark.base();
995         size_t pn;
996         for (pn = 0; pn < pool.npages; pn++, bbase += PAGESIZE / (32 * 16))
997         {
998             Bins bin = cast(Bins)pool.pagetable[pn];
999
1000             if (bin < B_PAGE)
1001             {
1002                 auto size = binsize[bin];
1003                 byte* p = pool.baseAddr + pn * PAGESIZE;
1004                 byte* ptop = p + PAGESIZE;
1005                 size_t bit_i = pn * (PAGESIZE/16);
1006                 size_t bit_stride = size / 16;
1007
1008 version(none) // BUG: doesn't work because freebits() must also be cleared
1009 {
1010                 // If free'd entire page
1011                 if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 &&
1012                         bbase[3] == 0 && bbase[4] == 0 && bbase[5] == 0 &&
1013                         bbase[6] == 0 && bbase[7] == 0)
1014                 {
1015                     for (; p < ptop; p += size, bit_i += bit_stride)
1016                     {
1017                         if (pool.finals.testClear(bit_i)) {
1018                             if (opts.options.sentinel)
1019                                 rt_finalize(sentinel_add(p), false/*gc.no_stack > 0*/);
1020                             else
1021                                 rt_finalize(p, false/*gc.no_stack > 0*/);
1022                         }
1023                         clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
1024
1025                         if (opts.options.mem_stomp)
1026                             memset(p, 0xF3, size);
1027                     }
1028                     pool.pagetable[pn] = B_FREE;
1029                     freed += PAGESIZE;
1030                     continue;
1031                 }
1032 }
1033                 for (; p < ptop; p += size, bit_i += bit_stride)
1034                 {
1035                     if (!pool.mark.test(bit_i))
1036                     {
1037                         if (opts.options.sentinel)
1038                             sentinel_Invariant(sentinel_add(p));
1039
1040                         pool.freebits.set(bit_i);
1041                         if (pool.finals.testClear(bit_i)) {
1042                             if (opts.options.sentinel)
1043                                 rt_finalize(sentinel_add(p), false/*gc.no_stack > 0*/);
1044                             else
1045                                 rt_finalize(p, false/*gc.no_stack > 0*/);
1046                         }
1047                         clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
1048
1049                         if (opts.options.mem_stomp)
1050                             memset(p, 0xF3, size);
1051
1052                         freed += size;
1053                     }
1054                 }
1055             }
1056             else if (bin == B_PAGE)
1057             {
1058                 size_t bit_stride = PAGESIZE / 16;
1059                 size_t bit_i = pn * bit_stride;
1060                 if (!pool.mark.test(bit_i))
1061                 {
1062                     byte *p = pool.baseAddr + pn * PAGESIZE;
1063                     if (opts.options.sentinel)
1064                         sentinel_Invariant(sentinel_add(p));
1065                     if (pool.finals.testClear(bit_i)) {
1066                         if (opts.options.sentinel)
1067                             rt_finalize(sentinel_add(p), false/*gc.no_stack > 0*/);
1068                         else
1069                             rt_finalize(p, false/*gc.no_stack > 0*/);
1070                     }
1071                     clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
1072
1073                     debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
1074                     pool.pagetable[pn] = B_FREE;
1075                     pool.freebits.set_group(bit_i, PAGESIZE / 16);
1076                     freedpages++;
1077                     if (opts.options.mem_stomp)
1078                         memset(p, 0xF3, PAGESIZE);
1079                     while (pn + 1 < pool.npages && pool.pagetable[pn + 1] == B_PAGEPLUS)
1080                     {
1081                         pn++;
1082                         pool.pagetable[pn] = B_FREE;
1083                         bit_i += bit_stride;
1084                         pool.freebits.set_group(bit_i, PAGESIZE / 16);
1085                         freedpages++;
1086
1087                         if (opts.options.mem_stomp)
1088                         {
1089                             p += PAGESIZE;
1090                             memset(p, 0xF3, PAGESIZE);
1091                         }
1092                     }
1093                 }
1094             }
1095         }
1096     }
1097
1098     // Zero buckets
1099     gc.free_list[] = null;
1100
1101     // Free complete pages, rebuild free list
1102     debug(COLLECT_PRINTF) printf("\tfree complete pages\n");
1103     size_t recoveredpages = 0;
1104     for (size_t n = 0; n < gc.pools.length; n++)
1105     {
1106         Pool* pool = gc.pools[n];
1107         for (size_t pn = 0; pn < pool.npages; pn++)
1108         {
1109             Bins   bin = cast(Bins)pool.pagetable[pn];
1110             size_t bit_i;
1111             size_t u;
1112
1113             if (bin < B_PAGE)
1114             {
1115                 size_t size = binsize[bin];
1116                 size_t bit_stride = size / 16;
1117                 size_t bit_base = pn * (PAGESIZE / 16);
1118                 size_t bit_top = bit_base + (PAGESIZE / 16);
1119                 byte*  p;
1120
1121                 bit_i = bit_base;
1122                 for (; bit_i < bit_top; bit_i += bit_stride)
1123                 {
1124                     if (!pool.freebits.test(bit_i))
1125                         goto Lnotfree;
1126                 }
1127                 pool.pagetable[pn] = B_FREE;
1128                 pool.freebits.set_group(bit_base, PAGESIZE / 16);
1129                 recoveredpages++;
1130                 continue;
1131
1132              Lnotfree:
1133                 p = pool.baseAddr + pn * PAGESIZE;
1134                 for (u = 0; u < PAGESIZE; u += size)
1135                 {
1136                     bit_i = bit_base + u / 16;
1137                     if (pool.freebits.test(bit_i))
1138                     {
1139                         assert ((p+u) >= pool.baseAddr);
1140                         assert ((p+u) < pool.topAddr);
1141                         List* list = cast(List*) (p + u);
1142                         // avoid unnecesary writes (it really saves time)
1143                         if (list.next != gc.free_list[bin])
1144                             list.next = gc.free_list[bin];
1145                         if (list.pool != pool)
1146                             list.pool = pool;
1147                         gc.free_list[bin] = list;
1148                     }
1149                 }
1150             }
1151         }
1152     }
1153
1154     debug(COLLECT_PRINTF) printf("recovered pages = %d\n", recoveredpages);
1155     debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, gc.pools.length);
1156
1157     return freedpages + recoveredpages;
1158 }
1159
1160
1161 /**
1162  *
1163  */
1164 uint getAttr(Pool* pool, size_t bit_i)
1165 in
1166 {
1167     assert( pool );
1168 }
1169 body
1170 {
1171     uint attrs;
1172     if (pool.finals.test(bit_i))
1173         attrs |= BlkAttr.FINALIZE;
1174     if (pool.noscan.test(bit_i))
1175         attrs |= BlkAttr.NO_SCAN;
1176 //        if (pool.nomove.test(bit_i))
1177 //            attrs |= BlkAttr.NO_MOVE;
1178     return attrs;
1179 }
1180
1181
1182 /**
1183  *
1184  */
1185 void setAttr(Pool* pool, size_t bit_i, uint mask)
1186 in
1187 {
1188     assert( pool );
1189 }
1190 body
1191 {
1192     if (mask & BlkAttr.FINALIZE)
1193     {
1194         pool.finals.set(bit_i);
1195     }
1196     if (mask & BlkAttr.NO_SCAN)
1197     {
1198         pool.noscan.set(bit_i);
1199     }
1200 //        if (mask & BlkAttr.NO_MOVE)
1201 //        {
1202 //            if (!pool.nomove.nbits)
1203 //                pool.nomove.alloc(pool.mark.nbits);
1204 //            pool.nomove.set(bit_i);
1205 //        }
1206 }
1207
1208
1209 /**
1210  *
1211  */
1212 void clrAttr(Pool* pool, size_t bit_i, uint mask)
1213 in
1214 {
1215     assert( pool );
1216 }
1217 body
1218 {
1219     if (mask & BlkAttr.FINALIZE)
1220         pool.finals.clear(bit_i);
1221     if (mask & BlkAttr.NO_SCAN)
1222         pool.noscan.clear(bit_i);
1223 //        if (mask & BlkAttr.NO_MOVE && pool.nomove.nbits)
1224 //            pool.nomove.clear(bit_i);
1225 }
1226
1227
1228 void disable_fork()
1229 {
1230     // we have to disable both options, as eager_alloc assumes fork is enabled
1231     opts.options.fork = false;
1232     opts.options.eager_alloc = false;
1233 }
1234
1235
1236 void initialize()
1237 {
1238     int dummy;
1239     gc.stack_bottom = cast(char*)&dummy;
1240     opts.parse(cstdlib.getenv("D_GC_OPTS"));
1241     // If we are going to fork, make sure we have the needed OS support
1242     if (opts.options.fork)
1243         opts.options.fork = os.HAVE_SHARED && os.HAVE_FORK;
1244     // Eager allocation is only possible when forking
1245     if (!opts.options.fork)
1246         opts.options.eager_alloc = false;
1247     gc.lock = GCLock.classinfo;
1248     gc.inited = 1;
1249     setStackBottom(rt_stackBottom());
1250     gc.stats = Stats(gc);
1251     if (opts.options.prealloc_npools) {
1252         size_t pages = round_up(opts.options.prealloc_psize, PAGESIZE);
1253         for (size_t i = 0; i < opts.options.prealloc_npools; ++i)
1254             newPool(pages);
1255     }
1256 }
1257
1258
1259 //
1260 //
1261 //
1262 private void *malloc(size_t size, uint attrs, size_t* pm_bitmask)
1263 {
1264     assert(size != 0);
1265
1266     gc.stats.malloc_started(size, attrs, pm_bitmask);
1267     scope (exit)
1268         gc.stats.malloc_finished(p);
1269
1270     void *p = null;
1271     Bins bin;
1272
1273     if (opts.options.sentinel)
1274         size += SENTINEL_EXTRA;
1275
1276     bool has_pm = has_pointermap(attrs);
1277     if (has_pm)
1278         size += size_t.sizeof;
1279
1280     // Compute size bin
1281     // Cache previous binsize lookup - Dave Fladebo.
1282     static size_t lastsize = -1;
1283     static Bins lastbin;
1284     if (size == lastsize)
1285         bin = lastbin;
1286     else
1287     {
1288         bin = findBin(size);
1289         lastsize = size;
1290         lastbin = bin;
1291     }
1292
1293     Pool* pool = void;
1294     size_t bit_i = void;
1295     size_t capacity = void; // to figure out where to store the bitmask
1296     if (bin < B_PAGE)
1297     {
1298         p = gc.free_list[bin];
1299         if (p is null)
1300         {
1301             if (!allocPage(bin) && !gc.disabled)   // try to find a new page
1302             {
1303                 if (!thread_needLock())
1304                 {
1305                     /* Then we haven't locked it yet. Be sure
1306                      * and gc.lock for a collection, since a finalizer
1307                      * may start a new thread.
1308                      */
1309                     synchronized (gc.lock)
1310                     {
1311                         fullcollectshell();
1312                     }
1313                 }
1314                 else if (!fullcollectshell())       // collect to find a new page
1315                 {
1316                     //newPool(1);
1317                 }
1318                 minimize();
1319             }
1320             if (!gc.free_list[bin] && !allocPage(bin))
1321             {
1322                 newPool(1);         // allocate new pool to find a new page
1323                 // TODO: hint allocPage() to use the pool we just created
1324                 int result = allocPage(bin);
1325                 if (!result)
1326                     onOutOfMemoryError();
1327             }
1328             p = gc.free_list[bin];
1329         }
1330         capacity = binsize[bin];
1331
1332         // Return next item from free list
1333         List* list = cast(List*) p;
1334         assert ((cast(byte*)list) >= list.pool.baseAddr);
1335         assert ((cast(byte*)list) < list.pool.topAddr);
1336         gc.free_list[bin] = list.next;
1337         pool = list.pool;
1338         bit_i = (p - pool.baseAddr) / 16;
1339         assert (pool.freebits.test(bit_i));
1340         pool.freebits.clear(bit_i);
1341         if (!(attrs & BlkAttr.NO_SCAN))
1342             memset(p + size, 0, capacity - size);
1343         if (opts.options.mem_stomp)
1344             memset(p, 0xF0, size);
1345     }
1346     else
1347     {
1348         size_t pn;
1349         size_t npages = round_up(size, PAGESIZE);
1350         p = bigAlloc(npages, pool, &pn);
1351         if (!p)
1352             onOutOfMemoryError();
1353         assert (pool !is null);
1354
1355         capacity = npages * PAGESIZE;
1356         bit_i = pn * (PAGESIZE / 16);
1357         pool.freebits.clear(bit_i);
1358         pool.pagetable[pn] = B_PAGE;
1359         if (npages > 1)
1360             memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1);
1361         p = pool.baseAddr + pn * PAGESIZE;
1362         memset(cast(char *)p + size, 0, npages * PAGESIZE - size);
1363         if (opts.options.mem_stomp)
1364             memset(p, 0xF1, size);
1365
1366     }
1367
1368     // Store the bit mask AFTER SENTINEL_POST
1369     // TODO: store it BEFORE, so the bitmask is protected too
1370     if (has_pm) {
1371         auto end_of_blk = cast(size_t**)(p + capacity - size_t.sizeof);
1372         *end_of_blk = pm_bitmask;
1373         size -= size_t.sizeof;
1374     }
1375
1376     if (opts.options.sentinel) {
1377         size -= SENTINEL_EXTRA;
1378         p = sentinel_add(p);
1379         sentinel_init(p, size);
1380     }
1381
1382     if (attrs) {
1383         setAttr(pool, bit_i, attrs);
1384         assert (bin >= B_PAGE || !pool.freebits.test(bit_i));
1385     }
1386
1387     return p;
1388 }
1389
1390
1391 //
1392 //
1393 //
1394 private void *calloc(size_t size, uint attrs, size_t* pm_bitmask)
1395 {
1396     assert(size != 0);
1397
1398     void *p = malloc(size, attrs, pm_bitmask);
1399     memset(p, 0, size);
1400     return p;
1401 }
1402
1403
1404 //
1405 //
1406 //
1407 private void *realloc(void *p, size_t size, uint attrs,
1408         size_t* pm_bitmask)
1409 {
1410     if (!size)
1411     {
1412         if (p)
1413         {
1414             free(p);
1415             p = null;
1416         }
1417     }
1418     else if (!p)
1419     {
1420         p = malloc(size, attrs, pm_bitmask);
1421     }
1422     else
1423     {
1424         Pool* pool = findPool(p);
1425         if (pool is null)
1426             return null;
1427
1428         // Set or retrieve attributes as appropriate
1429         auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
1430         if (attrs) {
1431             clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
1432             setAttr(pool, bit_i, attrs);
1433         }
1434         else
1435             attrs = getAttr(pool, bit_i);
1436
1437         void* blk_base_addr = pool.findBase(p);
1438         size_t blk_size = pool.findSize(p);
1439         bool has_pm = has_pointermap(attrs);
1440         size_t pm_bitmask_size = 0;
1441         if (has_pm) {
1442             pm_bitmask_size = size_t.sizeof;
1443             // Retrieve pointer map bit mask if appropriate
1444             if (pm_bitmask is null) {
1445                 auto end_of_blk = cast(size_t**)(blk_base_addr +
1446                         blk_size - size_t.sizeof);
1447                 pm_bitmask = *end_of_blk;
1448             }
1449         }
1450
1451         if (opts.options.sentinel)
1452         {
1453             sentinel_Invariant(p);
1454             size_t sentinel_stored_size = *sentinel_size(p);
1455             if (sentinel_stored_size != size)
1456             {
1457                 void* p2 = malloc(size, attrs, pm_bitmask);
1458                 if (sentinel_stored_size < size)
1459                     size = sentinel_stored_size;
1460                 cstring.memcpy(p2, p, size);
1461                 p = p2;
1462             }
1463         }
1464         else
1465         {
1466             size += pm_bitmask_size;
1467             if (blk_size >= PAGESIZE && size >= PAGESIZE)
1468             {
1469                 auto psz = blk_size / PAGESIZE;
1470                 auto newsz = round_up(size, PAGESIZE);
1471                 if (newsz == psz)
1472                     return p;
1473
1474                 auto pagenum = (p - pool.baseAddr) / PAGESIZE;
1475
1476                 if (newsz < psz)
1477                 {
1478                     // Shrink in place
1479                     if (opts.options.mem_stomp)
1480                         memset(p + size - pm_bitmask_size, 0xF2,
1481                                 blk_size - size - pm_bitmask_size);
1482                     pool.freePages(pagenum + newsz, psz - newsz);
1483                     auto new_blk_size = (PAGESIZE * newsz);
1484                     // update the size cache, assuming that is very likely the
1485                     // size of this block will be queried in the near future
1486                     pool.update_cache(p, new_blk_size);
1487                     if (has_pm) {
1488                         auto end_of_blk = cast(size_t**)(blk_base_addr +
1489                                 new_blk_size - pm_bitmask_size);
1490                         *end_of_blk = pm_bitmask;
1491                     }
1492                     return p;
1493                 }
1494                 else if (pagenum + newsz <= pool.npages)
1495                 {
1496                     // Attempt to expand in place
1497                     for (size_t i = pagenum + psz; 1;)
1498                     {
1499                         if (i == pagenum + newsz)
1500                         {
1501                             if (opts.options.mem_stomp)
1502                                 memset(p + blk_size - pm_bitmask_size,
1503                                         0xF0, size - blk_size
1504                                         - pm_bitmask_size);
1505                             memset(pool.pagetable + pagenum +
1506                                     psz, B_PAGEPLUS, newsz - psz);
1507                             auto new_blk_size = (PAGESIZE * newsz);
1508                             // update the size cache, assuming that is very
1509                             // likely the size of this block will be queried in
1510                             // the near future
1511                             pool.update_cache(p, new_blk_size);
1512                             if (has_pm) {
1513                                 auto end_of_blk = cast(size_t**)(
1514                                         blk_base_addr + new_blk_size -
1515                                         pm_bitmask_size);
1516                                 *end_of_blk = pm_bitmask;
1517                             }
1518                             return p;
1519                         }
1520                         if (i == pool.npages)
1521                         {
1522                             break;
1523                         }
1524                         if (pool.pagetable[i] != B_FREE)
1525                             break;
1526                         i++;
1527                     }
1528                 }
1529             }
1530             // if new size is bigger or less than half
1531             if (blk_size < size || blk_size > size * 2)
1532             {
1533                 size -= pm_bitmask_size;
1534                 blk_size -= pm_bitmask_size;
1535                 void* p2 = malloc(size, attrs, pm_bitmask);
1536                 if (blk_size < size)
1537                     size = blk_size;
1538                 cstring.memcpy(p2, p, size);
1539                 p = p2;
1540             }
1541         }
1542     }
1543     return p;
1544 }
1545
1546
1547 /**
1548  * Attempt to in-place enlarge the memory block pointed to by p by at least
1549  * min_size beyond its current capacity, up to a maximum of max_size.  This
1550  * does not attempt to move the memory block (like realloc() does).
1551  *
1552  * Returns:
1553  *  0 if could not extend p,
1554  *  total size of entire memory block if successful.
1555  */
1556 private size_t extend(void* p, size_t minsize, size_t maxsize)
1557 in
1558 {
1559     assert( minsize <= maxsize );
1560 }
1561 body
1562 {
1563     if (opts.options.sentinel)
1564         return 0;
1565
1566     Pool* pool = findPool(p);
1567     if (pool is null)
1568         return 0;
1569
1570     // Retrieve attributes
1571     auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
1572     uint attrs = getAttr(pool, bit_i);
1573
1574     void* blk_base_addr = pool.findBase(p);
1575     size_t blk_size = pool.findSize(p);
1576     bool has_pm = has_pointermap(attrs);
1577     size_t* pm_bitmask = null;
1578     size_t pm_bitmask_size = 0;
1579     if (has_pm) {
1580         pm_bitmask_size = size_t.sizeof;
1581         // Retrieve pointer map bit mask
1582         auto end_of_blk = cast(size_t**)(blk_base_addr +
1583                 blk_size - size_t.sizeof);
1584         pm_bitmask = *end_of_blk;
1585
1586         minsize += size_t.sizeof;
1587         maxsize += size_t.sizeof;
1588     }
1589
1590     if (blk_size < PAGESIZE)
1591         return 0; // cannot extend buckets
1592
1593     auto psz = blk_size / PAGESIZE;
1594     auto minsz = round_up(minsize, PAGESIZE);
1595     auto maxsz = round_up(maxsize, PAGESIZE);
1596
1597     auto pagenum = (p - pool.baseAddr) / PAGESIZE;
1598
1599     size_t sz;
1600     for (sz = 0; sz < maxsz; sz++)
1601     {
1602         auto i = pagenum + psz + sz;
1603         if (i == pool.npages)
1604             break;
1605         if (pool.pagetable[i] != B_FREE)
1606         {
1607             if (sz < minsz)
1608                 return 0;
1609             break;
1610         }
1611     }
1612     if (sz < minsz)
1613         return 0;
1614
1615     size_t new_size = (psz + sz) * PAGESIZE;
1616
1617     if (opts.options.mem_stomp)
1618         memset(p + blk_size - pm_bitmask_size, 0xF0,
1619                 new_size - blk_size - pm_bitmask_size);
1620     memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
1621     gc.p_cache = null;
1622     gc.size_cache = 0;
1623     // update the size cache, assuming that is very likely the size of this
1624     // block will be queried in the near future
1625     pool.update_cache(p, new_size);
1626
1627     if (has_pm) {
1628         new_size -= size_t.sizeof;
1629         auto end_of_blk = cast(size_t**)(blk_base_addr + new_size);
1630         *end_of_blk = pm_bitmask;
1631     }
1632     return new_size;
1633 }
1634
1635
1636 //
1637 //
1638 //
1639 private void free(void *p)
1640 {
1641     assert (p);
1642
1643     Pool*  pool;
1644     size_t pagenum;
1645     Bins   bin;
1646     size_t bit_i;
1647
1648     // Find which page it is in
1649     pool = findPool(p);
1650     if (!pool)                              // if not one of ours
1651         return;                             // ignore
1652     if (opts.options.sentinel) {
1653         sentinel_Invariant(p);
1654         p = sentinel_sub(p);
1655     }
1656     pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
1657     bit_i = cast(size_t)(p - pool.baseAddr) / 16;
1658     clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
1659
1660     bin = cast(Bins)pool.pagetable[pagenum];
1661     if (bin == B_PAGE)              // if large alloc
1662     {
1663         // Free pages
1664         size_t npages = 1;
1665         size_t n = pagenum;
1666         pool.freebits.set_group(bit_i, PAGESIZE / 16);
1667         while (++n < pool.npages && pool.pagetable[n] == B_PAGEPLUS)
1668             npages++;
1669         if (opts.options.mem_stomp)
1670             memset(p, 0xF2, npages * PAGESIZE);
1671         pool.freePages(pagenum, npages);
1672         // just in case we were caching this pointer
1673         pool.clear_cache(p);
1674     }
1675     else
1676     {
1677         // Add to free list
1678         List* list = cast(List*) p;
1679
1680         if (opts.options.mem_stomp)
1681             memset(p, 0xF2, binsize[bin]);
1682
1683         list.next = gc.free_list[bin];
1684         list.pool = pool;
1685         gc.free_list[bin] = list;
1686         pool.freebits.set(bit_i);
1687     }
1688 }
1689
1690
1691 /**
1692  * Determine the allocated size of pointer p.  If p is an interior pointer
1693  * or not a gc allocated pointer, return 0.
1694  */
1695 private size_t sizeOf(void *p)
1696 {
1697     assert (p);
1698
1699     if (opts.options.sentinel)
1700         p = sentinel_sub(p);
1701
1702     Pool* pool = findPool(p);
1703     if (pool is null)
1704         return 0;
1705
1706     auto biti = cast(size_t)(p - pool.baseAddr) / 16;
1707     uint attrs = getAttr(pool, biti);
1708
1709     size_t size = pool.findSize(p);
1710     size_t pm_bitmask_size = 0;
1711     if (has_pointermap(attrs))
1712         pm_bitmask_size = size_t.sizeof;
1713
1714     if (opts.options.sentinel) {
1715         // Check for interior pointer
1716         // This depends on:
1717         // 1) size is a power of 2 for less than PAGESIZE values
1718         // 2) base of memory pool is aligned on PAGESIZE boundary
1719         if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
1720             return 0;
1721         return size - SENTINEL_EXTRA - pm_bitmask_size;
1722     }
1723     else {
1724         if (p == gc.p_cache)
1725             return gc.size_cache;
1726
1727         // Check for interior pointer
1728         // This depends on:
1729         // 1) size is a power of 2 for less than PAGESIZE values
1730         // 2) base of memory pool is aligned on PAGESIZE boundary
1731         if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
1732             return 0;
1733
1734         gc.p_cache = p;
1735         gc.size_cache = size - pm_bitmask_size;
1736
1737         return gc.size_cache;
1738     }
1739 }
1740
1741
1742 /**
1743  * Verify that pointer p:
1744  *  1) belongs to this memory pool
1745  *  2) points to the start of an allocated piece of memory
1746  *  3) is not on a free list
1747  */
1748 private void checkNoSync(void *p)
1749 {
1750     assert(p);
1751
1752     if (opts.options.sentinel)
1753         sentinel_Invariant(p);
1754     debug (PTRCHECK)
1755     {
1756         Pool*  pool;
1757         size_t pagenum;
1758         Bins   bin;
1759         size_t size;
1760
1761         if (opts.options.sentinel)
1762             p = sentinel_sub(p);
1763         pool = findPool(p);
1764         assert(pool);
1765         pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
1766         bin = cast(Bins)pool.pagetable[pagenum];
1767         assert(bin <= B_PAGE);
1768         size = binsize[bin];
1769         assert((cast(size_t)p & (size - 1)) == 0);
1770
1771         debug (PTRCHECK2)
1772         {
1773             if (bin < B_PAGE)
1774             {
1775                 // Check that p is not on a free list
1776                 for (List* list = gc.free_list[bin]; list; list = list.next)
1777                 {
1778                     assert(cast(void*)list != p);
1779                 }
1780             }
1781         }
1782     }
1783 }
1784
1785
1786 //
1787 //
1788 //
1789 private void setStackBottom(void *p)
1790 {
1791     version (STACKGROWSDOWN)
1792     {
1793         //p = (void *)((uint *)p + 4);
1794         if (p > gc.stack_bottom)
1795         {
1796             gc.stack_bottom = p;
1797         }
1798     }
1799     else
1800     {
1801         //p = (void *)((uint *)p - 4);
1802         if (p < gc.stack_bottom)
1803         {
1804             gc.stack_bottom = cast(char*)p;
1805         }
1806     }
1807 }
1808
1809
1810 /**
1811  * Retrieve statistics about garbage collection.
1812  * Useful for debugging and tuning.
1813  */
1814 private GCStats getStats()
1815 {
1816     GCStats stats;
1817     size_t psize = 0;
1818     size_t usize = 0;
1819     size_t flsize = 0;
1820
1821     size_t n;
1822     size_t bsize = 0;
1823
1824     for (n = 0; n < gc.pools.length; n++)
1825     {
1826         Pool* pool = gc.pools[n];
1827         psize += pool.npages * PAGESIZE;
1828         for (size_t j = 0; j < pool.npages; j++)
1829         {
1830             Bins bin = cast(Bins)pool.pagetable[j];
1831             if (bin == B_FREE)
1832                 stats.freeblocks++;
1833             else if (bin == B_PAGE)
1834                 stats.pageblocks++;
1835             else if (bin < B_PAGE)
1836                 bsize += PAGESIZE;
1837         }
1838     }
1839
1840     for (n = 0; n < B_PAGE; n++)
1841     {
1842         for (List* list = gc.free_list[n]; list; list = list.next)
1843             flsize += binsize[n];
1844     }
1845
1846     usize = bsize - flsize;
1847
1848     stats.poolsize = psize;
1849     stats.usedsize = bsize - flsize;
1850     stats.freelistsize = flsize;
1851     return stats;
1852 }
1853
1854 /******************* weak-reference support *********************/
1855
1856 private struct WeakPointer
1857 {
1858     Object reference;
1859
1860     void ondestroy(Object r)
1861     {
1862         assert(r is reference);
1863         // lock for memory consistency (parallel readers)
1864         // also ensures that weakpointerDestroy can be called while another
1865         // thread is freeing the reference with "delete"
1866         return locked!(void, () {
1867             reference = null;
1868         })();
1869     }
1870 }
1871
1872 /**
1873  * Create a weak pointer to the given object.
1874  * Returns a pointer to an opaque struct allocated in C memory.
1875  */
1876 void* weakpointerCreate( Object r )
1877 {
1878     if (r)
1879     {
1880         // must be allocated in C memory
1881         // 1. to hide the reference from the GC
1882         // 2. the GC doesn't scan delegates added by rt_attachDisposeEvent
1883         //    for references
1884         auto wp = cast(WeakPointer*)(cstdlib.malloc(WeakPointer.sizeof));
1885         if (!wp)
1886             onOutOfMemoryError();
1887         wp.reference = r;
1888         rt_attachDisposeEvent(r, &wp.ondestroy);
1889         return wp;
1890     }
1891     return null;
1892 }
1893
1894 /**
1895  * Destroy a weak pointer returned by weakpointerCreate().
1896  * If null is passed, nothing happens.
1897  */
1898 void weakpointerDestroy( void* p )
1899 {
1900     if (p)
1901     {
1902         auto wp = cast(WeakPointer*)p;
1903         // must be extra careful about the GC or parallel threads
1904         // finalizing the reference at the same time
1905         return locked!(void, () {
1906             if (wp.reference)
1907                 rt_detachDisposeEvent(wp.reference, &wp.ondestroy);
1908         })();
1909         cstdlib.free(wp);
1910     }
1911 }
1912
1913 /**
1914  * Query a weak pointer and return either the object passed to
1915  * weakpointerCreate, or null if it was free'd in the meantime.
1916  * If null is passed, null is returned.
1917  */
1918 Object weakpointerGet( void* p )
1919 {
1920     if (p)
1921     {
1922         // NOTE: could avoid the lock by using Fawzi style GC counters but
1923         // that'd require core.sync.Atomic and lots of care about memory
1924         // consistency it's an optional optimization see
1925         // http://dsource.org/projects/tango/browser/trunk/user/tango/core/Lifetime.d?rev=5100#L158
1926         return locked!(Object, () {
1927             return (cast(WeakPointer*)p).reference;
1928         })();
1929         }
1930 }
1931
1932
1933 /* ============================ Pool  =============================== */
1934
1935
1936 struct Pool
1937 {
1938     byte* baseAddr;
1939     byte* topAddr;
1940     GCBits mark;     // entries already scanned, or should not be scanned
1941     GCBits scan;     // entries that need to be scanned
1942     GCBits freebits; // entries that are on the free list
1943     GCBits finals;   // entries that need finalizer run on them
1944     GCBits noscan;   // entries that should not be scanned
1945
1946     size_t npages;
1947     ubyte* pagetable;
1948
1949     /// Cache for findSize()
1950     size_t cached_size;
1951     void* cached_ptr;
1952
1953     void clear_cache(void* ptr = null)
1954     {
1955         if (ptr is null || ptr is this.cached_ptr) {
1956             this.cached_ptr = null;
1957             this.cached_size = 0;
1958         }
1959     }
1960
1961     void update_cache(void* ptr, size_t size)
1962     {
1963         this.cached_ptr = ptr;
1964         this.cached_size = size;
1965     }
1966
1967     void initialize(size_t npages)
1968     {
1969         size_t poolsize = npages * PAGESIZE;
1970         assert(poolsize >= POOLSIZE);
1971         baseAddr = cast(byte *) os.alloc(poolsize);
1972
1973         // Some of the code depends on page alignment of memory pools
1974         assert((cast(size_t)baseAddr & (PAGESIZE - 1)) == 0);
1975
1976         if (!baseAddr)
1977         {
1978             npages = 0;
1979             poolsize = 0;
1980         }
1981         topAddr = baseAddr + poolsize;
1982
1983         size_t nbits = cast(size_t)poolsize / 16;
1984
1985         // if the GC will run in parallel in a fork()ed process, we need to
1986         // share the mark bits
1987         os.Vis vis = os.Vis.PRIV;
1988         if (opts.options.fork)
1989             vis = os.Vis.SHARED;
1990         mark.alloc(nbits, vis); // shared between mark and sweep
1991         freebits.alloc(nbits); // not used by the mark phase
1992         scan.alloc(nbits); // only used in the mark phase
1993         finals.alloc(nbits); // not used by the mark phase
1994         noscan.alloc(nbits); // mark phase *MUST* have a snapshot
1995
1996         // all is free when we start
1997         freebits.set_all();
1998
1999         // avoid accidental sweeping of new pools while using eager allocation
2000         if (gc.mark_proc_pid)
2001             mark.set_all();
2002
2003         pagetable = cast(ubyte*) cstdlib.malloc(npages);
2004         if (!pagetable)
2005             onOutOfMemoryError();
2006         memset(pagetable, B_FREE, npages);
2007
2008         this.npages = npages;
2009     }
2010
2011
2012     void Dtor()
2013     {
2014         if (baseAddr)
2015         {
2016             int result;
2017
2018             if (npages)
2019             {
2020                 result = os.dealloc(baseAddr, npages * PAGESIZE);
2021                 assert(result);
2022                 npages = 0;
2023             }
2024
2025             baseAddr = null;
2026             topAddr = null;
2027         }
2028         // See Gcx.Dtor() for the rationale of the null check.
2029         if (pagetable)
2030             cstdlib.free(pagetable);
2031
2032         os.Vis vis = os.Vis.PRIV;
2033         if (opts.options.fork)
2034             vis = os.Vis.SHARED;
2035         mark.Dtor(vis);
2036         freebits.Dtor();
2037         scan.Dtor();
2038         finals.Dtor();
2039         noscan.Dtor();
2040     }
2041
2042
2043     bool Invariant()
2044     {
2045         return true;
2046     }
2047
2048
2049     invariant
2050     {
2051         //mark.Invariant();
2052         //scan.Invariant();
2053         //freebits.Invariant();
2054         //finals.Invariant();
2055         //noscan.Invariant();
2056
2057         if (baseAddr)
2058         {
2059             //if (baseAddr + npages * PAGESIZE != topAddr)
2060                 //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr);
2061             assert(baseAddr + npages * PAGESIZE == topAddr);
2062         }
2063
2064         for (size_t i = 0; i < npages; i++)
2065         {
2066             Bins bin = cast(Bins)pagetable[i];
2067             assert(bin < B_MAX);
2068         }
2069     }
2070
2071
2072     /**
2073      * Allocate n pages from Pool.
2074      * Returns OPFAIL on failure.
2075      */
2076     size_t allocPages(size_t n)
2077     {
2078         size_t i;
2079         size_t n2;
2080
2081         n2 = n;
2082         for (i = 0; i < npages; i++)
2083         {
2084             if (pagetable[i] == B_FREE)
2085             {
2086                 if (--n2 == 0)
2087                     return i - n + 1;
2088             }
2089             else
2090                 n2 = n;
2091         }
2092         return OPFAIL;
2093     }
2094
2095
2096     /**
2097      * Free npages pages starting with pagenum.
2098      */
2099     void freePages(size_t pagenum, size_t npages)
2100     {
2101         memset(&pagetable[pagenum], B_FREE, npages);
2102     }
2103
2104
2105     /**
2106      * Find base address of block containing pointer p.
2107      * Returns null if the pointer doesn't belong to this pool
2108      */
2109     void* findBase(void *p)
2110     {
2111         size_t offset = cast(size_t)(p - this.baseAddr);
2112         size_t pagenum = offset / PAGESIZE;
2113         Bins bin = cast(Bins)this.pagetable[pagenum];
2114         // Adjust bit to be at start of allocated memory block
2115         if (bin <= B_PAGE)
2116             return this.baseAddr + (offset & notbinsize[bin]);
2117         if (bin == B_PAGEPLUS) {
2118             do {
2119                 --pagenum, offset -= PAGESIZE;
2120             } while (cast(Bins)this.pagetable[pagenum] == B_PAGEPLUS);
2121             return this.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
2122         }
2123         // we are in a B_FREE page
2124         return null;
2125     }
2126
2127
2128     /**
2129      * Find size of pointer p.
2130      * Returns 0 if p doesn't belong to this pool if if it's block size is less
2131      * than a PAGE.
2132      */
2133     size_t findSize(void *p)
2134     {
2135         size_t pagenum = cast(size_t)(p - this.baseAddr) / PAGESIZE;
2136         Bins bin = cast(Bins)this.pagetable[pagenum];
2137         if (bin != B_PAGE)
2138             return binsize[bin];
2139         if (this.cached_ptr == p)
2140             return this.cached_size;
2141         size_t i = pagenum + 1;
2142         for (; i < this.npages; i++)
2143             if (this.pagetable[i] != B_PAGEPLUS)
2144                 break;
2145         this.cached_ptr = p;
2146         this.cached_size = (i - pagenum) * PAGESIZE;
2147         return this.cached_size;
2148     }
2149
2150
2151     /**
2152      * Used for sorting pools
2153      */
2154     int opCmp(in Pool other)
2155     {
2156         if (baseAddr < other.baseAddr)
2157             return -1;
2158         else
2159         return cast(int)(baseAddr > other.baseAddr);
2160     }
2161 }
2162
2163
2164 /* ============================ SENTINEL =============================== */
2165
2166
2167 const size_t SENTINEL_PRE = cast(size_t) 0xF4F4F4F4F4F4F4F4UL; // 32 or 64 bits
2168 const ubyte SENTINEL_POST = 0xF5;           // 8 bits
2169 const uint SENTINEL_EXTRA = 2 * size_t.sizeof + 1;
2170
2171
2172 size_t* sentinel_size(void *p)  { return &(cast(size_t *)p)[-2]; }
2173 size_t* sentinel_pre(void *p)   { return &(cast(size_t *)p)[-1]; }
2174 ubyte* sentinel_post(void *p) { return &(cast(ubyte *)p)[*sentinel_size(p)]; }
2175
2176
2177 void sentinel_init(void *p, size_t size)
2178 {
2179     *sentinel_size(p) = size;
2180     *sentinel_pre(p) = SENTINEL_PRE;
2181     *sentinel_post(p) = SENTINEL_POST;
2182 }
2183
2184
2185 void sentinel_Invariant(void *p)
2186 {
2187     if (*sentinel_pre(p) != SENTINEL_PRE ||
2188             *sentinel_post(p) != SENTINEL_POST)
2189         cstdlib.abort();
2190 }
2191
2192
2193 void *sentinel_add(void *p)
2194 {
2195     return p + 2 * size_t.sizeof;
2196 }
2197
2198
2199 void *sentinel_sub(void *p)
2200 {
2201     return p - 2 * size_t.sizeof;
2202 }
2203
2204
2205
2206 /* ============================ C Public Interface ======================== */
2207
2208
2209 private int _termCleanupLevel=1;
2210
2211 extern (C):
2212
2213 /// sets the cleanup level done by gc
2214 /// 0: none
2215 /// 1: fullCollect
2216 /// 2: fullCollect ignoring stack roots (might crash daemonThreads)
2217 /// result !=0 if the value was invalid
2218 int gc_setTermCleanupLevel(int cLevel)
2219 {
2220     if (cLevel<0 || cLevel>2) return cLevel;
2221     _termCleanupLevel=cLevel;
2222     return 0;
2223 }
2224
2225 /// returns the cleanup level done by gc
2226 int gc_getTermCleanupLevel()
2227 {
2228     return _termCleanupLevel;
2229 }
2230
2231 void gc_init()
2232 {
2233     scope (exit) assert (Invariant());
2234     gc = cast(GC*) cstdlib.calloc(1, GC.sizeof);
2235     *gc = GC.init;
2236     initialize();
2237     version (DigitalMars) version(OSX) {
2238         _d_osx_image_init();
2239     }
2240     // NOTE: The GC must initialize the thread library
2241     //       before its first collection.
2242     thread_init();
2243 }
2244
2245 void gc_term()
2246 {
2247     assert (Invariant());
2248     if (_termCleanupLevel<1) {
2249         // no cleanup
2250     } else if (_termCleanupLevel==2){
2251         // a more complete cleanup
2252         // NOTE: There may be daemons threads still running when this routine is
2253         //       called.  If so, cleaning memory out from under then is a good
2254         //       way to make them crash horribly.
2255         //       Often this probably doesn't matter much since the app is
2256         //       supposed to be shutting down anyway, but for example tests might
2257         //       crash (and be considerd failed even if the test was ok).
2258         //       thus this is not the default and should be enabled by
2259         //       I'm disabling cleanup for now until I can think about it some
2260         //       more.
2261         //
2262         // not really a 'collect all' -- still scans static data area, roots,
2263         // and ranges.
2264         return locked!(void, () {
2265             gc.no_stack++;
2266             fullcollectshell();
2267             gc.no_stack--;
2268         })();
2269     } else {
2270         // default (safe) clenup
2271         return locked!(void, () {
2272             fullcollectshell();
2273         })();
2274     }
2275 }
2276
2277 void gc_enable()
2278 {
2279     return locked!(void, () {
2280         assert (Invariant()); scope (exit) assert (Invariant());
2281         assert (gc.disabled > 0);
2282         gc.disabled--;
2283     })();
2284 }
2285
2286 void gc_disable()
2287 {
2288     return locked!(void, () {
2289         assert (Invariant()); scope (exit) assert (Invariant());
2290         gc.disabled++;
2291     })();
2292 }
2293
2294 void gc_collect()
2295 {
2296     return locked!(void, () {
2297         assert (Invariant()); scope (exit) assert (Invariant());
2298         fullcollectshell();
2299     })();
2300 }
2301
2302
2303 void gc_minimize()
2304 {
2305     return locked!(void, () {
2306         assert (Invariant()); scope (exit) assert (Invariant());
2307         minimize();
2308     })();
2309 }
2310
2311 uint gc_getAttr(void* p)
2312 {
2313     if (p is null)
2314         return 0;
2315     return locked!(uint, () {
2316         assert (Invariant()); scope (exit) assert (Invariant());
2317         Pool* pool = findPool(p);
2318         if (pool is null)
2319             return 0u;
2320         auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
2321         return getAttr(pool, bit_i);
2322     })();
2323 }
2324
2325 uint gc_setAttr(void* p, uint attrs)
2326 {
2327     if (p is null)
2328         return 0;
2329     return locked!(uint, () {
2330         assert (Invariant()); scope (exit) assert (Invariant());
2331         Pool* pool = findPool(p);
2332         if (pool is null)
2333             return 0u;
2334         auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
2335         uint old_attrs = getAttr(pool, bit_i);
2336         setAttr(pool, bit_i, attrs);
2337         return old_attrs;
2338     })();
2339 }
2340
2341 uint gc_clrAttr(void* p, uint attrs)
2342 {
2343     if (p is null)
2344         return 0;
2345     return locked!(uint, () {
2346         assert (Invariant()); scope (exit) assert (Invariant());
2347         Pool* pool = findPool(p);
2348         if (pool is null)
2349             return 0u;
2350         auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
2351         uint old_attrs = getAttr(pool, bit_i);
2352         clrAttr(pool, bit_i, attrs);
2353         return old_attrs;
2354     })();
2355 }
2356
2357 void* gc_malloc(size_t size, uint attrs = 0,
2358         PointerMap ptrmap = PointerMap.init)
2359 {
2360     if (size == 0)
2361         return null;
2362     return locked!(void*, () {
2363         assert (Invariant()); scope (exit) assert (Invariant());
2364         return malloc(size, attrs, ptrmap.bits.ptr);
2365     })();
2366 }
2367
2368 void* gc_calloc(size_t size, uint attrs = 0,
2369         PointerMap ptrmap = PointerMap.init)
2370 {
2371     if (size == 0)
2372         return null;
2373     return locked!(void*, () {
2374         assert (Invariant()); scope (exit) assert (Invariant());
2375         return calloc(size, attrs, ptrmap.bits.ptr);
2376     })();
2377 }
2378
2379 void* gc_realloc(void* p, size_t size, uint attrs = 0,
2380         PointerMap ptrmap = PointerMap.init)
2381 {
2382     return locked!(void*, () {
2383         assert (Invariant()); scope (exit) assert (Invariant());
2384         return realloc(p, size, attrs, ptrmap.bits.ptr);
2385     })();
2386 }
2387
2388 size_t gc_extend(void* p, size_t min_size, size_t max_size)
2389 {
2390     return locked!(size_t, () {
2391         assert (Invariant()); scope (exit) assert (Invariant());
2392         return extend(p, min_size, max_size);
2393     })();
2394 }
2395
2396 size_t gc_reserve(size_t size)
2397 {
2398     if (size == 0)
2399         return 0;
2400     return locked!(size_t, () {
2401         assert (Invariant()); scope (exit) assert (Invariant());
2402         return reserve(size);
2403     })();
2404 }
2405
2406 void gc_free(void* p)
2407 {
2408     if (p is null)
2409         return;
2410     return locked!(void, () {
2411         assert (Invariant()); scope (exit) assert (Invariant());
2412         free(p);
2413     })();
2414 }
2415
2416 void* gc_addrOf(void* p)
2417 {
2418     if (p is null)
2419         return null;
2420     return locked!(void*, () {
2421         assert (Invariant()); scope (exit) assert (Invariant());
2422         Pool* pool = findPool(p);
2423         if (pool is null)
2424             return null;
2425         return pool.findBase(p);
2426     })();
2427 }
2428
2429 size_t gc_sizeOf(void* p)
2430 {
2431     if (p is null)
2432         return 0;
2433     return locked!(size_t, () {
2434         assert (Invariant()); scope (exit) assert (Invariant());
2435         return sizeOf(p);
2436     })();
2437 }
2438
2439 BlkInfo gc_query(void* p)
2440 {
2441     if (p is null)
2442         return BlkInfo.init;
2443     return locked!(BlkInfo, () {
2444         assert (Invariant()); scope (exit) assert (Invariant());
2445         return getInfo(p);
2446     })();
2447 }
2448
2449 // NOTE: This routine is experimental.  The stats or function name may change
2450 //       before it is made officially available.
2451 GCStats gc_stats()
2452 {
2453     return locked!(GCStats, () {
2454         assert (Invariant()); scope (exit) assert (Invariant());
2455         return getStats();
2456     })();
2457 }
2458
2459 void gc_addRoot(void* p)
2460 {
2461     if (p is null)
2462         return;
2463     return locked!(void, () {
2464         assert (Invariant()); scope (exit) assert (Invariant());
2465         if (gc.roots.append(p) is null)
2466             onOutOfMemoryError();
2467     })();
2468 }
2469
2470 void gc_addRange(void* p, size_t size)
2471 {
2472     if (p is null || size == 0)
2473         return;
2474     return locked!(void, () {
2475         assert (Invariant()); scope (exit) assert (Invariant());
2476         if (gc.ranges.append(Range(p, p + size)) is null)
2477             onOutOfMemoryError();
2478     })();
2479 }
2480
2481 void gc_removeRoot(void* p)
2482 {
2483     if (p is null)
2484         return;
2485     return locked!(void, () {
2486         assert (Invariant()); scope (exit) assert (Invariant());
2487         bool r = gc.roots.remove(p);
2488         assert (r);
2489     })();
2490 }
2491
2492 void gc_removeRange(void* p)
2493 {
2494     if (p is null)
2495         return;
2496     return locked!(void, () {
2497         assert (Invariant()); scope (exit) assert (Invariant());
2498         bool r = gc.ranges.remove(Range(p, null));
2499         assert (r);
2500     })();
2501 }
2502
2503 void* gc_weakpointerCreate(Object r)
2504 {
2505     // weakpointers do their own locking
2506     return weakpointerCreate(r);
2507 }
2508
2509 void gc_weakpointerDestroy(void* wp)
2510 {
2511     // weakpointers do their own locking
2512     weakpointerDestroy(wp);
2513 }
2514
2515 Object gc_weakpointerGet(void* wp)
2516 {
2517     // weakpointers do their own locking
2518     return weakpointerGet(wp);
2519 }
2520
2521
2522 // vim: set et sw=4 sts=4 :