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