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