]> git.llucax.com Git - software/dgc/cdgc.git/blob - rt/gc/cdgc/gc.d
Add (disabled) debug print of overriden options
[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         if (p)
1469             free(p);
1470         return null;
1471     }
1472
1473     if (p is null)
1474         return malloc(size, attrs, pm_bitmask);
1475
1476     Pool* pool = findPool(p);
1477     if (pool is null)
1478         return null;
1479
1480     // Set or retrieve attributes as appropriate
1481     auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
1482     if (attrs) {
1483         clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
1484         setAttr(pool, bit_i, attrs);
1485     }
1486     else
1487         attrs = getAttr(pool, bit_i);
1488
1489     void* blk_base_addr = pool.findBase(p);
1490     size_t blk_size = pool.findSize(p);
1491     bool has_pm = has_pointermap(attrs);
1492     size_t pm_bitmask_size = 0;
1493     if (has_pm) {
1494         pm_bitmask_size = size_t.sizeof;
1495         // Retrieve pointer map bit mask if appropriate
1496         if (pm_bitmask is null) {
1497             auto end_of_blk = cast(size_t**)(
1498                     blk_base_addr + blk_size - size_t.sizeof);
1499             pm_bitmask = *end_of_blk;
1500         }
1501     }
1502
1503     if (opts.options.sentinel) {
1504         sentinel_Invariant(p);
1505         size_t sentinel_stored_size = *sentinel_size(p);
1506         if (sentinel_stored_size != size) {
1507             void* p2 = malloc(size, attrs, pm_bitmask);
1508             if (sentinel_stored_size < size)
1509                 size = sentinel_stored_size;
1510             cstring.memcpy(p2, p, size);
1511             p = p2;
1512         }
1513         return p;
1514     }
1515
1516     size += pm_bitmask_size;
1517     if (blk_size >= PAGESIZE && size >= PAGESIZE) {
1518         auto psz = blk_size / PAGESIZE;
1519         auto newsz = round_up(size, PAGESIZE);
1520         if (newsz == psz)
1521             return p;
1522
1523         auto pagenum = (p - pool.baseAddr) / PAGESIZE;
1524
1525         if (newsz < psz) {
1526             // Shrink in place
1527             if (opts.options.mem_stomp)
1528                 memset(p + size - pm_bitmask_size, 0xF2,
1529                         blk_size - size - pm_bitmask_size);
1530             pool.freePages(pagenum + newsz, psz - newsz);
1531             auto new_blk_size = (PAGESIZE * newsz);
1532             gc.free_mem += blk_size - new_blk_size;
1533             // update the size cache, assuming that is very likely the
1534             // size of this block will be queried in the near future
1535             pool.update_cache(p, new_blk_size);
1536             if (has_pm) {
1537                 auto end_of_blk = cast(size_t**)(blk_base_addr +
1538                         new_blk_size - pm_bitmask_size);
1539                 *end_of_blk = pm_bitmask;
1540             }
1541             return p;
1542         }
1543
1544         if (pagenum + newsz <= pool.npages) {
1545             // Attempt to expand in place
1546             for (size_t i = pagenum + psz; 1;) {
1547                 if (i == pagenum + newsz) {
1548                     if (opts.options.mem_stomp)
1549                         memset(p + blk_size - pm_bitmask_size, 0xF0,
1550                                 size - blk_size - pm_bitmask_size);
1551                     memset(pool.pagetable + pagenum + psz, B_PAGEPLUS,
1552                             newsz - psz);
1553                     auto new_blk_size = (PAGESIZE * newsz);
1554                     gc.free_mem -= new_blk_size - blk_size;
1555                     // update the size cache, assuming that is very
1556                     // likely the size of this block will be queried in
1557                     // the near future
1558                     pool.update_cache(p, new_blk_size);
1559                     if (has_pm) {
1560                         auto end_of_blk = cast(size_t**)(
1561                                 blk_base_addr + new_blk_size - pm_bitmask_size);
1562                         *end_of_blk = pm_bitmask;
1563                     }
1564                     return p;
1565                 }
1566                 if (i == pool.npages)
1567                     break;
1568                 if (pool.pagetable[i] != B_FREE)
1569                     break;
1570                 i++;
1571             }
1572         }
1573     }
1574
1575     // if new size is bigger or less than half
1576     if (blk_size < size || blk_size > size * 2) {
1577         size -= pm_bitmask_size;
1578         blk_size -= pm_bitmask_size;
1579         void* p2 = malloc(size, attrs, pm_bitmask);
1580         if (blk_size < size)
1581             size = blk_size;
1582         cstring.memcpy(p2, p, size);
1583         p = p2;
1584     }
1585
1586     return p;
1587 }
1588
1589
1590 /**
1591  * Attempt to in-place enlarge the memory block pointed to by p by at least
1592  * min_size beyond its current capacity, up to a maximum of max_size.  This
1593  * does not attempt to move the memory block (like realloc() does).
1594  *
1595  * Returns:
1596  *  0 if could not extend p,
1597  *  total size of entire memory block if successful.
1598  */
1599 private size_t extend(void* p, size_t minsize, size_t maxsize)
1600 in
1601 {
1602     assert( minsize <= maxsize );
1603 }
1604 body
1605 {
1606     if (opts.options.sentinel)
1607         return 0;
1608
1609     Pool* pool = findPool(p);
1610     if (pool is null)
1611         return 0;
1612
1613     // Retrieve attributes
1614     auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
1615     uint attrs = getAttr(pool, bit_i);
1616
1617     void* blk_base_addr = pool.findBase(p);
1618     size_t blk_size = pool.findSize(p);
1619     bool has_pm = has_pointermap(attrs);
1620     size_t* pm_bitmask = null;
1621     size_t pm_bitmask_size = 0;
1622     if (has_pm) {
1623         pm_bitmask_size = size_t.sizeof;
1624         // Retrieve pointer map bit mask
1625         auto end_of_blk = cast(size_t**)(blk_base_addr +
1626                 blk_size - size_t.sizeof);
1627         pm_bitmask = *end_of_blk;
1628
1629         minsize += size_t.sizeof;
1630         maxsize += size_t.sizeof;
1631     }
1632
1633     if (blk_size < PAGESIZE)
1634         return 0; // cannot extend buckets
1635
1636     auto psz = blk_size / PAGESIZE;
1637     auto minsz = round_up(minsize, PAGESIZE);
1638     auto maxsz = round_up(maxsize, PAGESIZE);
1639
1640     auto pagenum = (p - pool.baseAddr) / PAGESIZE;
1641
1642     size_t sz;
1643     for (sz = 0; sz < maxsz; sz++)
1644     {
1645         auto i = pagenum + psz + sz;
1646         if (i == pool.npages)
1647             break;
1648         if (pool.pagetable[i] != B_FREE)
1649         {
1650             if (sz < minsz)
1651                 return 0;
1652             break;
1653         }
1654     }
1655     if (sz < minsz)
1656         return 0;
1657
1658     size_t new_size = (psz + sz) * PAGESIZE;
1659
1660     if (opts.options.mem_stomp)
1661         memset(p + blk_size - pm_bitmask_size, 0xF0,
1662                 new_size - blk_size - pm_bitmask_size);
1663     memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
1664     gc.p_cache = null;
1665     gc.size_cache = 0;
1666     gc.free_mem -= new_size - blk_size;
1667     // update the size cache, assuming that is very likely the size of this
1668     // block will be queried in the near future
1669     pool.update_cache(p, new_size);
1670
1671     if (has_pm) {
1672         new_size -= size_t.sizeof;
1673         auto end_of_blk = cast(size_t**)(blk_base_addr + new_size);
1674         *end_of_blk = pm_bitmask;
1675     }
1676     return new_size;
1677 }
1678
1679
1680 //
1681 //
1682 //
1683 private void free(void *p)
1684 {
1685     assert (p);
1686
1687     Pool*  pool;
1688     size_t pagenum;
1689     Bins   bin;
1690     size_t bit_i;
1691
1692     // Find which page it is in
1693     pool = findPool(p);
1694     if (!pool)                              // if not one of ours
1695         return;                             // ignore
1696     if (opts.options.sentinel) {
1697         sentinel_Invariant(p);
1698         p = sentinel_sub(p);
1699     }
1700     pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
1701     bit_i = cast(size_t)(p - pool.baseAddr) / 16;
1702     clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
1703
1704     bin = cast(Bins)pool.pagetable[pagenum];
1705     if (bin == B_PAGE)              // if large alloc
1706     {
1707         // Free pages
1708         size_t npages = 1;
1709         size_t n = pagenum;
1710         pool.freebits.set_group(bit_i, PAGESIZE / 16);
1711         while (++n < pool.npages && pool.pagetable[n] == B_PAGEPLUS)
1712             npages++;
1713         size_t size = npages * PAGESIZE;
1714         if (opts.options.mem_stomp)
1715             memset(p, 0xF2, size);
1716         pool.freePages(pagenum, npages);
1717         gc.free_mem += size;
1718         // just in case we were caching this pointer
1719         pool.clear_cache(p);
1720     }
1721     else
1722     {
1723         // Add to free list
1724         List* list = cast(List*) p;
1725
1726         if (opts.options.mem_stomp)
1727             memset(p, 0xF2, binsize[bin]);
1728
1729         list.next = gc.free_list[bin];
1730         list.pool = pool;
1731         gc.free_list[bin] = list;
1732         pool.freebits.set(bit_i);
1733         gc.free_mem += binsize[bin];
1734     }
1735     double percent_free = gc.free_mem * 100.0 / gc.total_mem;
1736     if (percent_free > opts.options.min_free)
1737         minimize(false);
1738 }
1739
1740
1741 /**
1742  * Determine the allocated size of pointer p.  If p is an interior pointer
1743  * or not a gc allocated pointer, return 0.
1744  */
1745 private size_t sizeOf(void *p)
1746 {
1747     assert (p);
1748
1749     if (opts.options.sentinel)
1750         p = sentinel_sub(p);
1751
1752     Pool* pool = findPool(p);
1753     if (pool is null)
1754         return 0;
1755
1756     auto biti = cast(size_t)(p - pool.baseAddr) / 16;
1757     uint attrs = getAttr(pool, biti);
1758
1759     size_t size = pool.findSize(p);
1760     size_t pm_bitmask_size = 0;
1761     if (has_pointermap(attrs))
1762         pm_bitmask_size = size_t.sizeof;
1763
1764     if (opts.options.sentinel) {
1765         // Check for interior pointer
1766         // This depends on:
1767         // 1) size is a power of 2 for less than PAGESIZE values
1768         // 2) base of memory pool is aligned on PAGESIZE boundary
1769         if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
1770             return 0;
1771         return size - SENTINEL_EXTRA - pm_bitmask_size;
1772     }
1773     else {
1774         if (p == gc.p_cache)
1775             return gc.size_cache;
1776
1777         // Check for interior pointer
1778         // This depends on:
1779         // 1) size is a power of 2 for less than PAGESIZE values
1780         // 2) base of memory pool is aligned on PAGESIZE boundary
1781         if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
1782             return 0;
1783
1784         gc.p_cache = p;
1785         gc.size_cache = size - pm_bitmask_size;
1786
1787         return gc.size_cache;
1788     }
1789 }
1790
1791
1792 /**
1793  * Verify that pointer p:
1794  *  1) belongs to this memory pool
1795  *  2) points to the start of an allocated piece of memory
1796  *  3) is not on a free list
1797  */
1798 private void checkNoSync(void *p)
1799 {
1800     assert(p);
1801
1802     if (opts.options.sentinel)
1803         sentinel_Invariant(p);
1804     debug (PTRCHECK)
1805     {
1806         Pool*  pool;
1807         size_t pagenum;
1808         Bins   bin;
1809         size_t size;
1810
1811         if (opts.options.sentinel)
1812             p = sentinel_sub(p);
1813         pool = findPool(p);
1814         assert(pool);
1815         pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
1816         bin = cast(Bins)pool.pagetable[pagenum];
1817         assert(bin <= B_PAGE);
1818         size = binsize[bin];
1819         assert((cast(size_t)p & (size - 1)) == 0);
1820
1821         debug (PTRCHECK2)
1822         {
1823             if (bin < B_PAGE)
1824             {
1825                 // Check that p is not on a free list
1826                 for (List* list = gc.free_list[bin]; list; list = list.next)
1827                 {
1828                     assert(cast(void*)list != p);
1829                 }
1830             }
1831         }
1832     }
1833 }
1834
1835
1836 //
1837 //
1838 //
1839 private void setStackBottom(void *p)
1840 {
1841     version (STACKGROWSDOWN)
1842     {
1843         //p = (void *)((uint *)p + 4);
1844         if (p > gc.stack_bottom)
1845         {
1846             gc.stack_bottom = p;
1847         }
1848     }
1849     else
1850     {
1851         //p = (void *)((uint *)p - 4);
1852         if (p < gc.stack_bottom)
1853         {
1854             gc.stack_bottom = cast(char*)p;
1855         }
1856     }
1857 }
1858
1859
1860 /**
1861  * Retrieve statistics about garbage collection.
1862  * Useful for debugging and tuning.
1863  */
1864 private GCStats getStats()
1865 {
1866     GCStats stats;
1867     size_t psize = 0;
1868     size_t usize = 0;
1869     size_t flsize = 0;
1870
1871     size_t n;
1872     size_t bsize = 0;
1873
1874     for (n = 0; n < gc.pools.length; n++)
1875     {
1876         Pool* pool = gc.pools[n];
1877         psize += pool.npages * PAGESIZE;
1878         for (size_t j = 0; j < pool.npages; j++)
1879         {
1880             Bins bin = cast(Bins)pool.pagetable[j];
1881             if (bin == B_FREE)
1882                 stats.freeblocks++;
1883             else if (bin == B_PAGE)
1884                 stats.pageblocks++;
1885             else if (bin < B_PAGE)
1886                 bsize += PAGESIZE;
1887         }
1888     }
1889
1890     for (n = 0; n < B_PAGE; n++)
1891     {
1892         for (List* list = gc.free_list[n]; list; list = list.next)
1893             flsize += binsize[n];
1894     }
1895
1896     usize = bsize - flsize;
1897
1898     stats.poolsize = psize;
1899     stats.usedsize = bsize - flsize;
1900     stats.freelistsize = flsize;
1901     return stats;
1902 }
1903
1904 /******************* weak-reference support *********************/
1905
1906 private struct WeakPointer
1907 {
1908     Object reference;
1909
1910     void ondestroy(Object r)
1911     {
1912         assert(r is reference);
1913         // lock for memory consistency (parallel readers)
1914         // also ensures that weakpointerDestroy can be called while another
1915         // thread is freeing the reference with "delete"
1916         return locked!(void, () {
1917             reference = null;
1918         })();
1919     }
1920 }
1921
1922 /**
1923  * Create a weak pointer to the given object.
1924  * Returns a pointer to an opaque struct allocated in C memory.
1925  */
1926 void* weakpointerCreate( Object r )
1927 {
1928     if (r)
1929     {
1930         // must be allocated in C memory
1931         // 1. to hide the reference from the GC
1932         // 2. the GC doesn't scan delegates added by rt_attachDisposeEvent
1933         //    for references
1934         auto wp = cast(WeakPointer*)(cstdlib.malloc(WeakPointer.sizeof));
1935         if (!wp)
1936             onOutOfMemoryError();
1937         wp.reference = r;
1938         rt_attachDisposeEvent(r, &wp.ondestroy);
1939         return wp;
1940     }
1941     return null;
1942 }
1943
1944 /**
1945  * Destroy a weak pointer returned by weakpointerCreate().
1946  * If null is passed, nothing happens.
1947  */
1948 void weakpointerDestroy( void* p )
1949 {
1950     if (p)
1951     {
1952         auto wp = cast(WeakPointer*)p;
1953         // must be extra careful about the GC or parallel threads
1954         // finalizing the reference at the same time
1955         return locked!(void, () {
1956             if (wp.reference)
1957                 rt_detachDisposeEvent(wp.reference, &wp.ondestroy);
1958         })();
1959         cstdlib.free(wp);
1960     }
1961 }
1962
1963 /**
1964  * Query a weak pointer and return either the object passed to
1965  * weakpointerCreate, or null if it was free'd in the meantime.
1966  * If null is passed, null is returned.
1967  */
1968 Object weakpointerGet( void* p )
1969 {
1970     if (p)
1971     {
1972         // NOTE: could avoid the lock by using Fawzi style GC counters but
1973         // that'd require core.sync.Atomic and lots of care about memory
1974         // consistency it's an optional optimization see
1975         // http://dsource.org/projects/tango/browser/trunk/user/tango/core/Lifetime.d?rev=5100#L158
1976         return locked!(Object, () {
1977             return (cast(WeakPointer*)p).reference;
1978         })();
1979         }
1980 }
1981
1982
1983 /* ============================ Pool  =============================== */
1984
1985
1986 struct Pool
1987 {
1988     byte* baseAddr;
1989     byte* topAddr;
1990     GCBits mark;     // entries already scanned, or should not be scanned
1991     GCBits scan;     // entries that need to be scanned
1992     GCBits freebits; // entries that are on the free list
1993     GCBits finals;   // entries that need finalizer run on them
1994     GCBits noscan;   // entries that should not be scanned
1995
1996     size_t npages;
1997     ubyte* pagetable;
1998
1999     /// Cache for findSize()
2000     size_t cached_size;
2001     void* cached_ptr;
2002
2003     void clear_cache(void* ptr = null)
2004     {
2005         if (ptr is null || ptr is this.cached_ptr) {
2006             this.cached_ptr = null;
2007             this.cached_size = 0;
2008         }
2009     }
2010
2011     void update_cache(void* ptr, size_t size)
2012     {
2013         this.cached_ptr = ptr;
2014         this.cached_size = size;
2015     }
2016
2017     void initialize(size_t npages)
2018     {
2019         size_t poolsize = npages * PAGESIZE;
2020         assert(poolsize >= POOLSIZE);
2021         baseAddr = cast(byte *) os.alloc(poolsize);
2022
2023         // Some of the code depends on page alignment of memory pools
2024         assert((cast(size_t)baseAddr & (PAGESIZE - 1)) == 0);
2025
2026         if (!baseAddr)
2027         {
2028             npages = 0;
2029             poolsize = 0;
2030         }
2031         topAddr = baseAddr + poolsize;
2032
2033         size_t nbits = cast(size_t)poolsize / 16;
2034
2035         // if the GC will run in parallel in a fork()ed process, we need to
2036         // share the mark bits
2037         os.Vis vis = os.Vis.PRIV;
2038         if (opts.options.fork)
2039             vis = os.Vis.SHARED;
2040         mark.alloc(nbits, vis); // shared between mark and sweep
2041         freebits.alloc(nbits); // not used by the mark phase
2042         scan.alloc(nbits); // only used in the mark phase
2043         finals.alloc(nbits); // not used by the mark phase
2044         noscan.alloc(nbits); // mark phase *MUST* have a snapshot
2045
2046         // all is free when we start
2047         freebits.set_all();
2048
2049         // avoid accidental sweeping of new pools while using eager allocation
2050         if (collect_in_progress())
2051             mark.set_all();
2052
2053         pagetable = cast(ubyte*) cstdlib.malloc(npages);
2054         if (!pagetable)
2055             onOutOfMemoryError();
2056         memset(pagetable, B_FREE, npages);
2057
2058         this.npages = npages;
2059     }
2060
2061
2062     void Dtor()
2063     {
2064         if (baseAddr)
2065         {
2066             int result;
2067
2068             if (npages)
2069             {
2070                 result = os.dealloc(baseAddr, npages * PAGESIZE);
2071                 assert(result);
2072                 npages = 0;
2073             }
2074
2075             baseAddr = null;
2076             topAddr = null;
2077         }
2078         // See Gcx.Dtor() for the rationale of the null check.
2079         if (pagetable)
2080             cstdlib.free(pagetable);
2081
2082         os.Vis vis = os.Vis.PRIV;
2083         if (opts.options.fork)
2084             vis = os.Vis.SHARED;
2085         mark.Dtor(vis);
2086         freebits.Dtor();
2087         scan.Dtor();
2088         finals.Dtor();
2089         noscan.Dtor();
2090     }
2091
2092
2093     bool Invariant()
2094     {
2095         return true;
2096     }
2097
2098
2099     invariant
2100     {
2101         //mark.Invariant();
2102         //scan.Invariant();
2103         //freebits.Invariant();
2104         //finals.Invariant();
2105         //noscan.Invariant();
2106
2107         if (baseAddr)
2108         {
2109             //if (baseAddr + npages * PAGESIZE != topAddr)
2110                 //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr);
2111             assert(baseAddr + npages * PAGESIZE == topAddr);
2112         }
2113
2114         for (size_t i = 0; i < npages; i++)
2115         {
2116             Bins bin = cast(Bins)pagetable[i];
2117             assert(bin < B_MAX);
2118         }
2119     }
2120
2121
2122     /**
2123      * Allocate n pages from Pool.
2124      * Returns OPFAIL on failure.
2125      */
2126     size_t allocPages(size_t n)
2127     {
2128         size_t i;
2129         size_t n2;
2130
2131         n2 = n;
2132         for (i = 0; i < npages; i++)
2133         {
2134             if (pagetable[i] == B_FREE)
2135             {
2136                 if (--n2 == 0)
2137                     return i - n + 1;
2138             }
2139             else
2140                 n2 = n;
2141         }
2142         return OPFAIL;
2143     }
2144
2145
2146     /**
2147      * Free npages pages starting with pagenum.
2148      */
2149     void freePages(size_t pagenum, size_t npages)
2150     {
2151         memset(&pagetable[pagenum], B_FREE, npages);
2152     }
2153
2154
2155     /**
2156      * Find base address of block containing pointer p.
2157      * Returns null if the pointer doesn't belong to this pool
2158      */
2159     void* findBase(void *p)
2160     {
2161         size_t offset = cast(size_t)(p - this.baseAddr);
2162         size_t pagenum = offset / PAGESIZE;
2163         Bins bin = cast(Bins)this.pagetable[pagenum];
2164         // Adjust bit to be at start of allocated memory block
2165         if (bin <= B_PAGE)
2166             return this.baseAddr + (offset & notbinsize[bin]);
2167         if (bin == B_PAGEPLUS) {
2168             do {
2169                 --pagenum, offset -= PAGESIZE;
2170             } while (cast(Bins)this.pagetable[pagenum] == B_PAGEPLUS);
2171             return this.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
2172         }
2173         // we are in a B_FREE page
2174         return null;
2175     }
2176
2177
2178     /**
2179      * Find size of pointer p.
2180      * Returns 0 if p doesn't belong to this pool if if it's block size is less
2181      * than a PAGE.
2182      */
2183     size_t findSize(void *p)
2184     {
2185         size_t pagenum = cast(size_t)(p - this.baseAddr) / PAGESIZE;
2186         Bins bin = cast(Bins)this.pagetable[pagenum];
2187         if (bin != B_PAGE)
2188             return binsize[bin];
2189         if (this.cached_ptr == p)
2190             return this.cached_size;
2191         size_t i = pagenum + 1;
2192         for (; i < this.npages; i++)
2193             if (this.pagetable[i] != B_PAGEPLUS)
2194                 break;
2195         this.cached_ptr = p;
2196         this.cached_size = (i - pagenum) * PAGESIZE;
2197         return this.cached_size;
2198     }
2199
2200
2201     /**
2202      * Used for sorting pools
2203      */
2204     int opCmp(in Pool other)
2205     {
2206         if (baseAddr < other.baseAddr)
2207             return -1;
2208         else
2209         return cast(int)(baseAddr > other.baseAddr);
2210     }
2211 }
2212
2213
2214 /* ============================ SENTINEL =============================== */
2215
2216
2217 const size_t SENTINEL_PRE = cast(size_t) 0xF4F4F4F4F4F4F4F4UL; // 32 or 64 bits
2218 const ubyte SENTINEL_POST = 0xF5;           // 8 bits
2219 const uint SENTINEL_EXTRA = 2 * size_t.sizeof + 1;
2220
2221
2222 size_t* sentinel_size(void *p)  { return &(cast(size_t *)p)[-2]; }
2223 size_t* sentinel_pre(void *p)   { return &(cast(size_t *)p)[-1]; }
2224 ubyte* sentinel_post(void *p) { return &(cast(ubyte *)p)[*sentinel_size(p)]; }
2225
2226
2227 void sentinel_init(void *p, size_t size)
2228 {
2229     *sentinel_size(p) = size;
2230     *sentinel_pre(p) = SENTINEL_PRE;
2231     *sentinel_post(p) = SENTINEL_POST;
2232 }
2233
2234
2235 void sentinel_Invariant(void *p)
2236 {
2237     if (*sentinel_pre(p) != SENTINEL_PRE ||
2238             *sentinel_post(p) != SENTINEL_POST)
2239         cstdlib.abort();
2240 }
2241
2242
2243 void *sentinel_add(void *p)
2244 {
2245     return p + 2 * size_t.sizeof;
2246 }
2247
2248
2249 void *sentinel_sub(void *p)
2250 {
2251     return p - 2 * size_t.sizeof;
2252 }
2253
2254
2255
2256 /* ============================ C Public Interface ======================== */
2257
2258
2259 private int _termCleanupLevel=1;
2260
2261 extern (C):
2262
2263 /// sets the cleanup level done by gc
2264 /// 0: none
2265 /// 1: fullCollect
2266 /// 2: fullCollect ignoring stack roots (might crash daemonThreads)
2267 /// result !=0 if the value was invalid
2268 int gc_setTermCleanupLevel(int cLevel)
2269 {
2270     if (cLevel<0 || cLevel>2) return cLevel;
2271     _termCleanupLevel=cLevel;
2272     return 0;
2273 }
2274
2275 /// returns the cleanup level done by gc
2276 int gc_getTermCleanupLevel()
2277 {
2278     return _termCleanupLevel;
2279 }
2280
2281 void gc_init()
2282 {
2283     scope (exit) assert (Invariant());
2284     gc = cast(GC*) cstdlib.calloc(1, GC.sizeof);
2285     *gc = GC.init;
2286     initialize();
2287     version (DigitalMars) version(OSX) {
2288         _d_osx_image_init();
2289     }
2290     // NOTE: The GC must initialize the thread library
2291     //       before its first collection.
2292     thread_init();
2293 }
2294
2295 void gc_term()
2296 {
2297     assert (Invariant());
2298     if (_termCleanupLevel<1) {
2299         // no cleanup
2300     } else if (_termCleanupLevel==2){
2301         // a more complete cleanup
2302         // NOTE: There may be daemons threads still running when this routine is
2303         //       called.  If so, cleaning memory out from under then is a good
2304         //       way to make them crash horribly.
2305         //       Often this probably doesn't matter much since the app is
2306         //       supposed to be shutting down anyway, but for example tests might
2307         //       crash (and be considerd failed even if the test was ok).
2308         //       thus this is not the default and should be enabled by
2309         //       I'm disabling cleanup for now until I can think about it some
2310         //       more.
2311         //
2312         // not really a 'collect all' -- still scans static data area, roots,
2313         // and ranges.
2314         return locked!(void, () {
2315             gc.no_stack++;
2316             fullcollectshell();
2317             gc.no_stack--;
2318         })();
2319     } else {
2320         // default (safe) clenup
2321         return locked!(void, () {
2322             fullcollectshell();
2323         })();
2324     }
2325 }
2326
2327 void gc_enable()
2328 {
2329     return locked!(void, () {
2330         assert (Invariant()); scope (exit) assert (Invariant());
2331         assert (gc.disabled > 0);
2332         gc.disabled--;
2333     })();
2334 }
2335
2336 void gc_disable()
2337 {
2338     return locked!(void, () {
2339         assert (Invariant()); scope (exit) assert (Invariant());
2340         gc.disabled++;
2341     })();
2342 }
2343
2344 void gc_collect()
2345 {
2346     return locked!(void, () {
2347         assert (Invariant()); scope (exit) assert (Invariant());
2348         fullcollectshell();
2349     })();
2350 }
2351
2352
2353 void gc_minimize()
2354 {
2355     return locked!(void, () {
2356         assert (Invariant()); scope (exit) assert (Invariant());
2357         minimize();
2358     })();
2359 }
2360
2361 uint gc_getAttr(void* p)
2362 {
2363     if (p is null)
2364         return 0;
2365     return locked!(uint, () {
2366         assert (Invariant()); scope (exit) assert (Invariant());
2367         Pool* pool = findPool(p);
2368         if (pool is null)
2369             return 0u;
2370         auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
2371         return getAttr(pool, bit_i);
2372     })();
2373 }
2374
2375 uint gc_setAttr(void* p, uint attrs)
2376 {
2377     if (p is null)
2378         return 0;
2379     return locked!(uint, () {
2380         assert (Invariant()); scope (exit) assert (Invariant());
2381         Pool* pool = findPool(p);
2382         if (pool is null)
2383             return 0u;
2384         auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
2385         uint old_attrs = getAttr(pool, bit_i);
2386         setAttr(pool, bit_i, attrs);
2387         return old_attrs;
2388     })();
2389 }
2390
2391 uint gc_clrAttr(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         clrAttr(pool, bit_i, attrs);
2403         return old_attrs;
2404     })();
2405 }
2406
2407 void* gc_malloc(size_t size, uint attrs = 0,
2408         PointerMap ptrmap = PointerMap.init)
2409 {
2410     if (size == 0)
2411         return null;
2412     return locked!(void*, () {
2413         assert (Invariant()); scope (exit) assert (Invariant());
2414         return malloc(size, attrs, ptrmap.bits.ptr);
2415     })();
2416 }
2417
2418 void* gc_calloc(size_t size, uint attrs = 0,
2419         PointerMap ptrmap = PointerMap.init)
2420 {
2421     if (size == 0)
2422         return null;
2423     return locked!(void*, () {
2424         assert (Invariant()); scope (exit) assert (Invariant());
2425         return calloc(size, attrs, ptrmap.bits.ptr);
2426     })();
2427 }
2428
2429 void* gc_realloc(void* p, size_t size, uint attrs = 0,
2430         PointerMap ptrmap = PointerMap.init)
2431 {
2432     return locked!(void*, () {
2433         assert (Invariant()); scope (exit) assert (Invariant());
2434         return realloc(p, size, attrs, ptrmap.bits.ptr);
2435     })();
2436 }
2437
2438 size_t gc_extend(void* p, size_t min_size, size_t max_size)
2439 {
2440     return locked!(size_t, () {
2441         assert (Invariant()); scope (exit) assert (Invariant());
2442         return extend(p, min_size, max_size);
2443     })();
2444 }
2445
2446 size_t gc_reserve(size_t size)
2447 {
2448     if (size == 0)
2449         return 0;
2450     return locked!(size_t, () {
2451         assert (Invariant()); scope (exit) assert (Invariant());
2452         return reserve(size);
2453     })();
2454 }
2455
2456 void gc_free(void* p)
2457 {
2458     if (p is null)
2459         return;
2460     return locked!(void, () {
2461         assert (Invariant()); scope (exit) assert (Invariant());
2462         free(p);
2463     })();
2464 }
2465
2466 void* gc_addrOf(void* p)
2467 {
2468     if (p is null)
2469         return null;
2470     return locked!(void*, () {
2471         assert (Invariant()); scope (exit) assert (Invariant());
2472         Pool* pool = findPool(p);
2473         if (pool is null)
2474             return null;
2475         return pool.findBase(p);
2476     })();
2477 }
2478
2479 size_t gc_sizeOf(void* p)
2480 {
2481     if (p is null)
2482         return 0;
2483     return locked!(size_t, () {
2484         assert (Invariant()); scope (exit) assert (Invariant());
2485         return sizeOf(p);
2486     })();
2487 }
2488
2489 BlkInfo gc_query(void* p)
2490 {
2491     if (p is null)
2492         return BlkInfo.init;
2493     return locked!(BlkInfo, () {
2494         assert (Invariant()); scope (exit) assert (Invariant());
2495         return getInfo(p);
2496     })();
2497 }
2498
2499 // NOTE: This routine is experimental.  The stats or function name may change
2500 //       before it is made officially available.
2501 GCStats gc_stats()
2502 {
2503     return locked!(GCStats, () {
2504         assert (Invariant()); scope (exit) assert (Invariant());
2505         return getStats();
2506     })();
2507 }
2508
2509 void gc_addRoot(void* p)
2510 {
2511     if (p is null)
2512         return;
2513     return locked!(void, () {
2514         assert (Invariant()); scope (exit) assert (Invariant());
2515         if (gc.roots.append(p) is null)
2516             onOutOfMemoryError();
2517     })();
2518 }
2519
2520 void gc_addRange(void* p, size_t size)
2521 {
2522     if (p is null || size == 0)
2523         return;
2524     return locked!(void, () {
2525         assert (Invariant()); scope (exit) assert (Invariant());
2526         if (gc.ranges.append(Range(p, p + size)) is null)
2527             onOutOfMemoryError();
2528     })();
2529 }
2530
2531 void gc_removeRoot(void* p)
2532 {
2533     if (p is null)
2534         return;
2535     return locked!(void, () {
2536         assert (Invariant()); scope (exit) assert (Invariant());
2537         bool r = gc.roots.remove(p);
2538         assert (r);
2539     })();
2540 }
2541
2542 void gc_removeRange(void* p)
2543 {
2544     if (p is null)
2545         return;
2546     return locked!(void, () {
2547         assert (Invariant()); scope (exit) assert (Invariant());
2548         bool r = gc.ranges.remove(Range(p, null));
2549         assert (r);
2550     })();
2551 }
2552
2553 void* gc_weakpointerCreate(Object r)
2554 {
2555     // weakpointers do their own locking
2556     return weakpointerCreate(r);
2557 }
2558
2559 void gc_weakpointerDestroy(void* wp)
2560 {
2561     // weakpointers do their own locking
2562     weakpointerDestroy(wp);
2563 }
2564
2565 Object gc_weakpointerGet(void* wp)
2566 {
2567     // weakpointers do their own locking
2568     return weakpointerGet(wp);
2569 }
2570
2571
2572 // vim: set et sw=4 sts=4 :