2 * This module contains the garbage collector implementation.
4 * Copyright: Copyright (C) 2001-2007 Digital Mars, www.digitalmars.com.
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.
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
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
24 * Authors: Walter Bright, David Friedman, Sean Kelly
29 // D Programming Language Garbage Collector implementation
31 /************** Debugging ***************************/
33 //debug = COLLECT_PRINTF; // turn on printf's
34 //debug = PTRCHECK; // more pointer checking
35 //debug = PTRCHECK2; // thorough but slow pointer checking
37 /*************** Configuration *********************/
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
43 /***************************************************/
45 import rt.gc.cdgc.bits: GCBits;
46 import rt.gc.cdgc.stats: GCStats, Stats;
47 import rt.gc.cdgc.dynarray: DynArray;
48 import alloc = rt.gc.cdgc.alloc;
49 import opts = rt.gc.cdgc.opts;
51 import cstdlib = tango.stdc.stdlib;
52 import cstring = tango.stdc.string;
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.
59 void memset(void* dst, int c, size_t n)
61 // This number (32) has been determined empirically
63 cstring.memset(dst, c, n);
66 auto p = cast(ubyte*)(dst);
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
77 static import gcc.builtins; // for __builtin_unwind_int
87 package enum BlkAttr : uint
89 FINALIZE = 0b0000_0001,
90 NO_SCAN = 0b0000_0010,
91 NO_MOVE = 0b0000_0100,
92 ALL_BITS = 0b1111_1111
95 package bool has_pointermap(uint attrs)
97 return !opts.options.conservative && !(attrs & BlkAttr.NO_SCAN);
103 extern (C) void* rt_stackBottom();
104 extern (C) void* rt_stackTop();
106 extern (C) void rt_finalize( void* p, bool det = true );
108 alias void delegate(Object) DEvent;
109 extern (C) void rt_attachDisposeEvent(Object h, DEvent e);
110 extern (C) bool rt_detachDisposeEvent(Object h, DEvent e);
113 alias void delegate( void*, void* ) scanFn;
115 extern (C) void rt_scanStaticData( scanFn scan );
117 extern (C) bool thread_needLock();
118 extern (C) void thread_suspendAll();
119 extern (C) void thread_resumeAll();
121 extern (C) void thread_scanAll( scanFn fn, void* curStackTop = null );
123 extern (C) void onOutOfMemoryError();
127 OPFAIL = ~cast(size_t)0
135 /* ============================ GC =============================== */
138 class GCLock { } // just a dummy so we can get a global lock
145 Gcx *gcx; // implementation
146 static ClassInfo gcLock; // global lock
151 opts.parse(cstdlib.getenv("D_GC_OPTS"));
152 gcLock = GCLock.classinfo;
153 gcx = cast(Gcx*) cstdlib.calloc(1, Gcx.sizeof);
155 onOutOfMemoryError();
157 setStackBottom(rt_stackBottom());
167 if (!thread_needLock())
169 assert(gcx.disabled > 0);
172 else synchronized (gcLock)
174 assert(gcx.disabled > 0);
185 if (!thread_needLock())
189 else synchronized (gcLock)
199 uint getAttr(void* p)
208 Pool* pool = gcx.findPool(p);
213 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
215 old_attrs = gcx.getAttr(pool, bit_i);
220 if (!thread_needLock())
224 else synchronized (gcLock)
234 uint setAttr(void* p, uint mask)
243 Pool* pool = gcx.findPool(p);
248 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
250 old_attrs = gcx.getAttr(pool, bit_i);
251 gcx.setAttr(pool, bit_i, mask);
256 if (!thread_needLock())
260 else synchronized (gcLock)
270 uint clrAttr(void* p, uint mask)
279 Pool* pool = gcx.findPool(p);
284 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
286 old_attrs = gcx.getAttr(pool, bit_i);
287 gcx.clrAttr(pool, bit_i, mask);
292 if (!thread_needLock())
296 else synchronized (gcLock)
306 void *malloc(size_t size, uint attrs, PointerMap ptrmap)
313 if (!thread_needLock())
315 return mallocNoSync(size, attrs, ptrmap.bits.ptr);
317 else synchronized (gcLock)
319 return mallocNoSync(size, attrs, ptrmap.bits.ptr);
327 private void *mallocNoSync(size_t size, uint attrs, size_t* pm_bitmask)
331 stats.malloc_started(size, attrs, pm_bitmask);
333 stats.malloc_finished(p);
340 if (opts.options.sentinel)
341 size += SENTINEL_EXTRA;
343 bool has_pm = has_pointermap(attrs);
345 size += size_t.sizeof;
348 // Cache previous binsize lookup - Dave Fladebo.
349 static size_t lastsize = -1;
351 if (size == lastsize)
355 bin = gcx.findBin(size);
360 size_t capacity; // to figure out where to store the bitmask
366 if (!gcx.allocPage(bin) && !gcx.disabled) // try to find a new page
368 if (!thread_needLock())
370 /* Then we haven't locked it yet. Be sure
371 * and lock for a collection, since a finalizer
372 * may start a new thread.
374 synchronized (gcLock)
376 gcx.fullcollectshell();
379 else if (!gcx.fullcollectshell()) // collect to find a new page
384 if (!gcx.bucket[bin] && !gcx.allocPage(bin))
386 gcx.newPool(1); // allocate new pool to find a new page
387 int result = gcx.allocPage(bin);
389 onOutOfMemoryError();
393 capacity = binsize[bin];
395 // Return next item from free list
396 gcx.bucket[bin] = (cast(List*)p).next;
397 if (!(attrs & BlkAttr.NO_SCAN))
398 memset(p + size, 0, capacity - size);
399 if (opts.options.mem_stomp)
400 memset(p, 0xF0, size);
404 p = gcx.bigAlloc(size);
406 onOutOfMemoryError();
407 // Round the size up to the number of pages needed to store it
408 size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
409 capacity = npages * PAGESIZE;
412 // Store the bit mask AFTER SENTINEL_POST
413 // TODO: store it BEFORE, so the bitmask is protected too
415 auto end_of_blk = cast(size_t**)(p + capacity - size_t.sizeof);
416 *end_of_blk = pm_bitmask;
417 size -= size_t.sizeof;
420 if (opts.options.sentinel) {
421 size -= SENTINEL_EXTRA;
423 sentinel_init(p, size);
428 Pool *pool = gcx.findPool(p);
431 gcx.setAttr(pool, cast(size_t)(p - pool.baseAddr) / 16, attrs);
440 void *calloc(size_t size, uint attrs, PointerMap ptrmap)
447 if (!thread_needLock())
449 return callocNoSync(size, attrs, ptrmap.bits.ptr);
451 else synchronized (gcLock)
453 return callocNoSync(size, attrs, ptrmap.bits.ptr);
461 private void *callocNoSync(size_t size, uint attrs, size_t* pm_bitmask)
465 void *p = mallocNoSync(size, attrs, pm_bitmask);
474 void *realloc(void *p, size_t size, uint attrs, PointerMap ptrmap)
476 if (!thread_needLock())
478 return reallocNoSync(p, size, attrs, ptrmap.bits.ptr);
480 else synchronized (gcLock)
482 return reallocNoSync(p, size, attrs, ptrmap.bits.ptr);
490 private void *reallocNoSync(void *p, size_t size, uint attrs,
503 p = mallocNoSync(size, attrs, pm_bitmask);
507 Pool* pool = gcx.findPool(p);
511 // Set or retrieve attributes as appropriate
512 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
514 gcx.clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
515 gcx.setAttr(pool, bit_i, attrs);
518 attrs = gcx.getAttr(pool, bit_i);
520 void* blk_base_addr = gcx.findBase(p);
521 size_t blk_size = gcx.findSize(p);
522 bool has_pm = has_pointermap(attrs);
523 size_t pm_bitmask_size = 0;
525 pm_bitmask_size = size_t.sizeof;
526 // Retrieve pointer map bit mask if appropriate
527 if (pm_bitmask is null) {
528 auto end_of_blk = cast(size_t**)(blk_base_addr +
529 blk_size - size_t.sizeof);
530 pm_bitmask = *end_of_blk;
534 if (opts.options.sentinel)
536 sentinel_Invariant(p);
537 size_t sentinel_stored_size = *sentinel_size(p);
538 if (sentinel_stored_size != size)
540 void* p2 = mallocNoSync(size, attrs, pm_bitmask);
541 if (sentinel_stored_size < size)
542 size = sentinel_stored_size;
543 cstring.memcpy(p2, p, size);
549 size += pm_bitmask_size;
550 if (blk_size >= PAGESIZE && size >= PAGESIZE)
552 auto psz = blk_size / PAGESIZE;
553 auto newsz = (size + PAGESIZE - 1) / PAGESIZE;
557 auto pagenum = (p - pool.baseAddr) / PAGESIZE;
562 synchronized (gcLock)
564 if (opts.options.mem_stomp)
565 memset(p + size - pm_bitmask_size, 0xF2,
566 blk_size - size - pm_bitmask_size);
567 pool.freePages(pagenum + newsz, psz - newsz);
570 auto end_of_blk = cast(size_t**)(
571 blk_base_addr + (PAGESIZE * newsz) -
573 *end_of_blk = pm_bitmask;
577 else if (pagenum + newsz <= pool.npages)
579 // Attempt to expand in place
580 synchronized (gcLock)
582 for (size_t i = pagenum + psz; 1;)
584 if (i == pagenum + newsz)
586 if (opts.options.mem_stomp)
587 memset(p + blk_size - pm_bitmask_size,
588 0xF0, size - blk_size
590 memset(pool.pagetable + pagenum +
591 psz, B_PAGEPLUS, newsz - psz);
593 auto end_of_blk = cast(size_t**)(
597 *end_of_blk = pm_bitmask;
601 if (i == pool.npages)
605 if (pool.pagetable[i] != B_FREE)
612 // if new size is bigger or less than half
613 if (blk_size < size || blk_size > size * 2)
615 size -= pm_bitmask_size;
616 blk_size -= pm_bitmask_size;
617 void* p2 = mallocNoSync(size, attrs, pm_bitmask);
620 cstring.memcpy(p2, p, size);
630 * Attempt to in-place enlarge the memory block pointed to by p by at least
631 * minbytes beyond its current capacity, up to a maximum of maxsize. This
632 * does not attempt to move the memory block (like realloc() does).
635 * 0 if could not extend p,
636 * total size of entire memory block if successful.
638 size_t extend(void* p, size_t minsize, size_t maxsize)
640 if (!thread_needLock())
642 return extendNoSync(p, minsize, maxsize);
644 else synchronized (gcLock)
646 return extendNoSync(p, minsize, maxsize);
654 private size_t extendNoSync(void* p, size_t minsize, size_t maxsize)
657 assert( minsize <= maxsize );
661 if (opts.options.sentinel)
664 Pool* pool = gcx.findPool(p);
668 // Retrieve attributes
669 auto bit_i = cast(size_t)(p - pool.baseAddr) / 16;
670 uint attrs = gcx.getAttr(pool, bit_i);
672 void* blk_base_addr = gcx.findBase(p);
673 size_t blk_size = gcx.findSize(p);
674 bool has_pm = has_pointermap(attrs);
675 size_t* pm_bitmask = null;
676 size_t pm_bitmask_size = 0;
678 pm_bitmask_size = size_t.sizeof;
679 // Retrieve pointer map bit mask
680 auto end_of_blk = cast(size_t**)(blk_base_addr +
681 blk_size - size_t.sizeof);
682 pm_bitmask = *end_of_blk;
684 minsize += size_t.sizeof;
685 maxsize += size_t.sizeof;
688 if (blk_size < PAGESIZE)
689 return 0; // cannot extend buckets
691 auto psz = blk_size / PAGESIZE;
692 auto minsz = (minsize + PAGESIZE - 1) / PAGESIZE;
693 auto maxsz = (maxsize + PAGESIZE - 1) / PAGESIZE;
695 auto pagenum = (p - pool.baseAddr) / PAGESIZE;
698 for (sz = 0; sz < maxsz; sz++)
700 auto i = pagenum + psz + sz;
701 if (i == pool.npages)
703 if (pool.pagetable[i] != B_FREE)
713 size_t new_size = (psz + sz) * PAGESIZE;
715 if (opts.options.mem_stomp)
716 memset(p + blk_size - pm_bitmask_size, 0xF0,
717 new_size - blk_size - pm_bitmask_size);
718 memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
723 new_size -= size_t.sizeof;
724 auto end_of_blk = cast(size_t**)(blk_base_addr + new_size);
725 *end_of_blk = pm_bitmask;
734 size_t reserve(size_t size)
741 if (!thread_needLock())
743 return reserveNoSync(size);
745 else synchronized (gcLock)
747 return reserveNoSync(size);
755 private size_t reserveNoSync(size_t size)
760 return gcx.reserve(size);
774 if (!thread_needLock())
776 return freeNoSync(p);
778 else synchronized (gcLock)
780 return freeNoSync(p);
788 private void freeNoSync(void *p)
797 // Find which page it is in
798 pool = gcx.findPool(p);
799 if (!pool) // if not one of ours
801 if (opts.options.sentinel) {
802 sentinel_Invariant(p);
805 pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
806 bit_i = cast(size_t)(p - pool.baseAddr) / 16;
807 gcx.clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
809 bin = cast(Bins)pool.pagetable[pagenum];
810 if (bin == B_PAGE) // if large alloc
815 while (++n < pool.npages && pool.pagetable[n] == B_PAGEPLUS)
817 if (opts.options.mem_stomp)
818 memset(p, 0xF2, npages * PAGESIZE);
819 pool.freePages(pagenum, npages);
824 List *list = cast(List*)p;
826 if (opts.options.mem_stomp)
827 memset(p, 0xF2, binsize[bin]);
829 list.next = gcx.bucket[bin];
830 gcx.bucket[bin] = list;
836 * Determine the base address of the block containing p. If p is not a gc
837 * allocated pointer, return null.
839 void* addrOf(void *p)
846 if (!thread_needLock())
848 return addrOfNoSync(p);
850 else synchronized (gcLock)
852 return addrOfNoSync(p);
860 void* addrOfNoSync(void *p)
867 return gcx.findBase(p);
872 * Determine the allocated size of pointer p. If p is an interior pointer
873 * or not a gc allocated pointer, return 0.
875 size_t sizeOf(void *p)
882 if (!thread_needLock())
884 return sizeOfNoSync(p);
886 else synchronized (gcLock)
888 return sizeOfNoSync(p);
896 private size_t sizeOfNoSync(void *p)
900 if (opts.options.sentinel)
903 Pool* pool = gcx.findPool(p);
907 auto biti = cast(size_t)(p - pool.baseAddr) / 16;
908 uint attrs = gcx.getAttr(pool, biti);
910 size_t size = gcx.findSize(p);
911 size_t pm_bitmask_size = 0;
912 if (has_pointermap(attrs))
913 pm_bitmask_size = size_t.sizeof;
915 if (opts.options.sentinel) {
916 // Check for interior pointer
918 // 1) size is a power of 2 for less than PAGESIZE values
919 // 2) base of memory pool is aligned on PAGESIZE boundary
920 if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
922 return size - SENTINEL_EXTRA - pm_bitmask_size;
925 if (p == gcx.p_cache)
926 return gcx.size_cache;
928 // Check for interior pointer
930 // 1) size is a power of 2 for less than PAGESIZE values
931 // 2) base of memory pool is aligned on PAGESIZE boundary
932 if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
936 gcx.size_cache = size - pm_bitmask_size;
938 return gcx.size_cache;
944 * Determine the base address of the block containing p. If p is not a gc
945 * allocated pointer, return null.
947 BlkInfo query(void *p)
955 if (!thread_needLock())
957 return queryNoSync(p);
959 else synchronized (gcLock)
961 return queryNoSync(p);
969 BlkInfo queryNoSync(void *p)
973 return gcx.getInfo(p);
978 * Verify that pointer p:
979 * 1) belongs to this memory pool
980 * 2) points to the start of an allocated piece of memory
981 * 3) is not on a free list
990 if (!thread_needLock())
994 else synchronized (gcLock)
1004 private void checkNoSync(void *p)
1008 if (opts.options.sentinel)
1009 sentinel_Invariant(p);
1017 if (opts.options.sentinel)
1018 p = sentinel_sub(p);
1019 pool = gcx.findPool(p);
1021 pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
1022 bin = cast(Bins)pool.pagetable[pagenum];
1023 assert(bin <= B_PAGE);
1024 size = binsize[bin];
1025 assert((cast(size_t)p & (size - 1)) == 0);
1031 // Check that p is not on a free list
1034 for (list = gcx.bucket[bin]; list; list = list.next)
1036 assert(cast(void*)list != p);
1047 private void setStackBottom(void *p)
1049 version (STACKGROWSDOWN)
1051 //p = (void *)((uint *)p + 4);
1052 if (p > gcx.stackBottom)
1054 gcx.stackBottom = p;
1059 //p = (void *)((uint *)p - 4);
1060 if (p < gcx.stackBottom)
1062 gcx.stackBottom = cast(char*)p;
1069 * add p to list of roots
1071 void addRoot(void *p)
1078 if (!thread_needLock())
1080 if (roots.append(p) is null)
1081 onOutOfMemoryError();
1083 else synchronized (gcLock)
1085 if (roots.append(p) is null)
1086 onOutOfMemoryError();
1092 * remove p from list of roots
1094 void removeRoot(void *p)
1102 if (!thread_needLock())
1104 r = roots.remove(p);
1106 else synchronized (gcLock)
1108 r = roots.remove(p);
1115 * add range to scan for roots
1117 void addRange(void *p, size_t sz)
1124 if (!thread_needLock())
1126 if (ranges.append(Range(p, p+sz)) is null)
1127 onOutOfMemoryError();
1129 else synchronized (gcLock)
1131 if (ranges.append(Range(p, p+sz)) is null)
1132 onOutOfMemoryError();
1140 void removeRange(void *p)
1148 if (!thread_needLock())
1150 r = ranges.remove(Range(p, null));
1152 else synchronized (gcLock)
1154 r = ranges.remove(Range(p, null));
1161 * do full garbage collection
1166 if (!thread_needLock())
1168 gcx.fullcollectshell();
1170 else synchronized (gcLock)
1172 gcx.fullcollectshell();
1185 * do full garbage collection ignoring roots
1187 void fullCollectNoStack()
1189 if (!thread_needLock())
1192 gcx.fullcollectshell();
1195 else synchronized (gcLock)
1198 gcx.fullcollectshell();
1205 * minimize free space usage
1209 if (!thread_needLock())
1213 else synchronized (gcLock)
1221 * Retrieve statistics about garbage collection.
1222 * Useful for debugging and tuning.
1224 void getStats(out GCStats stats)
1226 if (!thread_needLock())
1228 getStatsNoSync(stats);
1230 else synchronized (gcLock)
1232 getStatsNoSync(stats);
1240 private void getStatsNoSync(out GCStats stats)
1249 memset(&stats, 0, GCStats.sizeof);
1251 for (n = 0; n < pools.length; n++)
1253 Pool* pool = pools[n];
1254 psize += pool.npages * PAGESIZE;
1255 for (size_t j = 0; j < pool.npages; j++)
1257 Bins bin = cast(Bins)pool.pagetable[j];
1260 else if (bin == B_PAGE)
1262 else if (bin < B_PAGE)
1267 for (n = 0; n < B_PAGE; n++)
1269 for (List *list = gcx.bucket[n]; list; list = list.next)
1270 flsize += binsize[n];
1273 usize = bsize - flsize;
1275 stats.poolsize = psize;
1276 stats.usedsize = bsize - flsize;
1277 stats.freelistsize = flsize;
1280 /******************* weak-reference support *********************/
1282 // call locked if necessary
1283 private T locked(T)(in T delegate() code)
1285 if (thread_needLock)
1286 synchronized(gcLock) return code();
1291 private struct WeakPointer
1295 void ondestroy(Object r)
1297 assert(r is reference);
1298 // lock for memory consistency (parallel readers)
1299 // also ensures that weakpointerDestroy can be called while another
1300 // thread is freeing the reference with "delete"
1301 locked!(void)({ reference = null; });
1306 * Create a weak pointer to the given object.
1307 * Returns a pointer to an opaque struct allocated in C memory.
1309 void* weakpointerCreate( Object r )
1313 // must be allocated in C memory
1314 // 1. to hide the reference from the GC
1315 // 2. the GC doesn't scan delegates added by rt_attachDisposeEvent
1317 auto wp = cast(WeakPointer*)(cstdlib.malloc(WeakPointer.sizeof));
1319 onOutOfMemoryError();
1321 rt_attachDisposeEvent(r, &wp.ondestroy);
1328 * Destroy a weak pointer returned by weakpointerCreate().
1329 * If null is passed, nothing happens.
1331 void weakpointerDestroy( void* p )
1335 auto wp = cast(WeakPointer*)p;
1336 // must be extra careful about the GC or parallel threads
1337 // finalizing the reference at the same time
1340 rt_detachDisposeEvent(wp.reference, &wp.ondestroy);
1347 * Query a weak pointer and return either the object passed to
1348 * weakpointerCreate, or null if it was free'd in the meantime.
1349 * If null is passed, null is returned.
1351 Object weakpointerGet( void* p )
1355 // NOTE: could avoid the lock by using Fawzi style GC counters but
1356 // that'd require core.sync.Atomic and lots of care about memory
1357 // consistency it's an optional optimization see
1358 // http://dsource.org/projects/tango/browser/trunk/user/tango/core/Lifetime.d?rev=5100#L158
1359 return locked!(Object)({
1360 return (cast(WeakPointer*)p).reference;
1367 /* ============================ Gcx =============================== */
1372 POOLSIZE = (4096*256),
1386 B_PAGE, // start of large alloc
1387 B_PAGEPLUS, // continuation of large alloc
1388 B_FREE, // free page
1406 int opCmp(in Range other)
1408 if (pbot < other.pbot)
1411 return cast(int)(pbot > other.pbot);
1416 const uint binsize[B_MAX] = [ 16,32,64,128,256,512,1024,2048,4096 ];
1417 const uint notbinsize[B_MAX] = [ ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1),
1418 ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) ];
1420 DynArray!(void*) roots;
1422 DynArray!(Range) ranges;
1424 DynArray!(Pool) pools;
1427 /* ============================ Gcx =============================== */
1436 uint noStack; // !=0 means don't scan stack
1440 int disabled; // turn off collections if >0
1442 byte *minAddr; // min(baseAddr)
1443 byte *maxAddr; // max(topAddr)
1445 List *bucket[B_MAX]; // free list for each size
1451 (cast(byte*)this)[0 .. Gcx.sizeof] = 0;
1452 stackBottom = cast(char*)&dummy;
1453 //printf("gcx = %p, self = %x\n", this, self);
1458 void Invariant() { }
1465 //printf("Gcx.invariant(): this = %p\n", this);
1468 for (i = 0; i < pools.length; i++)
1470 Pool* pool = pools[i];
1474 assert(minAddr == pool.baseAddr);
1476 if (i + 1 < pools.length)
1478 assert(*pool < pools[i + 1]);
1480 else if (i + 1 == pools.length)
1482 assert(maxAddr == pool.topAddr);
1489 for (i = 0; i < ranges.length; i++)
1491 assert(ranges[i].pbot);
1492 assert(ranges[i].ptop);
1493 assert(ranges[i].pbot <= ranges[i].ptop);
1496 for (i = 0; i < B_PAGE; i++)
1498 for (List *list = bucket[i]; list; list = list.next)
1507 * Find Pool that pointer is in.
1508 * Return null if not in a Pool.
1509 * Assume pools is sorted.
1511 Pool *findPool(void *p)
1513 if (p >= minAddr && p < maxAddr)
1515 if (pools.length == 1)
1520 for (size_t i = 0; i < pools.length; i++)
1522 Pool* pool = pools[i];
1523 if (p < pool.topAddr)
1525 if (pool.baseAddr <= p)
1536 * Find base address of block containing pointer p.
1537 * Returns null if not a gc'd pointer
1539 void* findBase(void *p)
1546 size_t offset = cast(size_t)(p - pool.baseAddr);
1547 size_t pn = offset / PAGESIZE;
1548 Bins bin = cast(Bins)pool.pagetable[pn];
1550 // Adjust bit to be at start of allocated memory block
1553 return pool.baseAddr + (offset & notbinsize[bin]);
1555 else if (bin == B_PAGEPLUS)
1559 --pn, offset -= PAGESIZE;
1560 } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
1562 return pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
1566 // we are in a B_FREE page
1575 * Find size of pointer p.
1576 * Returns 0 if not a gc'd pointer
1578 size_t findSize(void *p)
1589 pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE;
1590 bin = cast(Bins)pool.pagetable[pagenum];
1591 size = binsize[bin];
1597 pt = &pool.pagetable[0];
1598 for (i = pagenum + 1; i < pool.npages; i++)
1600 if (pt[i] != B_PAGEPLUS)
1603 size = (i - pagenum) * PAGESIZE;
1613 BlkInfo getInfo(void* p)
1621 size_t offset = cast(size_t)(p - pool.baseAddr);
1622 size_t pn = offset / PAGESIZE;
1623 Bins bin = cast(Bins)pool.pagetable[pn];
1625 ////////////////////////////////////////////////////////////////////
1627 ////////////////////////////////////////////////////////////////////
1631 info.base = pool.baseAddr + (offset & notbinsize[bin]);
1633 else if (bin == B_PAGEPLUS)
1637 --pn, offset -= PAGESIZE;
1639 while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
1641 info.base = pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
1643 // fix bin for use by size calc below
1644 bin = cast(Bins)pool.pagetable[pn];
1647 ////////////////////////////////////////////////////////////////////
1649 ////////////////////////////////////////////////////////////////////
1651 info.size = binsize[bin];
1657 pt = &pool.pagetable[0];
1658 for (i = pn + 1; i < pool.npages; i++)
1660 if (pt[i] != B_PAGEPLUS)
1663 info.size = (i - pn) * PAGESIZE;
1666 ////////////////////////////////////////////////////////////////////
1668 ////////////////////////////////////////////////////////////////////
1670 info.attr = getAttr(pool, cast(size_t)(offset / 16));
1671 if (!(info.attr & BlkAttr.NO_SCAN))
1672 info.size -= (size_t*).sizeof; // bitmask
1679 * Compute bin for size.
1681 static Bins findBin(size_t size)
1690 else if (size <= 32)
1725 * Allocate a new pool of at least size bytes.
1726 * Sort it into pools.
1727 * Mark all memory in the pool as B_FREE.
1728 * Return the actual number of bytes reserved or 0 on error.
1730 size_t reserve(size_t size)
1732 size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
1733 Pool* pool = newPool(npages);
1737 return pool.npages * PAGESIZE;
1742 * Minimizes physical memory usage by returning free pools to the OS.
1750 for (n = 0; n < pools.length; n++)
1753 for (pn = 0; pn < pool.npages; pn++)
1755 if (cast(Bins)pool.pagetable[pn] != B_FREE)
1758 if (pn < pool.npages)
1764 minAddr = pools[0].baseAddr;
1765 maxAddr = pools[pools.length - 1].topAddr;
1770 * Allocate a chunk of memory that is larger than a page.
1771 * Return null if out of memory.
1773 void *bigAlloc(size_t size)
1783 npages = (size + PAGESIZE - 1) / PAGESIZE;
1787 // This code could use some refinement when repeatedly
1788 // allocating very large arrays.
1790 for (n = 0; n < pools.length; n++)
1793 pn = pool.allocPages(npages);
1808 freedpages = fullcollectshell();
1809 if (freedpages >= pools.length * ((POOLSIZE / PAGESIZE) / 4))
1814 // Release empty pools to prevent bloat
1816 // Allocate new pool
1817 pool = newPool(npages);
1823 pn = pool.allocPages(npages);
1824 assert(pn != OPFAIL);
1827 // Release empty pools to prevent bloat
1829 // Allocate new pool
1830 pool = newPool(npages);
1833 pn = pool.allocPages(npages);
1834 assert(pn != OPFAIL);
1844 pool.pagetable[pn] = B_PAGE;
1846 memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1);
1847 p = pool.baseAddr + pn * PAGESIZE;
1848 memset(cast(char *)p + size, 0, npages * PAGESIZE - size);
1849 if (opts.options.mem_stomp)
1850 memset(p, 0xF1, size);
1854 return null; // let mallocNoSync handle the error
1859 * Allocate a new pool with at least npages in it.
1860 * Sort it into pools.
1861 * Return null if failed.
1863 Pool *newPool(size_t npages)
1865 // Minimum of POOLSIZE
1866 if (npages < POOLSIZE/PAGESIZE)
1867 npages = POOLSIZE/PAGESIZE;
1868 else if (npages > POOLSIZE/PAGESIZE)
1870 // Give us 150% of requested size, so there's room to extend
1871 auto n = npages + (npages >> 1);
1872 if (n < size_t.max/PAGESIZE)
1876 // Allocate successively larger pools up to 8 megs
1879 size_t n = pools.length;
1881 n = 8; // cap pool size at 8 megs
1882 n *= (POOLSIZE / PAGESIZE);
1888 p.initialize(npages);
1895 Pool* pool = pools.insert_sorted(p);
1898 minAddr = pools[0].baseAddr;
1899 maxAddr = pools[pools.length - 1].topAddr;
1906 * Allocate a page of bin's.
1910 int allocPage(Bins bin)
1918 for (n = 0; n < pools.length; n++)
1921 pn = pool.allocPages(1);
1928 pool.pagetable[pn] = cast(ubyte)bin;
1930 // Convert page to free list
1931 size_t size = binsize[bin];
1932 List **b = &bucket[bin];
1934 p = pool.baseAddr + pn * PAGESIZE;
1935 ptop = p + PAGESIZE;
1936 for (; p < ptop; p += size)
1938 (cast(List *)p).next = *b;
1946 * Marks a range of memory using the conservative bit mask. Used for
1947 * the stack, for the data segment, and additional memory ranges.
1949 void mark_conservative(void* pbot, void* ptop)
1951 mark(pbot, ptop, PointerMap.init.bits.ptr);
1956 * Search a range of memory values and mark any pointers into the GC pool.
1958 void mark(void *pbot, void *ptop, size_t* pm_bitmask)
1960 const BITS_PER_WORD = size_t.sizeof * 8;
1962 void **p1 = cast(void **)pbot;
1963 void **p2 = cast(void **)ptop;
1967 size_t type_size = pm_bitmask[0];
1968 size_t* pm_bits = pm_bitmask + 1;
1970 //printf("marking range: %p -> %p\n", pbot, ptop);
1971 for (; p1 + type_size <= p2; p1 += type_size) {
1972 for (size_t n = 0; n < type_size; n++) {
1973 // scan bit set for this word
1974 if (!(pm_bits[n / BITS_PER_WORD] & (1 << (n % BITS_PER_WORD))))
1977 void* p = *(p1 + n);
1979 if (p < minAddr || p >= maxAddr)
1982 if ((cast(size_t)p & ~(PAGESIZE-1)) == pcache)
1985 Pool* pool = findPool(p);
1988 size_t offset = cast(size_t)(p - pool.baseAddr);
1990 size_t pn = offset / PAGESIZE;
1991 Bins bin = cast(Bins)pool.pagetable[pn];
1993 // Adjust bit to be at start of allocated memory block
1995 bit_i = (offset & notbinsize[bin]) >> 4;
1996 else if (bin == B_PAGEPLUS)
2002 while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS);
2003 bit_i = pn * (PAGESIZE / 16);
2007 // Don't mark bits in B_FREE pages
2011 if (bin >= B_PAGE) // Cache B_PAGE and B_PAGEPLUS lookups
2012 pcache = cast(size_t)p & ~(PAGESIZE-1);
2014 if (!pool.mark.test(bit_i))
2016 pool.mark.set(bit_i);
2017 if (!pool.noscan.test(bit_i))
2019 pool.scan.set(bit_i);
2026 anychanges |= changes;
2030 * Return number of full pages free'd.
2032 size_t fullcollectshell()
2034 stats.collection_started();
2036 stats.collection_finished();
2038 // The purpose of the 'shell' is to ensure all the registers
2039 // get put on the stack so they'll be scanned
2044 gcc.builtins.__builtin_unwind_init();
2051 uint eax,ecx,edx,ebx,ebp,esi,edi;
2064 else version (X86_64)
2066 ulong rax,rbx,rcx,rdx,rbp,rsi,rdi,r8,r9,r10,r11,r12,r13,r14,r15;
2069 movq rax[RBP], RAX ;
2070 movq rbx[RBP], RBX ;
2071 movq rcx[RBP], RCX ;
2072 movq rdx[RBP], RDX ;
2073 movq rbp[RBP], RBP ;
2074 movq rsi[RBP], RSI ;
2075 movq rdi[RBP], RDI ;
2078 movq r10[RBP], R10 ;
2079 movq r11[RBP], R11 ;
2080 movq r12[RBP], R12 ;
2081 movq r13[RBP], R13 ;
2082 movq r14[RBP], R14 ;
2083 movq r15[RBP], R15 ;
2089 static assert( false, "Architecture not supported." );
2100 result = fullcollect(sp);
2123 size_t fullcollect(void *stackTop)
2128 debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
2130 thread_suspendAll();
2131 stats.world_stopped();
2137 for (n = 0; n < pools.length; n++)
2142 pool.freebits.zero();
2145 // Mark each free entry, so it doesn't get scanned
2146 for (n = 0; n < B_PAGE; n++)
2148 for (List *list = bucket[n]; list; list = list.next)
2150 pool = findPool(list);
2152 pool.freebits.set(cast(size_t)(cast(byte*)list - pool.baseAddr) / 16);
2156 for (n = 0; n < pools.length; n++)
2159 pool.mark.copy(&pool.freebits);
2162 rt_scanStaticData( &mark_conservative );
2166 // Scan stacks and registers for each paused thread
2167 thread_scanAll( &mark_conservative, stackTop );
2171 debug(COLLECT_PRINTF) printf("scan roots[]\n");
2172 mark_conservative(roots.ptr, roots.ptr + roots.length);
2175 debug(COLLECT_PRINTF) printf("scan ranges[]\n");
2176 for (n = 0; n < ranges.length; n++)
2178 debug(COLLECT_PRINTF) printf("\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop);
2179 mark_conservative(ranges[n].pbot, ranges[n].ptop);
2182 debug(COLLECT_PRINTF) printf("\tscan heap\n");
2186 for (n = 0; n < pools.length; n++)
2194 bbase = pool.scan.base();
2195 btop = bbase + pool.scan.nwords;
2196 for (b = bbase; b < btop;)
2212 o = pool.baseAddr + (b - bbase) * 32 * 16;
2213 if (!(bitm & 0xFFFF))
2218 for (; bitm; o += 16, bitm >>= 1)
2223 pn = cast(size_t)(o - pool.baseAddr) / PAGESIZE;
2224 bin = cast(Bins)pool.pagetable[pn];
2226 if (opts.options.conservative)
2227 mark_conservative(o, o + binsize[bin]);
2229 auto end_of_blk = cast(size_t**)(o +
2230 binsize[bin] - size_t.sizeof);
2231 size_t* pm_bitmask = *end_of_blk;
2232 mark(o, end_of_blk, pm_bitmask);
2235 else if (bin == B_PAGE || bin == B_PAGEPLUS)
2237 if (bin == B_PAGEPLUS)
2239 while (pool.pagetable[pn - 1] != B_PAGE)
2243 while (pn + u < pool.npages &&
2244 pool.pagetable[pn + u] == B_PAGEPLUS)
2247 size_t blk_size = u * PAGESIZE;
2248 if (opts.options.conservative)
2249 mark_conservative(o, o + blk_size);
2251 auto end_of_blk = cast(size_t**)(o + blk_size -
2253 size_t* pm_bitmask = *end_of_blk;
2254 mark(o, end_of_blk, pm_bitmask);
2263 stats.world_started();
2265 // Free up everything not marked
2266 debug(COLLECT_PRINTF) printf("\tfree'ing\n");
2267 size_t freedpages = 0;
2269 for (n = 0; n < pools.length; n++)
2272 uint* bbase = pool.mark.base();
2274 for (pn = 0; pn < pool.npages; pn++, bbase += PAGESIZE / (32 * 16))
2276 Bins bin = cast(Bins)pool.pagetable[pn];
2280 auto size = binsize[bin];
2281 byte* p = pool.baseAddr + pn * PAGESIZE;
2282 byte* ptop = p + PAGESIZE;
2283 size_t bit_i = pn * (PAGESIZE/16);
2284 size_t bit_stride = size / 16;
2286 version(none) // BUG: doesn't work because freebits() must also be cleared
2288 // If free'd entire page
2289 if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 &&
2290 bbase[3] == 0 && bbase[4] == 0 && bbase[5] == 0 &&
2291 bbase[6] == 0 && bbase[7] == 0)
2293 for (; p < ptop; p += size, bit_i += bit_stride)
2295 if (pool.finals.nbits && pool.finals.testClear(bit_i)) {
2296 if (opts.options.sentinel)
2297 rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
2299 rt_finalize(cast(List *)p, false/*noStack > 0*/);
2301 gcx.clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
2303 List *list = cast(List *)p;
2305 if (opts.options.mem_stomp)
2306 memset(p, 0xF3, size);
2308 pool.pagetable[pn] = B_FREE;
2313 for (; p < ptop; p += size, bit_i += bit_stride)
2315 if (!pool.mark.test(bit_i))
2317 if (opts.options.sentinel)
2318 sentinel_Invariant(sentinel_add(p));
2320 pool.freebits.set(bit_i);
2321 if (pool.finals.nbits && pool.finals.testClear(bit_i)) {
2322 if (opts.options.sentinel)
2323 rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/);
2325 rt_finalize(cast(List *)p, false/*noStack > 0*/);
2327 clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
2329 List *list = cast(List *)p;
2331 if (opts.options.mem_stomp)
2332 memset(p, 0xF3, size);
2338 else if (bin == B_PAGE)
2340 size_t bit_i = pn * (PAGESIZE / 16);
2341 if (!pool.mark.test(bit_i))
2343 byte *p = pool.baseAddr + pn * PAGESIZE;
2344 if (opts.options.sentinel)
2345 sentinel_Invariant(sentinel_add(p));
2346 if (pool.finals.nbits && pool.finals.testClear(bit_i)) {
2347 if (opts.options.sentinel)
2348 rt_finalize(sentinel_add(p), false/*noStack > 0*/);
2350 rt_finalize(p, false/*noStack > 0*/);
2352 clrAttr(pool, bit_i, BlkAttr.ALL_BITS);
2354 debug(COLLECT_PRINTF) printf("\tcollecting big %x\n", p);
2355 pool.pagetable[pn] = B_FREE;
2357 if (opts.options.mem_stomp)
2358 memset(p, 0xF3, PAGESIZE);
2359 while (pn + 1 < pool.npages && pool.pagetable[pn + 1] == B_PAGEPLUS)
2362 pool.pagetable[pn] = B_FREE;
2365 if (opts.options.mem_stomp)
2368 memset(p, 0xF3, PAGESIZE);
2379 // Free complete pages, rebuild free list
2380 debug(COLLECT_PRINTF) printf("\tfree complete pages\n");
2381 size_t recoveredpages = 0;
2382 for (n = 0; n < pools.length; n++)
2385 for (size_t pn = 0; pn < pool.npages; pn++)
2387 Bins bin = cast(Bins)pool.pagetable[pn];
2393 size_t size = binsize[bin];
2394 size_t bit_stride = size / 16;
2395 size_t bit_base = pn * (PAGESIZE / 16);
2396 size_t bit_top = bit_base + (PAGESIZE / 16);
2400 for (; bit_i < bit_top; bit_i += bit_stride)
2402 if (!pool.freebits.test(bit_i))
2405 pool.pagetable[pn] = B_FREE;
2410 p = pool.baseAddr + pn * PAGESIZE;
2411 for (u = 0; u < PAGESIZE; u += size)
2413 bit_i = bit_base + u / 16;
2414 if (pool.freebits.test(bit_i))
2416 List *list = cast(List *)(p + u);
2417 // avoid unnecessary writes
2418 if (list.next != bucket[bin])
2419 list.next = bucket[bin];
2427 debug(COLLECT_PRINTF) printf("recovered pages = %d\n", recoveredpages);
2428 debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, pools.length);
2430 return freedpages + recoveredpages;
2437 uint getAttr(Pool* pool, size_t bit_i)
2446 if (pool.finals.nbits &&
2447 pool.finals.test(bit_i))
2448 attrs |= BlkAttr.FINALIZE;
2449 if (pool.noscan.test(bit_i))
2450 attrs |= BlkAttr.NO_SCAN;
2451 // if (pool.nomove.nbits &&
2452 // pool.nomove.test(bit_i))
2453 // attrs |= BlkAttr.NO_MOVE;
2461 void setAttr(Pool* pool, size_t bit_i, uint mask)
2468 if (mask & BlkAttr.FINALIZE)
2470 if (!pool.finals.nbits)
2471 pool.finals.alloc(pool.mark.nbits);
2472 pool.finals.set(bit_i);
2474 if (mask & BlkAttr.NO_SCAN)
2476 pool.noscan.set(bit_i);
2478 // if (mask & BlkAttr.NO_MOVE)
2480 // if (!pool.nomove.nbits)
2481 // pool.nomove.alloc(pool.mark.nbits);
2482 // pool.nomove.set(bit_i);
2490 void clrAttr(Pool* pool, size_t bit_i, uint mask)
2497 if (mask & BlkAttr.FINALIZE && pool.finals.nbits)
2498 pool.finals.clear(bit_i);
2499 if (mask & BlkAttr.NO_SCAN)
2500 pool.noscan.clear(bit_i);
2501 // if (mask & BlkAttr.NO_MOVE && pool.nomove.nbits)
2502 // pool.nomove.clear(bit_i);
2508 /* ============================ Pool =============================== */
2515 GCBits mark; // entries already scanned, or should not be scanned
2516 GCBits scan; // entries that need to be scanned
2517 GCBits freebits; // entries that are on the free list
2518 GCBits finals; // entries that need finalizer run on them
2519 GCBits noscan; // entries that should not be scanned
2525 void initialize(size_t npages)
2527 size_t poolsize = npages * PAGESIZE;
2528 assert(poolsize >= POOLSIZE);
2529 baseAddr = cast(byte *) alloc.os_mem_map(poolsize);
2531 // Some of the code depends on page alignment of memory pools
2532 assert((cast(size_t)baseAddr & (PAGESIZE - 1)) == 0);
2540 topAddr = baseAddr + poolsize;
2542 mark.alloc(cast(size_t)poolsize / 16);
2543 scan.alloc(cast(size_t)poolsize / 16);
2544 freebits.alloc(cast(size_t)poolsize / 16);
2545 noscan.alloc(cast(size_t)poolsize / 16);
2547 pagetable = cast(ubyte*) cstdlib.malloc(npages);
2549 onOutOfMemoryError();
2550 memset(pagetable, B_FREE, npages);
2552 this.npages = npages;
2564 result = alloc.os_mem_unmap(baseAddr, npages * PAGESIZE);
2572 // See Gcx.Dtor() for the rationale of the null check.
2574 cstdlib.free(pagetable);
2584 void Invariant() { }
2591 //freebits.Invariant();
2592 //finals.Invariant();
2593 //noscan.Invariant();
2597 //if (baseAddr + npages * PAGESIZE != topAddr)
2598 //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr);
2599 assert(baseAddr + npages * PAGESIZE == topAddr);
2602 for (size_t i = 0; i < npages; i++)
2604 Bins bin = cast(Bins)pagetable[i];
2605 assert(bin < B_MAX);
2611 * Allocate n pages from Pool.
2612 * Returns OPFAIL on failure.
2614 size_t allocPages(size_t n)
2620 for (i = 0; i < npages; i++)
2622 if (pagetable[i] == B_FREE)
2635 * Free npages pages starting with pagenum.
2637 void freePages(size_t pagenum, size_t npages)
2639 memset(&pagetable[pagenum], B_FREE, npages);
2644 * Used for sorting pools
2646 int opCmp(in Pool other)
2648 if (baseAddr < other.baseAddr)
2651 return cast(int)(baseAddr > other.baseAddr);
2656 /* ============================ SENTINEL =============================== */
2659 const size_t SENTINEL_PRE = cast(size_t) 0xF4F4F4F4F4F4F4F4UL; // 32 or 64 bits
2660 const ubyte SENTINEL_POST = 0xF5; // 8 bits
2661 const uint SENTINEL_EXTRA = 2 * size_t.sizeof + 1;
2664 size_t* sentinel_size(void *p) { return &(cast(size_t *)p)[-2]; }
2665 size_t* sentinel_pre(void *p) { return &(cast(size_t *)p)[-1]; }
2666 ubyte* sentinel_post(void *p) { return &(cast(ubyte *)p)[*sentinel_size(p)]; }
2669 void sentinel_init(void *p, size_t size)
2671 *sentinel_size(p) = size;
2672 *sentinel_pre(p) = SENTINEL_PRE;
2673 *sentinel_post(p) = SENTINEL_POST;
2677 void sentinel_Invariant(void *p)
2679 assert(*sentinel_pre(p) == SENTINEL_PRE);
2680 assert(*sentinel_post(p) == SENTINEL_POST);
2684 void *sentinel_add(void *p)
2686 return p + 2 * size_t.sizeof;
2690 void *sentinel_sub(void *p)
2692 return p - 2 * size_t.sizeof;
2696 // vim: set et sw=4 sts=4 :