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