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