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