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