summaryrefslogtreecommitdiff
path: root/libphobos/libdruntime/gc/impl/conservative/gc.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/libdruntime/gc/impl/conservative/gc.d')
-rw-r--r--libphobos/libdruntime/gc/impl/conservative/gc.d3413
1 files changed, 0 insertions, 3413 deletions
diff --git a/libphobos/libdruntime/gc/impl/conservative/gc.d b/libphobos/libdruntime/gc/impl/conservative/gc.d
deleted file mode 100644
index 300a32ad2b0..00000000000
--- a/libphobos/libdruntime/gc/impl/conservative/gc.d
+++ /dev/null
@@ -1,3413 +0,0 @@
-/**
- * Contains the garbage collector implementation.
- *
- * Copyright: Copyright Digital Mars 2001 - 2016.
- * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
- * Authors: Walter Bright, David Friedman, Sean Kelly
- */
-
-/* Copyright Digital Mars 2005 - 2016.
- * Distributed under the Boost Software License, Version 1.0.
- * (See accompanying file LICENSE or copy at
- * http://www.boost.org/LICENSE_1_0.txt)
- */
-module gc.impl.conservative.gc;
-
-// D Programming Language Garbage Collector implementation
-
-/************** Debugging ***************************/
-
-//debug = PRINTF; // turn on printf's
-//debug = COLLECT_PRINTF; // turn on printf's
-//debug = PRINTF_TO_FILE; // redirect printf's ouptut to file "gcx.log"
-//debug = LOGGING; // log allocations / frees
-//debug = MEMSTOMP; // stomp on memory
-//debug = SENTINEL; // add underrun/overrrun protection
- // NOTE: this needs to be enabled globally in the makefiles
- // (-debug=SENTINEL) to pass druntime's unittests.
-//debug = PTRCHECK; // more pointer checking
-//debug = PTRCHECK2; // thorough but slow pointer checking
-//debug = INVARIANT; // enable invariants
-//debug = PROFILE_API; // profile API calls for config.profile > 1
-
-/***************************************************/
-
-import gc.bits;
-import gc.os;
-import gc.config;
-import gc.gcinterface;
-
-import rt.util.container.treap;
-
-import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
-import core.stdc.string : memcpy, memset, memmove;
-import core.bitop;
-import core.thread;
-static import core.memory;
-
-version (GNU) import gcc.builtins;
-
-debug (PRINTF_TO_FILE) import core.stdc.stdio : sprintf, fprintf, fopen, fflush, FILE;
-else import core.stdc.stdio : sprintf, printf; // needed to output profiling results
-
-import core.time;
-alias currTime = MonoTime.currTime;
-
-debug(PRINTF_TO_FILE)
-{
- private __gshared MonoTime gcStartTick;
- private __gshared FILE* gcx_fh;
-
- private int printf(ARGS...)(const char* fmt, ARGS args) nothrow
- {
- if (!gcx_fh)
- gcx_fh = fopen("gcx.log", "w");
- if (!gcx_fh)
- return 0;
-
- int len;
- if (MonoTime.ticksPerSecond == 0)
- {
- len = fprintf(gcx_fh, "before init: ");
- }
- else
- {
- if (gcStartTick == MonoTime.init)
- gcStartTick = MonoTime.currTime;
- immutable timeElapsed = MonoTime.currTime - gcStartTick;
- immutable secondsAsDouble = timeElapsed.total!"hnsecs" / cast(double)convert!("seconds", "hnsecs")(1);
- len = fprintf(gcx_fh, "%10.6f: ", secondsAsDouble);
- }
- len += fprintf(gcx_fh, fmt, args);
- fflush(gcx_fh);
- return len;
- }
-}
-
-debug(PRINTF) void printFreeInfo(Pool* pool) nothrow
-{
- uint nReallyFree;
- foreach (i; 0..pool.npages) {
- if (pool.pagetable[i] >= B_FREE) nReallyFree++;
- }
-
- printf("Pool %p: %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages);
-}
-
-// Track total time spent preparing for GC,
-// marking, sweeping and recovering pages.
-__gshared Duration prepTime;
-__gshared Duration markTime;
-__gshared Duration sweepTime;
-__gshared Duration recoverTime;
-__gshared Duration maxPauseTime;
-__gshared size_t numCollections;
-__gshared size_t maxPoolMemory;
-
-__gshared long numMallocs;
-__gshared long numFrees;
-__gshared long numReallocs;
-__gshared long numExtends;
-__gshared long numOthers;
-__gshared long mallocTime; // using ticks instead of MonoTime for better performance
-__gshared long freeTime;
-__gshared long reallocTime;
-__gshared long extendTime;
-__gshared long otherTime;
-__gshared long lockTime;
-
-private
-{
- extern (C)
- {
- // to allow compilation of this module without access to the rt package,
- // make these functions available from rt.lifetime
- void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow;
- int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, in void[] segment) nothrow;
-
- // Declared as an extern instead of importing core.exception
- // to avoid inlining - see issue 13725.
- void onInvalidMemoryOperationError() @nogc nothrow;
- void onOutOfMemoryErrorNoGC() @nogc nothrow;
- }
-
- enum
- {
- OPFAIL = ~cast(size_t)0
- }
-}
-
-
-alias GC gc_t;
-
-
-/* ======================= Leak Detector =========================== */
-
-
-debug (LOGGING)
-{
- struct Log
- {
- void* p;
- size_t size;
- size_t line;
- char* file;
- void* parent;
-
- void print() nothrow
- {
- printf(" p = %p, size = %zd, parent = %p ", p, size, parent);
- if (file)
- {
- printf("%s(%u)", file, cast(uint)line);
- }
- printf("\n");
- }
- }
-
-
- struct LogArray
- {
- size_t dim;
- size_t allocdim;
- Log *data;
-
- void Dtor() nothrow
- {
- if (data)
- cstdlib.free(data);
- data = null;
- }
-
- void reserve(size_t nentries) nothrow
- {
- assert(dim <= allocdim);
- if (allocdim - dim < nentries)
- {
- allocdim = (dim + nentries) * 2;
- assert(dim + nentries <= allocdim);
- if (!data)
- {
- data = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
- if (!data && allocdim)
- onOutOfMemoryErrorNoGC();
- }
- else
- { Log *newdata;
-
- newdata = cast(Log*)cstdlib.malloc(allocdim * Log.sizeof);
- if (!newdata && allocdim)
- onOutOfMemoryErrorNoGC();
- memcpy(newdata, data, dim * Log.sizeof);
- cstdlib.free(data);
- data = newdata;
- }
- }
- }
-
-
- void push(Log log) nothrow
- {
- reserve(1);
- data[dim++] = log;
- }
-
- void remove(size_t i) nothrow
- {
- memmove(data + i, data + i + 1, (dim - i) * Log.sizeof);
- dim--;
- }
-
-
- size_t find(void *p) nothrow
- {
- for (size_t i = 0; i < dim; i++)
- {
- if (data[i].p == p)
- return i;
- }
- return OPFAIL; // not found
- }
-
-
- void copy(LogArray *from) nothrow
- {
- reserve(from.dim - dim);
- assert(from.dim <= allocdim);
- memcpy(data, from.data, from.dim * Log.sizeof);
- dim = from.dim;
- }
- }
-}
-
-
-/* ============================ GC =============================== */
-
-class ConservativeGC : GC
-{
- // For passing to debug code (not thread safe)
- __gshared size_t line;
- __gshared char* file;
-
- Gcx *gcx; // implementation
-
- import core.internal.spinlock;
- static gcLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy);
- static bool _inFinalizer;
-
- // lock GC, throw InvalidMemoryOperationError on recursive locking during finalization
- static void lockNR() @nogc nothrow
- {
- if (_inFinalizer)
- onInvalidMemoryOperationError();
- gcLock.lock();
- }
-
-
- static void initialize(ref GC gc)
- {
- import core.stdc.string: memcpy;
-
- if (config.gc != "conservative")
- return;
-
- auto p = cstdlib.malloc(__traits(classInstanceSize,ConservativeGC));
-
- if (!p)
- onOutOfMemoryErrorNoGC();
-
- auto init = typeid(ConservativeGC).initializer();
- assert(init.length == __traits(classInstanceSize, ConservativeGC));
- auto instance = cast(ConservativeGC) memcpy(p, init.ptr, init.length);
- instance.__ctor();
-
- gc = instance;
- }
-
-
- static void finalize(ref GC gc)
- {
- if (config.gc != "conservative")
- return;
-
- auto instance = cast(ConservativeGC) gc;
- instance.Dtor();
- cstdlib.free(cast(void*)instance);
- }
-
-
- this()
- {
- //config is assumed to have already been initialized
-
- gcx = cast(Gcx*)cstdlib.calloc(1, Gcx.sizeof);
- if (!gcx)
- onOutOfMemoryErrorNoGC();
- gcx.initialize();
-
- if (config.initReserve)
- gcx.reserve(config.initReserve << 20);
- if (config.disable)
- gcx.disabled++;
- }
-
-
- void Dtor()
- {
- version (linux)
- {
- //debug(PRINTF) printf("Thread %x ", pthread_self());
- //debug(PRINTF) printf("GC.Dtor()\n");
- }
-
- if (gcx)
- {
- gcx.Dtor();
- cstdlib.free(gcx);
- gcx = null;
- }
- }
-
-
- void enable()
- {
- static void go(Gcx* gcx) nothrow
- {
- assert(gcx.disabled > 0);
- gcx.disabled--;
- }
- runLocked!(go, otherTime, numOthers)(gcx);
- }
-
-
- void disable()
- {
- static void go(Gcx* gcx) nothrow
- {
- gcx.disabled++;
- }
- runLocked!(go, otherTime, numOthers)(gcx);
- }
-
-
- auto runLocked(alias func, Args...)(auto ref Args args)
- {
- debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0);
- lockNR();
- scope (failure) gcLock.unlock();
- debug(PROFILE_API) immutable tm2 = (config.profile > 1 ? currTime.ticks : 0);
-
- static if (is(typeof(func(args)) == void))
- func(args);
- else
- auto res = func(args);
-
- debug(PROFILE_API) if (config.profile > 1)
- lockTime += tm2 - tm;
- gcLock.unlock();
-
- static if (!is(typeof(func(args)) == void))
- return res;
- }
-
-
- auto runLocked(alias func, alias time, alias count, Args...)(auto ref Args args)
- {
- debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0);
- lockNR();
- scope (failure) gcLock.unlock();
- debug(PROFILE_API) immutable tm2 = (config.profile > 1 ? currTime.ticks : 0);
-
- static if (is(typeof(func(args)) == void))
- func(args);
- else
- auto res = func(args);
-
- debug(PROFILE_API) if (config.profile > 1)
- {
- count++;
- immutable now = currTime.ticks;
- lockTime += tm2 - tm;
- time += now - tm2;
- }
- gcLock.unlock();
-
- static if (!is(typeof(func(args)) == void))
- return res;
- }
-
-
- uint getAttr(void* p) nothrow
- {
- if (!p)
- {
- return 0;
- }
-
- static uint go(Gcx* gcx, void* p) nothrow
- {
- Pool* pool = gcx.findPool(p);
- uint oldb = 0;
-
- if (pool)
- {
- p = sentinel_sub(p);
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- oldb = pool.getBits(biti);
- }
- return oldb;
- }
-
- return runLocked!(go, otherTime, numOthers)(gcx, p);
- }
-
-
- uint setAttr(void* p, uint mask) nothrow
- {
- if (!p)
- {
- return 0;
- }
-
- static uint go(Gcx* gcx, void* p, uint mask) nothrow
- {
- Pool* pool = gcx.findPool(p);
- uint oldb = 0;
-
- if (pool)
- {
- p = sentinel_sub(p);
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- oldb = pool.getBits(biti);
- pool.setBits(biti, mask);
- }
- return oldb;
- }
-
- return runLocked!(go, otherTime, numOthers)(gcx, p, mask);
- }
-
-
- uint clrAttr(void* p, uint mask) nothrow
- {
- if (!p)
- {
- return 0;
- }
-
- static uint go(Gcx* gcx, void* p, uint mask) nothrow
- {
- Pool* pool = gcx.findPool(p);
- uint oldb = 0;
-
- if (pool)
- {
- p = sentinel_sub(p);
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- oldb = pool.getBits(biti);
- pool.clrBits(biti, mask);
- }
- return oldb;
- }
-
- return runLocked!(go, otherTime, numOthers)(gcx, p, mask);
- }
-
-
- void *malloc(size_t size, uint bits, const TypeInfo ti) nothrow
- {
- if (!size)
- {
- return null;
- }
-
- size_t localAllocSize = void;
-
- auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti);
-
- if (!(bits & BlkAttr.NO_SCAN))
- {
- memset(p + size, 0, localAllocSize - size);
- }
-
- return p;
- }
-
-
- //
- //
- //
- private void *mallocNoSync(size_t size, uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow
- {
- assert(size != 0);
-
- //debug(PRINTF) printf("GC::malloc(size = %d, gcx = %p)\n", size, gcx);
- assert(gcx);
- //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self());
-
- auto p = gcx.alloc(size + SENTINEL_EXTRA, alloc_size, bits);
- if (!p)
- onOutOfMemoryErrorNoGC();
-
- debug (SENTINEL)
- {
- p = sentinel_add(p);
- sentinel_init(p, size);
- alloc_size = size;
- }
- gcx.log_malloc(p, size);
-
- return p;
- }
-
-
- BlkInfo qalloc( size_t size, uint bits, const TypeInfo ti) nothrow
- {
-
- if (!size)
- {
- return BlkInfo.init;
- }
-
- BlkInfo retval;
-
- retval.base = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, retval.size, ti);
-
- if (!(bits & BlkAttr.NO_SCAN))
- {
- memset(retval.base + size, 0, retval.size - size);
- }
-
- retval.attr = bits;
- return retval;
- }
-
-
- void *calloc(size_t size, uint bits, const TypeInfo ti) nothrow
- {
- if (!size)
- {
- return null;
- }
-
- size_t localAllocSize = void;
-
- auto p = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, localAllocSize, ti);
-
- memset(p, 0, size);
- if (!(bits & BlkAttr.NO_SCAN))
- {
- memset(p + size, 0, localAllocSize - size);
- }
-
- return p;
- }
-
-
- void *realloc(void *p, size_t size, uint bits, const TypeInfo ti) nothrow
- {
- size_t localAllocSize = void;
- auto oldp = p;
-
- p = runLocked!(reallocNoSync, mallocTime, numMallocs)(p, size, bits, localAllocSize, ti);
-
- if (p !is oldp && !(bits & BlkAttr.NO_SCAN))
- {
- memset(p + size, 0, localAllocSize - size);
- }
-
- return p;
- }
-
-
- //
- // bits will be set to the resulting bits of the new block
- //
- private void *reallocNoSync(void *p, size_t size, ref uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow
- {
- if (!size)
- { if (p)
- { freeNoSync(p);
- p = null;
- }
- alloc_size = 0;
- }
- else if (!p)
- {
- p = mallocNoSync(size, bits, alloc_size, ti);
- }
- else
- { void *p2;
- size_t psize;
-
- //debug(PRINTF) printf("GC::realloc(p = %p, size = %zu)\n", p, size);
- debug (SENTINEL)
- {
- sentinel_Invariant(p);
- psize = *sentinel_size(p);
- if (psize != size)
- {
- if (psize)
- {
- Pool *pool = gcx.findPool(p);
-
- if (pool)
- {
- auto biti = cast(size_t)(sentinel_sub(p) - pool.baseAddr) >> pool.shiftBy;
-
- if (bits)
- {
- pool.clrBits(biti, ~BlkAttr.NONE);
- pool.setBits(biti, bits);
- }
- else
- {
- bits = pool.getBits(biti);
- }
- }
- }
- p2 = mallocNoSync(size, bits, alloc_size, ti);
- if (psize < size)
- size = psize;
- //debug(PRINTF) printf("\tcopying %d bytes\n",size);
- memcpy(p2, p, size);
- p = p2;
- }
- }
- else
- {
- auto pool = gcx.findPool(p);
- if (pool.isLargeObject)
- {
- auto lpool = cast(LargeObjectPool*) pool;
- psize = lpool.getSize(p); // get allocated size
-
- if (size <= PAGESIZE / 2)
- goto Lmalloc; // switching from large object pool to small object pool
-
- auto psz = psize / PAGESIZE;
- auto newsz = (size + PAGESIZE - 1) / PAGESIZE;
- if (newsz == psz)
- {
- alloc_size = psize;
- return p;
- }
-
- auto pagenum = lpool.pagenumOf(p);
-
- if (newsz < psz)
- { // Shrink in place
- debug (MEMSTOMP) memset(p + size, 0xF2, psize - size);
- lpool.freePages(pagenum + newsz, psz - newsz);
- }
- else if (pagenum + newsz <= pool.npages)
- { // Attempt to expand in place
- foreach (binsz; lpool.pagetable[pagenum + psz .. pagenum + newsz])
- if (binsz != B_FREE)
- goto Lmalloc;
-
- debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize);
- debug(PRINTF) printFreeInfo(pool);
- memset(&lpool.pagetable[pagenum + psz], B_PAGEPLUS, newsz - psz);
- gcx.usedLargePages += newsz - psz;
- lpool.freepages -= (newsz - psz);
- debug(PRINTF) printFreeInfo(pool);
- }
- else
- goto Lmalloc; // does not fit into current pool
-
- lpool.updateOffsets(pagenum);
- if (bits)
- {
- immutable biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
- pool.clrBits(biti, ~BlkAttr.NONE);
- pool.setBits(biti, bits);
- }
- alloc_size = newsz * PAGESIZE;
- return p;
- }
-
- psize = (cast(SmallObjectPool*) pool).getSize(p); // get allocated size
- if (psize < size || // if new size is bigger
- psize > size * 2) // or less than half
- {
- Lmalloc:
- if (psize && pool)
- {
- auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- if (bits)
- {
- pool.clrBits(biti, ~BlkAttr.NONE);
- pool.setBits(biti, bits);
- }
- else
- {
- bits = pool.getBits(biti);
- }
- }
- p2 = mallocNoSync(size, bits, alloc_size, ti);
- if (psize < size)
- size = psize;
- //debug(PRINTF) printf("\tcopying %d bytes\n",size);
- memcpy(p2, p, size);
- p = p2;
- }
- else
- alloc_size = psize;
- }
- }
- return p;
- }
-
-
- size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow
- {
- return runLocked!(extendNoSync, extendTime, numExtends)(p, minsize, maxsize, ti);
- }
-
-
- //
- //
- //
- private size_t extendNoSync(void* p, size_t minsize, size_t maxsize, const TypeInfo ti = null) nothrow
- in
- {
- assert(minsize <= maxsize);
- }
- body
- {
- //debug(PRINTF) printf("GC::extend(p = %p, minsize = %zu, maxsize = %zu)\n", p, minsize, maxsize);
- debug (SENTINEL)
- {
- return 0;
- }
- else
- {
- auto pool = gcx.findPool(p);
- if (!pool || !pool.isLargeObject)
- return 0;
-
- auto lpool = cast(LargeObjectPool*) pool;
- auto psize = lpool.getSize(p); // get allocated size
- if (psize < PAGESIZE)
- return 0; // cannot extend buckets
-
- auto psz = psize / PAGESIZE;
- auto minsz = (minsize + PAGESIZE - 1) / PAGESIZE;
- auto maxsz = (maxsize + PAGESIZE - 1) / PAGESIZE;
-
- auto pagenum = lpool.pagenumOf(p);
-
- size_t sz;
- for (sz = 0; sz < maxsz; sz++)
- {
- auto i = pagenum + psz + sz;
- if (i == lpool.npages)
- break;
- if (lpool.pagetable[i] != B_FREE)
- { if (sz < minsz)
- return 0;
- break;
- }
- }
- if (sz < minsz)
- return 0;
- debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE);
- memset(lpool.pagetable + pagenum + psz, B_PAGEPLUS, sz);
- lpool.updateOffsets(pagenum);
- lpool.freepages -= sz;
- gcx.usedLargePages += sz;
- return (psz + sz) * PAGESIZE;
- }
- }
-
-
- size_t reserve(size_t size) nothrow
- {
- if (!size)
- {
- return 0;
- }
-
- return runLocked!(reserveNoSync, otherTime, numOthers)(size);
- }
-
-
- //
- //
- //
- private size_t reserveNoSync(size_t size) nothrow
- {
- assert(size != 0);
- assert(gcx);
-
- return gcx.reserve(size);
- }
-
-
- void free(void *p) nothrow
- {
- if (!p || _inFinalizer)
- {
- return;
- }
-
- return runLocked!(freeNoSync, freeTime, numFrees)(p);
- }
-
-
- //
- //
- //
- private void freeNoSync(void *p) nothrow
- {
- debug(PRINTF) printf("Freeing %p\n", cast(size_t) p);
- assert (p);
-
- Pool* pool;
- size_t pagenum;
- Bins bin;
- size_t biti;
-
- // Find which page it is in
- pool = gcx.findPool(p);
- if (!pool) // if not one of ours
- return; // ignore
-
- pagenum = pool.pagenumOf(p);
-
- debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]);
- debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]);
-
- bin = cast(Bins)pool.pagetable[pagenum];
-
- // Verify that the pointer is at the beginning of a block,
- // no action should be taken if p is an interior pointer
- if (bin > B_PAGE) // B_PAGEPLUS or B_FREE
- return;
- if ((sentinel_sub(p) - pool.baseAddr) & (binsize[bin] - 1))
- return;
-
- sentinel_Invariant(p);
- p = sentinel_sub(p);
- biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
-
- pool.clrBits(biti, ~BlkAttr.NONE);
-
- if (pool.isLargeObject) // if large alloc
- {
- assert(bin == B_PAGE);
- auto lpool = cast(LargeObjectPool*) pool;
-
- // Free pages
- size_t npages = lpool.bPageOffsets[pagenum];
- debug (MEMSTOMP) memset(p, 0xF2, npages * PAGESIZE);
- lpool.freePages(pagenum, npages);
- }
- else
- { // Add to free list
- List *list = cast(List*)p;
-
- debug (MEMSTOMP) memset(p, 0xF2, binsize[bin]);
-
- list.next = gcx.bucket[bin];
- list.pool = pool;
- gcx.bucket[bin] = list;
- }
-
- gcx.log_free(sentinel_add(p));
- }
-
-
- void* addrOf(void *p) nothrow
- {
- if (!p)
- {
- return null;
- }
-
- return runLocked!(addrOfNoSync, otherTime, numOthers)(p);
- }
-
-
- //
- //
- //
- void* addrOfNoSync(void *p) nothrow
- {
- if (!p)
- {
- return null;
- }
-
- auto q = gcx.findBase(p);
- if (q)
- q = sentinel_add(q);
- return q;
- }
-
-
- size_t sizeOf(void *p) nothrow
- {
- if (!p)
- {
- return 0;
- }
-
- return runLocked!(sizeOfNoSync, otherTime, numOthers)(p);
- }
-
-
- //
- //
- //
- private size_t sizeOfNoSync(void *p) nothrow
- {
- assert (p);
-
- debug (SENTINEL)
- {
- p = sentinel_sub(p);
- size_t size = gcx.findSize(p);
-
- // Check for interior pointer
- // This depends on:
- // 1) size is a power of 2 for less than PAGESIZE values
- // 2) base of memory pool is aligned on PAGESIZE boundary
- if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
- size = 0;
- return size ? size - SENTINEL_EXTRA : 0;
- }
- else
- {
- size_t size = gcx.findSize(p);
-
- // Check for interior pointer
- // This depends on:
- // 1) size is a power of 2 for less than PAGESIZE values
- // 2) base of memory pool is aligned on PAGESIZE boundary
- if (cast(size_t)p & (size - 1) & (PAGESIZE - 1))
- return 0;
- return size;
- }
- }
-
-
- BlkInfo query(void *p) nothrow
- {
- if (!p)
- {
- BlkInfo i;
- return i;
- }
-
- return runLocked!(queryNoSync, otherTime, numOthers)(p);
- }
-
- //
- //
- //
- BlkInfo queryNoSync(void *p) nothrow
- {
- assert(p);
-
- BlkInfo info = gcx.getInfo(p);
- debug(SENTINEL)
- {
- if (info.base)
- {
- info.base = sentinel_add(info.base);
- info.size = *sentinel_size(info.base);
- }
- }
- return info;
- }
-
-
- /**
- * Verify that pointer p:
- * 1) belongs to this memory pool
- * 2) points to the start of an allocated piece of memory
- * 3) is not on a free list
- */
- void check(void *p) nothrow
- {
- if (!p)
- {
- return;
- }
-
- return runLocked!(checkNoSync, otherTime, numOthers)(p);
- }
-
-
- //
- //
- //
- private void checkNoSync(void *p) nothrow
- {
- assert(p);
-
- sentinel_Invariant(p);
- debug (PTRCHECK)
- {
- Pool* pool;
- size_t pagenum;
- Bins bin;
- size_t size;
-
- p = sentinel_sub(p);
- pool = gcx.findPool(p);
- assert(pool);
- pagenum = pool.pagenumOf(p);
- bin = cast(Bins)pool.pagetable[pagenum];
- assert(bin <= B_PAGE);
- size = binsize[bin];
- assert((cast(size_t)p & (size - 1)) == 0);
-
- debug (PTRCHECK2)
- {
- if (bin < B_PAGE)
- {
- // Check that p is not on a free list
- List *list;
-
- for (list = gcx.bucket[bin]; list; list = list.next)
- {
- assert(cast(void*)list != p);
- }
- }
- }
- }
- }
-
-
- void addRoot(void *p) nothrow @nogc
- {
- if (!p)
- {
- return;
- }
-
- gcx.addRoot(p);
- }
-
-
- void removeRoot(void *p) nothrow @nogc
- {
- if (!p)
- {
- return;
- }
-
- gcx.removeRoot(p);
- }
-
-
- @property RootIterator rootIter() @nogc
- {
- return &gcx.rootsApply;
- }
-
-
- void addRange(void *p, size_t sz, const TypeInfo ti = null) nothrow @nogc
- {
- if (!p || !sz)
- {
- return;
- }
-
- gcx.addRange(p, p + sz, ti);
- }
-
-
- void removeRange(void *p) nothrow @nogc
- {
- if (!p)
- {
- return;
- }
-
- gcx.removeRange(p);
- }
-
-
- @property RangeIterator rangeIter() @nogc
- {
- return &gcx.rangesApply;
- }
-
-
- void runFinalizers(in void[] segment) nothrow
- {
- static void go(Gcx* gcx, in void[] segment) nothrow
- {
- gcx.runFinalizers(segment);
- }
- return runLocked!(go, otherTime, numOthers)(gcx, segment);
- }
-
-
- bool inFinalizer() nothrow
- {
- return _inFinalizer;
- }
-
-
- void collect() nothrow
- {
- fullCollect();
- }
-
-
- void collectNoStack() nothrow
- {
- fullCollectNoStack();
- }
-
-
- /**
- * Do full garbage collection.
- * Return number of pages free'd.
- */
- size_t fullCollect() nothrow
- {
- debug(PRINTF) printf("GC.fullCollect()\n");
-
- // Since a finalizer could launch a new thread, we always need to lock
- // when collecting.
- static size_t go(Gcx* gcx) nothrow
- {
- return gcx.fullcollect();
- }
- immutable result = runLocked!go(gcx);
-
- version (none)
- {
- GCStats stats;
-
- getStats(stats);
- debug(PRINTF) printf("heapSize = %zx, freeSize = %zx\n",
- stats.heapSize, stats.freeSize);
- }
-
- gcx.log_collect();
- return result;
- }
-
-
- /**
- * do full garbage collection ignoring roots
- */
- void fullCollectNoStack() nothrow
- {
- // Since a finalizer could launch a new thread, we always need to lock
- // when collecting.
- static size_t go(Gcx* gcx) nothrow
- {
- return gcx.fullcollect(true);
- }
- runLocked!go(gcx);
- }
-
-
- void minimize() nothrow
- {
- static void go(Gcx* gcx) nothrow
- {
- gcx.minimize();
- }
- runLocked!(go, otherTime, numOthers)(gcx);
- }
-
-
- core.memory.GC.Stats stats() nothrow
- {
- typeof(return) ret;
-
- runLocked!(getStatsNoSync, otherTime, numOthers)(ret);
-
- return ret;
- }
-
-
- //
- //
- //
- private void getStatsNoSync(out core.memory.GC.Stats stats) nothrow
- {
- foreach (pool; gcx.pooltable[0 .. gcx.npools])
- {
- foreach (bin; pool.pagetable[0 .. pool.npages])
- {
- if (bin == B_FREE)
- stats.freeSize += PAGESIZE;
- else
- stats.usedSize += PAGESIZE;
- }
- }
-
- size_t freeListSize;
- foreach (n; 0 .. B_PAGE)
- {
- immutable sz = binsize[n];
- for (List *list = gcx.bucket[n]; list; list = list.next)
- freeListSize += sz;
- }
-
- stats.usedSize -= freeListSize;
- stats.freeSize += freeListSize;
- }
-}
-
-
-/* ============================ Gcx =============================== */
-
-enum
-{ PAGESIZE = 4096,
- POOLSIZE = (4096*256),
-}
-
-
-enum
-{
- B_16,
- B_32,
- B_64,
- B_128,
- B_256,
- B_512,
- B_1024,
- B_2048,
- B_PAGE, // start of large alloc
- B_PAGEPLUS, // continuation of large alloc
- B_FREE, // free page
- B_MAX
-}
-
-
-alias ubyte Bins;
-
-
-struct List
-{
- List *next;
- Pool *pool;
-}
-
-
-immutable uint[B_MAX] binsize = [ 16,32,64,128,256,512,1024,2048,4096 ];
-immutable size_t[B_MAX] notbinsize = [ ~(16-1),~(32-1),~(64-1),~(128-1),~(256-1),
- ~(512-1),~(1024-1),~(2048-1),~(4096-1) ];
-
-alias PageBits = GCBits.wordtype[PAGESIZE / 16 / GCBits.BITS_PER_WORD];
-static assert(PAGESIZE % (GCBits.BITS_PER_WORD * 16) == 0);
-
-private void set(ref PageBits bits, size_t i) @nogc pure nothrow
-{
- assert(i < PageBits.sizeof * 8);
- bts(bits.ptr, i);
-}
-
-/* ============================ Gcx =============================== */
-
-struct Gcx
-{
- import core.internal.spinlock;
- auto rootsLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
- auto rangesLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
- Treap!Root roots;
- Treap!Range ranges;
-
- bool log; // turn on logging
- debug(INVARIANT) bool initialized;
- uint disabled; // turn off collections if >0
-
- import gc.pooltable;
- @property size_t npools() pure const nothrow { return pooltable.length; }
- PoolTable!Pool pooltable;
-
- List*[B_PAGE] bucket; // free list for each small size
-
- // run a collection when reaching those thresholds (number of used pages)
- float smallCollectThreshold, largeCollectThreshold;
- uint usedSmallPages, usedLargePages;
- // total number of mapped pages
- uint mappedPages;
-
- void initialize()
- {
- (cast(byte*)&this)[0 .. Gcx.sizeof] = 0;
- log_init();
- roots.initialize();
- ranges.initialize();
- smallCollectThreshold = largeCollectThreshold = 0.0f;
- usedSmallPages = usedLargePages = 0;
- mappedPages = 0;
- //printf("gcx = %p, self = %x\n", &this, self);
- debug(INVARIANT) initialized = true;
- }
-
-
- void Dtor()
- {
- if (config.profile)
- {
- printf("\tNumber of collections: %llu\n", cast(ulong)numCollections);
- printf("\tTotal GC prep time: %lld milliseconds\n",
- prepTime.total!("msecs"));
- printf("\tTotal mark time: %lld milliseconds\n",
- markTime.total!("msecs"));
- printf("\tTotal sweep time: %lld milliseconds\n",
- sweepTime.total!("msecs"));
- printf("\tTotal page recovery time: %lld milliseconds\n",
- recoverTime.total!("msecs"));
- long maxPause = maxPauseTime.total!("msecs");
- printf("\tMax Pause Time: %lld milliseconds\n", maxPause);
- long gcTime = (recoverTime + sweepTime + markTime + prepTime).total!("msecs");
- printf("\tGrand total GC time: %lld milliseconds\n", gcTime);
- long pauseTime = (markTime + prepTime).total!("msecs");
-
- char[30] apitxt;
- apitxt[0] = 0;
- debug(PROFILE_API) if (config.profile > 1)
- {
- static Duration toDuration(long dur)
- {
- return MonoTime(dur) - MonoTime(0);
- }
-
- printf("\n");
- printf("\tmalloc: %llu calls, %lld ms\n", cast(ulong)numMallocs, toDuration(mallocTime).total!"msecs");
- printf("\trealloc: %llu calls, %lld ms\n", cast(ulong)numReallocs, toDuration(reallocTime).total!"msecs");
- printf("\tfree: %llu calls, %lld ms\n", cast(ulong)numFrees, toDuration(freeTime).total!"msecs");
- printf("\textend: %llu calls, %lld ms\n", cast(ulong)numExtends, toDuration(extendTime).total!"msecs");
- printf("\tother: %llu calls, %lld ms\n", cast(ulong)numOthers, toDuration(otherTime).total!"msecs");
- printf("\tlock time: %lld ms\n", toDuration(lockTime).total!"msecs");
-
- long apiTime = mallocTime + reallocTime + freeTime + extendTime + otherTime + lockTime;
- printf("\tGC API: %lld ms\n", toDuration(apiTime).total!"msecs");
- sprintf(apitxt.ptr, " API%5ld ms", toDuration(apiTime).total!"msecs");
- }
-
- printf("GC summary:%5lld MB,%5lld GC%5lld ms, Pauses%5lld ms <%5lld ms%s\n",
- cast(long) maxPoolMemory >> 20, cast(ulong)numCollections, gcTime,
- pauseTime, maxPause, apitxt.ptr);
- }
-
- debug(INVARIANT) initialized = false;
-
- for (size_t i = 0; i < npools; i++)
- {
- Pool *pool = pooltable[i];
- mappedPages -= pool.npages;
- pool.Dtor();
- cstdlib.free(pool);
- }
- assert(!mappedPages);
- pooltable.Dtor();
-
- roots.removeAll();
- ranges.removeAll();
- toscan.reset();
- }
-
-
- void Invariant() const { }
-
- debug(INVARIANT)
- invariant()
- {
- if (initialized)
- {
- //printf("Gcx.invariant(): this = %p\n", &this);
- pooltable.Invariant();
-
- rangesLock.lock();
- foreach (range; ranges)
- {
- assert(range.pbot);
- assert(range.ptop);
- assert(range.pbot <= range.ptop);
- }
- rangesLock.unlock();
-
- for (size_t i = 0; i < B_PAGE; i++)
- {
- for (auto list = cast(List*)bucket[i]; list; list = list.next)
- {
- }
- }
- }
- }
-
-
- /**
- *
- */
- void addRoot(void *p) nothrow @nogc
- {
- rootsLock.lock();
- scope (failure) rootsLock.unlock();
- roots.insert(Root(p));
- rootsLock.unlock();
- }
-
-
- /**
- *
- */
- void removeRoot(void *p) nothrow @nogc
- {
- rootsLock.lock();
- scope (failure) rootsLock.unlock();
- roots.remove(Root(p));
- rootsLock.unlock();
- }
-
-
- /**
- *
- */
- int rootsApply(scope int delegate(ref Root) nothrow dg) nothrow
- {
- rootsLock.lock();
- scope (failure) rootsLock.unlock();
- auto ret = roots.opApply(dg);
- rootsLock.unlock();
- return ret;
- }
-
-
- /**
- *
- */
- void addRange(void *pbot, void *ptop, const TypeInfo ti) nothrow @nogc
- {
- //debug(PRINTF) printf("Thread %x ", pthread_self());
- debug(PRINTF) printf("%p.Gcx::addRange(%p, %p)\n", &this, pbot, ptop);
- rangesLock.lock();
- scope (failure) rangesLock.unlock();
- ranges.insert(Range(pbot, ptop));
- rangesLock.unlock();
- }
-
-
- /**
- *
- */
- void removeRange(void *pbot) nothrow @nogc
- {
- //debug(PRINTF) printf("Thread %x ", pthread_self());
- debug(PRINTF) printf("Gcx.removeRange(%p)\n", pbot);
- rangesLock.lock();
- scope (failure) rangesLock.unlock();
- ranges.remove(Range(pbot, pbot)); // only pbot is used, see Range.opCmp
- rangesLock.unlock();
-
- // debug(PRINTF) printf("Wrong thread\n");
- // This is a fatal error, but ignore it.
- // The problem is that we can get a Close() call on a thread
- // other than the one the range was allocated on.
- //assert(zero);
- }
-
- /**
- *
- */
- int rangesApply(scope int delegate(ref Range) nothrow dg) nothrow
- {
- rangesLock.lock();
- scope (failure) rangesLock.unlock();
- auto ret = ranges.opApply(dg);
- rangesLock.unlock();
- return ret;
- }
-
-
- /**
- *
- */
- void runFinalizers(in void[] segment) nothrow
- {
- ConservativeGC._inFinalizer = true;
- scope (failure) ConservativeGC._inFinalizer = false;
-
- foreach (pool; pooltable[0 .. npools])
- {
- if (!pool.finals.nbits) continue;
-
- if (pool.isLargeObject)
- {
- auto lpool = cast(LargeObjectPool*) pool;
- lpool.runFinalizers(segment);
- }
- else
- {
- auto spool = cast(SmallObjectPool*) pool;
- spool.runFinalizers(segment);
- }
- }
- ConservativeGC._inFinalizer = false;
- }
-
- Pool* findPool(void* p) pure nothrow
- {
- return pooltable.findPool(p);
- }
-
- /**
- * Find base address of block containing pointer p.
- * Returns null if not a gc'd pointer
- */
- void* findBase(void *p) nothrow
- {
- Pool *pool;
-
- pool = findPool(p);
- if (pool)
- {
- size_t offset = cast(size_t)(p - pool.baseAddr);
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pool.pagetable[pn];
-
- // Adjust bit to be at start of allocated memory block
- if (bin <= B_PAGE)
- {
- return pool.baseAddr + (offset & notbinsize[bin]);
- }
- else if (bin == B_PAGEPLUS)
- {
- auto pageOffset = pool.bPageOffsets[pn];
- offset -= pageOffset * PAGESIZE;
- pn -= pageOffset;
-
- return pool.baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
- }
- else
- {
- // we are in a B_FREE page
- assert(bin == B_FREE);
- return null;
- }
- }
- return null;
- }
-
-
- /**
- * Find size of pointer p.
- * Returns 0 if not a gc'd pointer
- */
- size_t findSize(void *p) nothrow
- {
- Pool* pool = findPool(p);
- if (pool)
- return pool.slGetSize(p);
- return 0;
- }
-
- /**
- *
- */
- BlkInfo getInfo(void* p) nothrow
- {
- Pool* pool = findPool(p);
- if (pool)
- return pool.slGetInfo(p);
- return BlkInfo();
- }
-
- /**
- * Computes the bin table using CTFE.
- */
- static byte[2049] ctfeBins() nothrow
- {
- byte[2049] ret;
- size_t p = 0;
- for (Bins b = B_16; b <= B_2048; b++)
- for ( ; p <= binsize[b]; p++)
- ret[p] = b;
-
- return ret;
- }
-
- static const byte[2049] binTable = ctfeBins();
-
- /**
- * Allocate a new pool of at least size bytes.
- * Sort it into pooltable[].
- * Mark all memory in the pool as B_FREE.
- * Return the actual number of bytes reserved or 0 on error.
- */
- size_t reserve(size_t size) nothrow
- {
- size_t npages = (size + PAGESIZE - 1) / PAGESIZE;
-
- // Assume reserve() is for small objects.
- Pool* pool = newPool(npages, false);
-
- if (!pool)
- return 0;
- return pool.npages * PAGESIZE;
- }
-
- /**
- * Update the thresholds for when to collect the next time
- */
- void updateCollectThresholds() nothrow
- {
- static float max(float a, float b) nothrow
- {
- return a >= b ? a : b;
- }
-
- // instantly increases, slowly decreases
- static float smoothDecay(float oldVal, float newVal) nothrow
- {
- // decay to 63.2% of newVal over 5 collections
- // http://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
- enum alpha = 1.0 / (5 + 1);
- immutable decay = (newVal - oldVal) * alpha + oldVal;
- return max(newVal, decay);
- }
-
- immutable smTarget = usedSmallPages * config.heapSizeFactor;
- smallCollectThreshold = smoothDecay(smallCollectThreshold, smTarget);
- immutable lgTarget = usedLargePages * config.heapSizeFactor;
- largeCollectThreshold = smoothDecay(largeCollectThreshold, lgTarget);
- }
-
- /**
- * Minimizes physical memory usage by returning free pools to the OS.
- */
- void minimize() nothrow
- {
- debug(PRINTF) printf("Minimizing.\n");
-
- foreach (pool; pooltable.minimize())
- {
- debug(PRINTF) printFreeInfo(pool);
- mappedPages -= pool.npages;
- pool.Dtor();
- cstdlib.free(pool);
- }
-
- debug(PRINTF) printf("Done minimizing.\n");
- }
-
- private @property bool lowMem() const nothrow
- {
- return isLowOnMem(mappedPages * PAGESIZE);
- }
-
- void* alloc(size_t size, ref size_t alloc_size, uint bits) nothrow
- {
- return size <= 2048 ? smallAlloc(binTable[size], alloc_size, bits)
- : bigAlloc(size, alloc_size, bits);
- }
-
- void* smallAlloc(Bins bin, ref size_t alloc_size, uint bits) nothrow
- {
- alloc_size = binsize[bin];
-
- void* p;
- bool tryAlloc() nothrow
- {
- if (!bucket[bin])
- {
- bucket[bin] = allocPage(bin);
- if (!bucket[bin])
- return false;
- }
- p = bucket[bin];
- return true;
- }
-
- if (!tryAlloc())
- {
- if (!lowMem && (disabled || usedSmallPages < smallCollectThreshold))
- {
- // disabled or threshold not reached => allocate a new pool instead of collecting
- if (!newPool(1, false))
- {
- // out of memory => try to free some memory
- fullcollect();
- if (lowMem) minimize();
- }
- }
- else
- {
- fullcollect();
- if (lowMem) minimize();
- }
- // tryAlloc will succeed if a new pool was allocated above, if it fails allocate a new pool now
- if (!tryAlloc() && (!newPool(1, false) || !tryAlloc()))
- // out of luck or memory
- onOutOfMemoryErrorNoGC();
- }
- assert(p !is null);
-
- // Return next item from free list
- bucket[bin] = (cast(List*)p).next;
- auto pool = (cast(List*)p).pool;
- if (bits)
- pool.setBits((p - pool.baseAddr) >> pool.shiftBy, bits);
- //debug(PRINTF) printf("\tmalloc => %p\n", p);
- debug (MEMSTOMP) memset(p, 0xF0, alloc_size);
- return p;
- }
-
- /**
- * Allocate a chunk of memory that is larger than a page.
- * Return null if out of memory.
- */
- void* bigAlloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti = null) nothrow
- {
- debug(PRINTF) printf("In bigAlloc. Size: %d\n", size);
-
- LargeObjectPool* pool;
- size_t pn;
- immutable npages = (size + PAGESIZE - 1) / PAGESIZE;
- if (npages == 0)
- onOutOfMemoryErrorNoGC(); // size just below size_t.max requested
-
- bool tryAlloc() nothrow
- {
- foreach (p; pooltable[0 .. npools])
- {
- if (!p.isLargeObject || p.freepages < npages)
- continue;
- auto lpool = cast(LargeObjectPool*) p;
- if ((pn = lpool.allocPages(npages)) == OPFAIL)
- continue;
- pool = lpool;
- return true;
- }
- return false;
- }
-
- bool tryAllocNewPool() nothrow
- {
- pool = cast(LargeObjectPool*) newPool(npages, true);
- if (!pool) return false;
- pn = pool.allocPages(npages);
- assert(pn != OPFAIL);
- return true;
- }
-
- if (!tryAlloc())
- {
- if (!lowMem && (disabled || usedLargePages < largeCollectThreshold))
- {
- // disabled or threshold not reached => allocate a new pool instead of collecting
- if (!tryAllocNewPool())
- {
- // disabled but out of memory => try to free some memory
- fullcollect();
- minimize();
- }
- }
- else
- {
- fullcollect();
- minimize();
- }
- // If alloc didn't yet succeed retry now that we collected/minimized
- if (!pool && !tryAlloc() && !tryAllocNewPool())
- // out of luck or memory
- return null;
- }
- assert(pool);
-
- debug(PRINTF) printFreeInfo(&pool.base);
- pool.pagetable[pn] = B_PAGE;
- if (npages > 1)
- memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1);
- pool.updateOffsets(pn);
- usedLargePages += npages;
- pool.freepages -= npages;
-
- debug(PRINTF) printFreeInfo(&pool.base);
-
- auto p = pool.baseAddr + pn * PAGESIZE;
- debug(PRINTF) printf("Got large alloc: %p, pt = %d, np = %d\n", p, pool.pagetable[pn], npages);
- debug (MEMSTOMP) memset(p, 0xF1, size);
- alloc_size = npages * PAGESIZE;
- //debug(PRINTF) printf("\tp = %p\n", p);
-
- if (bits)
- pool.setBits(pn, bits);
- return p;
- }
-
-
- /**
- * Allocate a new pool with at least npages in it.
- * Sort it into pooltable[].
- * Return null if failed.
- */
- Pool *newPool(size_t npages, bool isLargeObject) nothrow
- {
- //debug(PRINTF) printf("************Gcx::newPool(npages = %d)****************\n", npages);
-
- // Minimum of POOLSIZE
- size_t minPages = (config.minPoolSize << 20) / PAGESIZE;
- if (npages < minPages)
- npages = minPages;
- else if (npages > minPages)
- { // Give us 150% of requested size, so there's room to extend
- auto n = npages + (npages >> 1);
- if (n < size_t.max/PAGESIZE)
- npages = n;
- }
-
- // Allocate successively larger pools up to 8 megs
- if (npools)
- { size_t n;
-
- n = config.minPoolSize + config.incPoolSize * npools;
- if (n > config.maxPoolSize)
- n = config.maxPoolSize; // cap pool size
- n *= (1 << 20) / PAGESIZE; // convert MB to pages
- if (npages < n)
- npages = n;
- }
-
- //printf("npages = %d\n", npages);
-
- auto pool = cast(Pool *)cstdlib.calloc(1, isLargeObject ? LargeObjectPool.sizeof : SmallObjectPool.sizeof);
- if (pool)
- {
- pool.initialize(npages, isLargeObject);
- if (!pool.baseAddr || !pooltable.insert(pool))
- {
- pool.Dtor();
- cstdlib.free(pool);
- return null;
- }
- }
-
- mappedPages += npages;
-
- if (config.profile)
- {
- if (mappedPages * PAGESIZE > maxPoolMemory)
- maxPoolMemory = mappedPages * PAGESIZE;
- }
- return pool;
- }
-
- /**
- * Allocate a page of bin's.
- * Returns:
- * head of a single linked list of new entries
- */
- List* allocPage(Bins bin) nothrow
- {
- //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin);
- for (size_t n = 0; n < npools; n++)
- {
- Pool* pool = pooltable[n];
- if (pool.isLargeObject)
- continue;
- if (List* p = (cast(SmallObjectPool*)pool).allocPage(bin))
- {
- ++usedSmallPages;
- return p;
- }
- }
- return null;
- }
-
- static struct ToScanStack
- {
- nothrow:
- @disable this(this);
-
- void reset()
- {
- _length = 0;
- os_mem_unmap(_p, _cap * Range.sizeof);
- _p = null;
- _cap = 0;
- }
-
- void push(Range rng)
- {
- if (_length == _cap) grow();
- _p[_length++] = rng;
- }
-
- Range pop()
- in { assert(!empty); }
- body
- {
- return _p[--_length];
- }
-
- ref inout(Range) opIndex(size_t idx) inout
- in { assert(idx < _length); }
- body
- {
- return _p[idx];
- }
-
- @property size_t length() const { return _length; }
- @property bool empty() const { return !length; }
-
- private:
- void grow()
- {
- enum initSize = 64 * 1024; // Windows VirtualAlloc granularity
- immutable ncap = _cap ? 2 * _cap : initSize / Range.sizeof;
- auto p = cast(Range*)os_mem_map(ncap * Range.sizeof);
- if (p is null) onOutOfMemoryErrorNoGC();
- if (_p !is null)
- {
- p[0 .. _length] = _p[0 .. _length];
- os_mem_unmap(_p, _cap * Range.sizeof);
- }
- _p = p;
- _cap = ncap;
- }
-
- size_t _length;
- Range* _p;
- size_t _cap;
- }
-
- ToScanStack toscan;
-
- /**
- * Search a range of memory values and mark any pointers into the GC pool.
- */
- void mark(void *pbot, void *ptop) scope nothrow
- {
- void **p1 = cast(void **)pbot;
- void **p2 = cast(void **)ptop;
-
- // limit the amount of ranges added to the toscan stack
- enum FANOUT_LIMIT = 32;
- size_t stackPos;
- Range[FANOUT_LIMIT] stack = void;
-
- Lagain:
- size_t pcache = 0;
-
- // let dmd allocate a register for this.pools
- auto pools = pooltable.pools;
- const highpool = pooltable.npools - 1;
- const minAddr = pooltable.minAddr;
- const maxAddr = pooltable.maxAddr;
-
- //printf("marking range: [%p..%p] (%#zx)\n", p1, p2, cast(size_t)p2 - cast(size_t)p1);
- Lnext: for (; p1 < p2; p1++)
- {
- auto p = *p1;
-
- //if (log) debug(PRINTF) printf("\tmark %p\n", p);
- if (p >= minAddr && p < maxAddr)
- {
- if ((cast(size_t)p & ~cast(size_t)(PAGESIZE-1)) == pcache)
- continue;
-
- Pool* pool = void;
- size_t low = 0;
- size_t high = highpool;
- while (true)
- {
- size_t mid = (low + high) >> 1;
- pool = pools[mid];
- if (p < pool.baseAddr)
- high = mid - 1;
- else if (p >= pool.topAddr)
- low = mid + 1;
- else break;
-
- if (low > high)
- continue Lnext;
- }
- size_t offset = cast(size_t)(p - pool.baseAddr);
- size_t biti = void;
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pool.pagetable[pn];
- void* base = void;
-
- //debug(PRINTF) printf("\t\tfound pool %p, base=%p, pn = %zd, bin = %d, biti = x%x\n", pool, pool.baseAddr, pn, bin, biti);
-
- // Adjust bit to be at start of allocated memory block
- if (bin < B_PAGE)
- {
- // We don't care abou setting pointsToBase correctly
- // because it's ignored for small object pools anyhow.
- auto offsetBase = offset & notbinsize[bin];
- biti = offsetBase >> pool.shiftBy;
- base = pool.baseAddr + offsetBase;
- //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
-
- if (!pool.mark.set(biti) && !pool.noscan.test(biti)) {
- stack[stackPos++] = Range(base, base + binsize[bin]);
- if (stackPos == stack.length)
- break;
- }
- }
- else if (bin == B_PAGE)
- {
- auto offsetBase = offset & notbinsize[bin];
- base = pool.baseAddr + offsetBase;
- biti = offsetBase >> pool.shiftBy;
- //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
-
- pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
-
- // For the NO_INTERIOR attribute. This tracks whether
- // the pointer is an interior pointer or points to the
- // base address of a block.
- bool pointsToBase = (base == sentinel_sub(p));
- if (!pointsToBase && pool.nointerior.nbits && pool.nointerior.test(biti))
- continue;
-
- if (!pool.mark.set(biti) && !pool.noscan.test(biti)) {
- stack[stackPos++] = Range(base, base + pool.bPageOffsets[pn] * PAGESIZE);
- if (stackPos == stack.length)
- break;
- }
- }
- else if (bin == B_PAGEPLUS)
- {
- pn -= pool.bPageOffsets[pn];
- base = pool.baseAddr + (pn * PAGESIZE);
- biti = pn * (PAGESIZE >> pool.shiftBy);
-
- pcache = cast(size_t)p & ~cast(size_t)(PAGESIZE-1);
- if (pool.nointerior.nbits && pool.nointerior.test(biti))
- continue;
-
- if (!pool.mark.set(biti) && !pool.noscan.test(biti)) {
- stack[stackPos++] = Range(base, base + pool.bPageOffsets[pn] * PAGESIZE);
- if (stackPos == stack.length)
- break;
- }
- }
- else
- {
- // Don't mark bits in B_FREE pages
- assert(bin == B_FREE);
- continue;
- }
- }
- }
-
- Range next=void;
- if (p1 < p2)
- {
- // local stack is full, push it to the global stack
- assert(stackPos == stack.length);
- toscan.push(Range(p1, p2));
- // reverse order for depth-first-order traversal
- foreach_reverse (ref rng; stack[0 .. $ - 1])
- toscan.push(rng);
- stackPos = 0;
- next = stack[$-1];
- }
- else if (stackPos)
- {
- // pop range from local stack and recurse
- next = stack[--stackPos];
- }
- else if (!toscan.empty)
- {
- // pop range from global stack and recurse
- next = toscan.pop();
- }
- else
- {
- // nothing more to do
- return;
- }
- p1 = cast(void**)next.pbot;
- p2 = cast(void**)next.ptop;
- // printf(" pop [%p..%p] (%#zx)\n", p1, p2, cast(size_t)p2 - cast(size_t)p1);
- goto Lagain;
- }
-
- // collection step 1: prepare freebits and mark bits
- void prepare() nothrow
- {
- size_t n;
- Pool* pool;
-
- for (n = 0; n < npools; n++)
- {
- pool = pooltable[n];
- pool.mark.zero();
- if (!pool.isLargeObject) pool.freebits.zero();
- }
-
- debug(COLLECT_PRINTF) printf("Set bits\n");
-
- // Mark each free entry, so it doesn't get scanned
- for (n = 0; n < B_PAGE; n++)
- {
- for (List *list = bucket[n]; list; list = list.next)
- {
- pool = list.pool;
- assert(pool);
- pool.freebits.set(cast(size_t)(cast(void*)list - pool.baseAddr) / 16);
- }
- }
-
- debug(COLLECT_PRINTF) printf("Marked free entries.\n");
-
- for (n = 0; n < npools; n++)
- {
- pool = pooltable[n];
- if (!pool.isLargeObject)
- {
- pool.mark.copy(&pool.freebits);
- }
- }
- }
-
- // collection step 2: mark roots and heap
- void markAll(bool nostack) nothrow
- {
- if (!nostack)
- {
- debug(COLLECT_PRINTF) printf("\tscan stacks.\n");
- // Scan stacks and registers for each paused thread
- thread_scanAll(&mark);
- }
-
- // Scan roots[]
- debug(COLLECT_PRINTF) printf("\tscan roots[]\n");
- foreach (root; roots)
- {
- mark(cast(void*)&root.proot, cast(void*)(&root.proot + 1));
- }
-
- // Scan ranges[]
- debug(COLLECT_PRINTF) printf("\tscan ranges[]\n");
- //log++;
- foreach (range; ranges)
- {
- debug(COLLECT_PRINTF) printf("\t\t%p .. %p\n", range.pbot, range.ptop);
- mark(range.pbot, range.ptop);
- }
- //log--;
- }
-
- // collection step 3: free all unreferenced objects
- size_t sweep() nothrow
- {
- // Free up everything not marked
- debug(COLLECT_PRINTF) printf("\tfree'ing\n");
- size_t freedLargePages;
- size_t freed;
- for (size_t n = 0; n < npools; n++)
- {
- size_t pn;
- Pool* pool = pooltable[n];
-
- if (pool.isLargeObject)
- {
- for (pn = 0; pn < pool.npages; pn++)
- {
- Bins bin = cast(Bins)pool.pagetable[pn];
- if (bin > B_PAGE) continue;
- size_t biti = pn;
-
- if (!pool.mark.test(biti))
- {
- void *p = pool.baseAddr + pn * PAGESIZE;
- void* q = sentinel_add(p);
- sentinel_Invariant(q);
-
- if (pool.finals.nbits && pool.finals.clear(biti))
- {
- size_t size = pool.bPageOffsets[pn] * PAGESIZE - SENTINEL_EXTRA;
- uint attr = pool.getBits(biti);
- rt_finalizeFromGC(q, size, attr);
- }
-
- pool.clrBits(biti, ~BlkAttr.NONE ^ BlkAttr.FINALIZE);
-
- debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
- log_free(q);
- pool.pagetable[pn] = B_FREE;
- if (pn < pool.searchStart) pool.searchStart = pn;
- freedLargePages++;
- pool.freepages++;
-
- debug (MEMSTOMP) memset(p, 0xF3, PAGESIZE);
- while (pn + 1 < pool.npages && pool.pagetable[pn + 1] == B_PAGEPLUS)
- {
- pn++;
- pool.pagetable[pn] = B_FREE;
-
- // Don't need to update searchStart here because
- // pn is guaranteed to be greater than last time
- // we updated it.
-
- pool.freepages++;
- freedLargePages++;
-
- debug (MEMSTOMP)
- { p += PAGESIZE;
- memset(p, 0xF3, PAGESIZE);
- }
- }
- pool.largestFree = pool.freepages; // invalidate
- }
- }
- }
- else
- {
-
- for (pn = 0; pn < pool.npages; pn++)
- {
- Bins bin = cast(Bins)pool.pagetable[pn];
-
- if (bin < B_PAGE)
- {
- immutable size = binsize[bin];
- void *p = pool.baseAddr + pn * PAGESIZE;
- void *ptop = p + PAGESIZE;
- immutable base = pn * (PAGESIZE/16);
- immutable bitstride = size / 16;
-
- bool freeBits;
- PageBits toFree;
-
- for (size_t i; p < ptop; p += size, i += bitstride)
- {
- immutable biti = base + i;
-
- if (!pool.mark.test(biti))
- {
- void* q = sentinel_add(p);
- sentinel_Invariant(q);
-
- if (pool.finals.nbits && pool.finals.test(biti))
- rt_finalizeFromGC(q, size - SENTINEL_EXTRA, pool.getBits(biti));
-
- freeBits = true;
- toFree.set(i);
-
- debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p);
- log_free(sentinel_add(p));
-
- debug (MEMSTOMP) memset(p, 0xF3, size);
-
- freed += size;
- }
- }
-
- if (freeBits)
- pool.freePageBits(pn, toFree);
- }
- }
- }
- }
-
- assert(freedLargePages <= usedLargePages);
- usedLargePages -= freedLargePages;
- debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedLargePages, npools);
- return freedLargePages;
- }
-
- // collection step 4: recover pages with no live objects, rebuild free lists
- size_t recover() nothrow
- {
- // init tail list
- List**[B_PAGE] tail = void;
- foreach (i, ref next; tail)
- next = &bucket[i];
-
- // Free complete pages, rebuild free list
- debug(COLLECT_PRINTF) printf("\tfree complete pages\n");
- size_t freedSmallPages;
- for (size_t n = 0; n < npools; n++)
- {
- size_t pn;
- Pool* pool = pooltable[n];
-
- if (pool.isLargeObject)
- continue;
-
- for (pn = 0; pn < pool.npages; pn++)
- {
- Bins bin = cast(Bins)pool.pagetable[pn];
- size_t biti;
- size_t u;
-
- if (bin < B_PAGE)
- {
- size_t size = binsize[bin];
- size_t bitstride = size / 16;
- size_t bitbase = pn * (PAGESIZE / 16);
- size_t bittop = bitbase + (PAGESIZE / 16);
- void* p;
-
- biti = bitbase;
- for (biti = bitbase; biti < bittop; biti += bitstride)
- {
- if (!pool.freebits.test(biti))
- goto Lnotfree;
- }
- pool.pagetable[pn] = B_FREE;
- if (pn < pool.searchStart) pool.searchStart = pn;
- pool.freepages++;
- freedSmallPages++;
- continue;
-
- Lnotfree:
- p = pool.baseAddr + pn * PAGESIZE;
- for (u = 0; u < PAGESIZE; u += size)
- {
- biti = bitbase + u / 16;
- if (!pool.freebits.test(biti))
- continue;
- auto elem = cast(List *)(p + u);
- elem.pool = pool;
- *tail[bin] = elem;
- tail[bin] = &elem.next;
- }
- }
- }
- }
- // terminate tail list
- foreach (ref next; tail)
- *next = null;
-
- assert(freedSmallPages <= usedSmallPages);
- usedSmallPages -= freedSmallPages;
- debug(COLLECT_PRINTF) printf("\trecovered pages = %d\n", freedSmallPages);
- return freedSmallPages;
- }
-
- /**
- * Return number of full pages free'd.
- */
- size_t fullcollect(bool nostack = false) nothrow
- {
- MonoTime start, stop, begin;
-
- if (config.profile)
- {
- begin = start = currTime;
- }
-
- debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n");
- //printf("\tpool address range = %p .. %p\n", minAddr, maxAddr);
-
- {
- // lock roots and ranges around suspending threads b/c they're not reentrant safe
- rangesLock.lock();
- rootsLock.lock();
- scope (exit)
- {
- rangesLock.unlock();
- rootsLock.unlock();
- }
- thread_suspendAll();
-
- prepare();
-
- if (config.profile)
- {
- stop = currTime;
- prepTime += (stop - start);
- start = stop;
- }
-
- markAll(nostack);
-
- thread_processGCMarks(&isMarked);
- thread_resumeAll();
- }
-
- if (config.profile)
- {
- stop = currTime;
- markTime += (stop - start);
- Duration pause = stop - begin;
- if (pause > maxPauseTime)
- maxPauseTime = pause;
- start = stop;
- }
-
- ConservativeGC._inFinalizer = true;
- size_t freedLargePages=void;
- {
- scope (failure) ConservativeGC._inFinalizer = false;
- freedLargePages = sweep();
- ConservativeGC._inFinalizer = false;
- }
-
- if (config.profile)
- {
- stop = currTime;
- sweepTime += (stop - start);
- start = stop;
- }
-
- immutable freedSmallPages = recover();
-
- if (config.profile)
- {
- stop = currTime;
- recoverTime += (stop - start);
- ++numCollections;
- }
-
- updateCollectThresholds();
-
- return freedLargePages + freedSmallPages;
- }
-
- /**
- * Returns true if the addr lies within a marked block.
- *
- * Warning! This should only be called while the world is stopped inside
- * the fullcollect function.
- */
- int isMarked(void *addr) scope nothrow
- {
- // first, we find the Pool this block is in, then check to see if the
- // mark bit is clear.
- auto pool = findPool(addr);
- if (pool)
- {
- auto offset = cast(size_t)(addr - pool.baseAddr);
- auto pn = offset / PAGESIZE;
- auto bins = cast(Bins)pool.pagetable[pn];
- size_t biti = void;
- if (bins <= B_PAGE)
- {
- biti = (offset & notbinsize[bins]) >> pool.shiftBy;
- }
- else if (bins == B_PAGEPLUS)
- {
- pn -= pool.bPageOffsets[pn];
- biti = pn * (PAGESIZE >> pool.shiftBy);
- }
- else // bins == B_FREE
- {
- assert(bins == B_FREE);
- return IsMarked.no;
- }
- return pool.mark.test(biti) ? IsMarked.yes : IsMarked.no;
- }
- return IsMarked.unknown;
- }
-
-
- /***** Leak Detector ******/
-
-
- debug (LOGGING)
- {
- LogArray current;
- LogArray prev;
-
-
- void log_init()
- {
- //debug(PRINTF) printf("+log_init()\n");
- current.reserve(1000);
- prev.reserve(1000);
- //debug(PRINTF) printf("-log_init()\n");
- }
-
-
- void log_malloc(void *p, size_t size) nothrow
- {
- //debug(PRINTF) printf("+log_malloc(p = %p, size = %zd)\n", p, size);
- Log log;
-
- log.p = p;
- log.size = size;
- log.line = GC.line;
- log.file = GC.file;
- log.parent = null;
-
- GC.line = 0;
- GC.file = null;
-
- current.push(log);
- //debug(PRINTF) printf("-log_malloc()\n");
- }
-
-
- void log_free(void *p) nothrow
- {
- //debug(PRINTF) printf("+log_free(%p)\n", p);
- auto i = current.find(p);
- if (i == OPFAIL)
- {
- debug(PRINTF) printf("free'ing unallocated memory %p\n", p);
- }
- else
- current.remove(i);
- //debug(PRINTF) printf("-log_free()\n");
- }
-
-
- void log_collect() nothrow
- {
- //debug(PRINTF) printf("+log_collect()\n");
- // Print everything in current that is not in prev
-
- debug(PRINTF) printf("New pointers this cycle: --------------------------------\n");
- size_t used = 0;
- for (size_t i = 0; i < current.dim; i++)
- {
- auto j = prev.find(current.data[i].p);
- if (j == OPFAIL)
- current.data[i].print();
- else
- used++;
- }
-
- debug(PRINTF) printf("All roots this cycle: --------------------------------\n");
- for (size_t i = 0; i < current.dim; i++)
- {
- void* p = current.data[i].p;
- if (!findPool(current.data[i].parent))
- {
- auto j = prev.find(current.data[i].p);
- debug(PRINTF) printf(j == OPFAIL ? "N" : " ");
- current.data[i].print();
- }
- }
-
- debug(PRINTF) printf("Used = %d-------------------------------------------------\n", used);
- prev.copy(&current);
-
- debug(PRINTF) printf("-log_collect()\n");
- }
-
-
- void log_parent(void *p, void *parent) nothrow
- {
- //debug(PRINTF) printf("+log_parent()\n");
- auto i = current.find(p);
- if (i == OPFAIL)
- {
- debug(PRINTF) printf("parent'ing unallocated memory %p, parent = %p\n", p, parent);
- Pool *pool;
- pool = findPool(p);
- assert(pool);
- size_t offset = cast(size_t)(p - pool.baseAddr);
- size_t biti;
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pool.pagetable[pn];
- biti = (offset & notbinsize[bin]);
- debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti);
- }
- else
- {
- current.data[i].parent = parent;
- }
- //debug(PRINTF) printf("-log_parent()\n");
- }
-
- }
- else
- {
- void log_init() nothrow { }
- void log_malloc(void *p, size_t size) nothrow { }
- void log_free(void *p) nothrow { }
- void log_collect() nothrow { }
- void log_parent(void *p, void *parent) nothrow { }
- }
-}
-
-/* ============================ Pool =============================== */
-
-struct Pool
-{
- void* baseAddr;
- void* topAddr;
- GCBits mark; // entries already scanned, or should not be scanned
- GCBits freebits; // entries that are on the free list
- GCBits finals; // entries that need finalizer run on them
- GCBits structFinals;// struct entries that need a finalzier run on them
- GCBits noscan; // entries that should not be scanned
- GCBits appendable; // entries that are appendable
- GCBits nointerior; // interior pointers should be ignored.
- // Only implemented for large object pools.
- size_t npages;
- size_t freepages; // The number of pages not in use.
- ubyte* pagetable;
-
- bool isLargeObject;
-
- uint shiftBy; // shift count for the divisor used for determining bit indices.
-
- // This tracks how far back we have to go to find the nearest B_PAGE at
- // a smaller address than a B_PAGEPLUS. To save space, we use a uint.
- // This limits individual allocations to 16 terabytes, assuming a 4k
- // pagesize.
- uint* bPageOffsets;
-
- // This variable tracks a conservative estimate of where the first free
- // page in this pool is, so that if a lot of pages towards the beginning
- // are occupied, we can bypass them in O(1).
- size_t searchStart;
- size_t largestFree; // upper limit for largest free chunk in large object pool
-
- void initialize(size_t npages, bool isLargeObject) nothrow
- {
- this.isLargeObject = isLargeObject;
- size_t poolsize;
-
- shiftBy = isLargeObject ? 12 : 4;
-
- //debug(PRINTF) printf("Pool::Pool(%u)\n", npages);
- poolsize = npages * PAGESIZE;
- assert(poolsize >= POOLSIZE);
- baseAddr = cast(byte *)os_mem_map(poolsize);
-
- // Some of the code depends on page alignment of memory pools
- assert((cast(size_t)baseAddr & (PAGESIZE - 1)) == 0);
-
- if (!baseAddr)
- {
- //debug(PRINTF) printf("GC fail: poolsize = x%zx, errno = %d\n", poolsize, errno);
- //debug(PRINTF) printf("message = '%s'\n", sys_errlist[errno]);
-
- npages = 0;
- poolsize = 0;
- }
- //assert(baseAddr);
- topAddr = baseAddr + poolsize;
- auto nbits = cast(size_t)poolsize >> shiftBy;
-
- mark.alloc(nbits);
-
- // pagetable already keeps track of what's free for the large object
- // pool.
- if (!isLargeObject)
- {
- freebits.alloc(nbits);
- }
-
- noscan.alloc(nbits);
- appendable.alloc(nbits);
-
- pagetable = cast(ubyte*)cstdlib.malloc(npages);
- if (!pagetable)
- onOutOfMemoryErrorNoGC();
-
- if (isLargeObject)
- {
- bPageOffsets = cast(uint*)cstdlib.malloc(npages * uint.sizeof);
- if (!bPageOffsets)
- onOutOfMemoryErrorNoGC();
- }
-
- memset(pagetable, B_FREE, npages);
-
- this.npages = npages;
- this.freepages = npages;
- this.searchStart = 0;
- this.largestFree = npages;
- }
-
-
- void Dtor() nothrow
- {
- if (baseAddr)
- {
- int result;
-
- if (npages)
- {
- result = os_mem_unmap(baseAddr, npages * PAGESIZE);
- assert(result == 0);
- npages = 0;
- }
-
- baseAddr = null;
- topAddr = null;
- }
- if (pagetable)
- {
- cstdlib.free(pagetable);
- pagetable = null;
- }
-
- if (bPageOffsets)
- cstdlib.free(bPageOffsets);
-
- mark.Dtor();
- if (isLargeObject)
- {
- nointerior.Dtor();
- }
- else
- {
- freebits.Dtor();
- }
- finals.Dtor();
- structFinals.Dtor();
- noscan.Dtor();
- appendable.Dtor();
- }
-
- /**
- *
- */
- uint getBits(size_t biti) nothrow
- {
- uint bits;
-
- if (finals.nbits && finals.test(biti))
- bits |= BlkAttr.FINALIZE;
- if (structFinals.nbits && structFinals.test(biti))
- bits |= BlkAttr.STRUCTFINAL;
- if (noscan.test(biti))
- bits |= BlkAttr.NO_SCAN;
- if (nointerior.nbits && nointerior.test(biti))
- bits |= BlkAttr.NO_INTERIOR;
- if (appendable.test(biti))
- bits |= BlkAttr.APPENDABLE;
- return bits;
- }
-
- /**
- *
- */
- void clrBits(size_t biti, uint mask) nothrow
- {
- immutable dataIndex = biti >> GCBits.BITS_SHIFT;
- immutable bitOffset = biti & GCBits.BITS_MASK;
- immutable keep = ~(GCBits.BITS_1 << bitOffset);
-
- if (mask & BlkAttr.FINALIZE && finals.nbits)
- finals.data[dataIndex] &= keep;
-
- if (structFinals.nbits && (mask & BlkAttr.STRUCTFINAL))
- structFinals.data[dataIndex] &= keep;
-
- if (mask & BlkAttr.NO_SCAN)
- noscan.data[dataIndex] &= keep;
- if (mask & BlkAttr.APPENDABLE)
- appendable.data[dataIndex] &= keep;
- if (nointerior.nbits && (mask & BlkAttr.NO_INTERIOR))
- nointerior.data[dataIndex] &= keep;
- }
-
- /**
- *
- */
- void setBits(size_t biti, uint mask) nothrow
- {
- // Calculate the mask and bit offset once and then use it to
- // set all of the bits we need to set.
- immutable dataIndex = biti >> GCBits.BITS_SHIFT;
- immutable bitOffset = biti & GCBits.BITS_MASK;
- immutable orWith = GCBits.BITS_1 << bitOffset;
-
- if (mask & BlkAttr.STRUCTFINAL)
- {
- if (!structFinals.nbits)
- structFinals.alloc(mark.nbits);
- structFinals.data[dataIndex] |= orWith;
- }
-
- if (mask & BlkAttr.FINALIZE)
- {
- if (!finals.nbits)
- finals.alloc(mark.nbits);
- finals.data[dataIndex] |= orWith;
- }
-
- if (mask & BlkAttr.NO_SCAN)
- {
- noscan.data[dataIndex] |= orWith;
- }
-// if (mask & BlkAttr.NO_MOVE)
-// {
-// if (!nomove.nbits)
-// nomove.alloc(mark.nbits);
-// nomove.data[dataIndex] |= orWith;
-// }
- if (mask & BlkAttr.APPENDABLE)
- {
- appendable.data[dataIndex] |= orWith;
- }
-
- if (isLargeObject && (mask & BlkAttr.NO_INTERIOR))
- {
- if (!nointerior.nbits)
- nointerior.alloc(mark.nbits);
- nointerior.data[dataIndex] |= orWith;
- }
- }
-
- void freePageBits(size_t pagenum, in ref PageBits toFree) nothrow
- {
- assert(!isLargeObject);
- assert(!nointerior.nbits); // only for large objects
-
- import core.internal.traits : staticIota;
- immutable beg = pagenum * (PAGESIZE / 16 / GCBits.BITS_PER_WORD);
- foreach (i; staticIota!(0, PageBits.length))
- {
- immutable w = toFree[i];
- if (!w) continue;
-
- immutable wi = beg + i;
- freebits.data[wi] |= w;
- noscan.data[wi] &= ~w;
- appendable.data[wi] &= ~w;
- }
-
- if (finals.nbits)
- {
- foreach (i; staticIota!(0, PageBits.length))
- if (toFree[i])
- finals.data[beg + i] &= ~toFree[i];
- }
-
- if (structFinals.nbits)
- {
- foreach (i; staticIota!(0, PageBits.length))
- if (toFree[i])
- structFinals.data[beg + i] &= ~toFree[i];
- }
- }
-
- /**
- * Given a pointer p in the p, return the pagenum.
- */
- size_t pagenumOf(void *p) const nothrow
- in
- {
- assert(p >= baseAddr);
- assert(p < topAddr);
- }
- body
- {
- return cast(size_t)(p - baseAddr) / PAGESIZE;
- }
-
- @property bool isFree() const pure nothrow
- {
- return npages == freepages;
- }
-
- size_t slGetSize(void* p) nothrow
- {
- if (isLargeObject)
- return (cast(LargeObjectPool*)&this).getSize(p);
- else
- return (cast(SmallObjectPool*)&this).getSize(p);
- }
-
- BlkInfo slGetInfo(void* p) nothrow
- {
- if (isLargeObject)
- return (cast(LargeObjectPool*)&this).getInfo(p);
- else
- return (cast(SmallObjectPool*)&this).getInfo(p);
- }
-
-
- void Invariant() const {}
-
- debug(INVARIANT)
- invariant()
- {
- //mark.Invariant();
- //scan.Invariant();
- //freebits.Invariant();
- //finals.Invariant();
- //structFinals.Invariant();
- //noscan.Invariant();
- //appendable.Invariant();
- //nointerior.Invariant();
-
- if (baseAddr)
- {
- //if (baseAddr + npages * PAGESIZE != topAddr)
- //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr);
- assert(baseAddr + npages * PAGESIZE == topAddr);
- }
-
- if (pagetable !is null)
- {
- for (size_t i = 0; i < npages; i++)
- {
- Bins bin = cast(Bins)pagetable[i];
- assert(bin < B_MAX);
- }
- }
- }
-}
-
-struct LargeObjectPool
-{
- Pool base;
- alias base this;
-
- void updateOffsets(size_t fromWhere) nothrow
- {
- assert(pagetable[fromWhere] == B_PAGE);
- size_t pn = fromWhere + 1;
- for (uint offset = 1; pn < npages; pn++, offset++)
- {
- if (pagetable[pn] != B_PAGEPLUS) break;
- bPageOffsets[pn] = offset;
- }
-
- // Store the size of the block in bPageOffsets[fromWhere].
- bPageOffsets[fromWhere] = cast(uint) (pn - fromWhere);
- }
-
- /**
- * Allocate n pages from Pool.
- * Returns OPFAIL on failure.
- */
- size_t allocPages(size_t n) nothrow
- {
- if (largestFree < n || searchStart + n > npages)
- return OPFAIL;
-
- //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n);
- size_t largest = 0;
- if (pagetable[searchStart] == B_PAGEPLUS)
- {
- searchStart -= bPageOffsets[searchStart]; // jump to B_PAGE
- searchStart += bPageOffsets[searchStart];
- }
- while (searchStart < npages && pagetable[searchStart] == B_PAGE)
- searchStart += bPageOffsets[searchStart];
-
- for (size_t i = searchStart; i < npages; )
- {
- assert(pagetable[i] == B_FREE);
- size_t p = 1;
- while (p < n && i + p < npages && pagetable[i + p] == B_FREE)
- p++;
-
- if (p == n)
- return i;
-
- if (p > largest)
- largest = p;
-
- i += p;
- while (i < npages && pagetable[i] == B_PAGE)
- {
- // we have the size information, so we skip a whole bunch of pages.
- i += bPageOffsets[i];
- }
- }
-
- // not enough free pages found, remember largest free chunk
- largestFree = largest;
- return OPFAIL;
- }
-
- /**
- * Free npages pages starting with pagenum.
- */
- void freePages(size_t pagenum, size_t npages) nothrow
- {
- //memset(&pagetable[pagenum], B_FREE, npages);
- if (pagenum < searchStart)
- searchStart = pagenum;
-
- for (size_t i = pagenum; i < npages + pagenum; i++)
- {
- if (pagetable[i] < B_FREE)
- {
- freepages++;
- }
-
- pagetable[i] = B_FREE;
- }
- largestFree = freepages; // invalidate
- }
-
- /**
- * Get size of pointer p in pool.
- */
- size_t getSize(void *p) const nothrow
- in
- {
- assert(p >= baseAddr);
- assert(p < topAddr);
- }
- body
- {
- size_t pagenum = pagenumOf(p);
- Bins bin = cast(Bins)pagetable[pagenum];
- assert(bin == B_PAGE);
- return bPageOffsets[pagenum] * PAGESIZE;
- }
-
- /**
- *
- */
- BlkInfo getInfo(void* p) nothrow
- {
- BlkInfo info;
-
- size_t offset = cast(size_t)(p - baseAddr);
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pagetable[pn];
-
- if (bin == B_PAGEPLUS)
- pn -= bPageOffsets[pn];
- else if (bin != B_PAGE)
- return info; // no info for free pages
-
- info.base = baseAddr + pn * PAGESIZE;
- info.size = bPageOffsets[pn] * PAGESIZE;
-
- info.attr = getBits(pn);
- return info;
- }
-
- void runFinalizers(in void[] segment) nothrow
- {
- foreach (pn; 0 .. npages)
- {
- Bins bin = cast(Bins)pagetable[pn];
- if (bin > B_PAGE)
- continue;
- size_t biti = pn;
-
- if (!finals.test(biti))
- continue;
-
- auto p = sentinel_add(baseAddr + pn * PAGESIZE);
- size_t size = bPageOffsets[pn] * PAGESIZE - SENTINEL_EXTRA;
- uint attr = getBits(biti);
-
- if (!rt_hasFinalizerInSegment(p, size, attr, segment))
- continue;
-
- rt_finalizeFromGC(p, size, attr);
-
- clrBits(biti, ~BlkAttr.NONE);
-
- if (pn < searchStart)
- searchStart = pn;
-
- debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
- //log_free(sentinel_add(p));
-
- size_t n = 1;
- for (; pn + n < npages; ++n)
- if (pagetable[pn + n] != B_PAGEPLUS)
- break;
- debug (MEMSTOMP) memset(baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE);
- freePages(pn, n);
- }
- }
-}
-
-
-struct SmallObjectPool
-{
- Pool base;
- alias base this;
-
- /**
- * Get size of pointer p in pool.
- */
- size_t getSize(void *p) const nothrow
- in
- {
- assert(p >= baseAddr);
- assert(p < topAddr);
- }
- body
- {
- size_t pagenum = pagenumOf(p);
- Bins bin = cast(Bins)pagetable[pagenum];
- assert(bin < B_PAGE);
- return binsize[bin];
- }
-
- BlkInfo getInfo(void* p) nothrow
- {
- BlkInfo info;
- size_t offset = cast(size_t)(p - baseAddr);
- size_t pn = offset / PAGESIZE;
- Bins bin = cast(Bins)pagetable[pn];
-
- if (bin >= B_PAGE)
- return info;
-
- info.base = cast(void*)((cast(size_t)p) & notbinsize[bin]);
- info.size = binsize[bin];
- offset = info.base - baseAddr;
- info.attr = getBits(cast(size_t)(offset >> shiftBy));
-
- return info;
- }
-
- void runFinalizers(in void[] segment) nothrow
- {
- foreach (pn; 0 .. npages)
- {
- Bins bin = cast(Bins)pagetable[pn];
- if (bin >= B_PAGE)
- continue;
-
- immutable size = binsize[bin];
- auto p = baseAddr + pn * PAGESIZE;
- const ptop = p + PAGESIZE;
- immutable base = pn * (PAGESIZE/16);
- immutable bitstride = size / 16;
-
- bool freeBits;
- PageBits toFree;
-
- for (size_t i; p < ptop; p += size, i += bitstride)
- {
- immutable biti = base + i;
-
- if (!finals.test(biti))
- continue;
-
- auto q = sentinel_add(p);
- uint attr = getBits(biti);
-
- if (!rt_hasFinalizerInSegment(q, size, attr, segment))
- continue;
-
- rt_finalizeFromGC(q, size, attr);
-
- freeBits = true;
- toFree.set(i);
-
- debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p);
- //log_free(sentinel_add(p));
-
- debug (MEMSTOMP) memset(p, 0xF3, size);
- }
-
- if (freeBits)
- freePageBits(pn, toFree);
- }
- }
-
- /**
- * Allocate a page of bin's.
- * Returns:
- * head of a single linked list of new entries
- */
- List* allocPage(Bins bin) nothrow
- {
- size_t pn;
- for (pn = searchStart; pn < npages; pn++)
- if (pagetable[pn] == B_FREE)
- goto L1;
-
- return null;
-
- L1:
- searchStart = pn + 1;
- pagetable[pn] = cast(ubyte)bin;
- freepages--;
-
- // Convert page to free list
- size_t size = binsize[bin];
- void* p = baseAddr + pn * PAGESIZE;
- void* ptop = p + PAGESIZE - size;
- auto first = cast(List*) p;
-
- for (; p < ptop; p += size)
- {
- (cast(List *)p).next = cast(List *)(p + size);
- (cast(List *)p).pool = &base;
- }
- (cast(List *)p).next = null;
- (cast(List *)p).pool = &base;
- return first;
- }
-}
-
-unittest // bugzilla 14467
-{
- int[] arr = new int[10];
- assert(arr.capacity);
- arr = arr[$..$];
- assert(arr.capacity);
-}
-
-unittest // bugzilla 15353
-{
- import core.memory : GC;
-
- static struct Foo
- {
- ~this()
- {
- GC.free(buf); // ignored in finalizer
- }
-
- void* buf;
- }
- new Foo(GC.malloc(10));
- GC.collect();
-}
-
-unittest // bugzilla 15822
-{
- import core.memory : GC;
-
- ubyte[16] buf;
- static struct Foo
- {
- ~this()
- {
- GC.removeRange(ptr);
- GC.removeRoot(ptr);
- }
-
- ubyte* ptr;
- }
- GC.addRoot(buf.ptr);
- GC.addRange(buf.ptr, buf.length);
- new Foo(buf.ptr);
- GC.collect();
-}
-
-unittest // bugzilla 1180
-{
- import core.exception;
- try
- {
- size_t x = size_t.max - 100;
- byte[] big_buf = new byte[x];
- }
- catch (OutOfMemoryError)
- {
- }
-}
-
-/* ============================ SENTINEL =============================== */
-
-
-debug (SENTINEL)
-{
- const size_t SENTINEL_PRE = cast(size_t) 0xF4F4F4F4F4F4F4F4UL; // 32 or 64 bits
- const ubyte SENTINEL_POST = 0xF5; // 8 bits
- const uint SENTINEL_EXTRA = 2 * size_t.sizeof + 1;
-
-
- inout(size_t*) sentinel_size(inout void *p) nothrow { return &(cast(inout size_t *)p)[-2]; }
- inout(size_t*) sentinel_pre(inout void *p) nothrow { return &(cast(inout size_t *)p)[-1]; }
- inout(ubyte*) sentinel_post(inout void *p) nothrow { return &(cast(inout ubyte *)p)[*sentinel_size(p)]; }
-
-
- void sentinel_init(void *p, size_t size) nothrow
- {
- *sentinel_size(p) = size;
- *sentinel_pre(p) = SENTINEL_PRE;
- *sentinel_post(p) = SENTINEL_POST;
- }
-
-
- void sentinel_Invariant(const void *p) nothrow
- {
- debug
- {
- assert(*sentinel_pre(p) == SENTINEL_PRE);
- assert(*sentinel_post(p) == SENTINEL_POST);
- }
- else if (*sentinel_pre(p) != SENTINEL_PRE || *sentinel_post(p) != SENTINEL_POST)
- onInvalidMemoryOperationError(); // also trigger in release build
- }
-
-
- void *sentinel_add(void *p) nothrow
- {
- return p + 2 * size_t.sizeof;
- }
-
-
- void *sentinel_sub(void *p) nothrow
- {
- return p - 2 * size_t.sizeof;
- }
-}
-else
-{
- const uint SENTINEL_EXTRA = 0;
-
-
- void sentinel_init(void *p, size_t size) nothrow
- {
- }
-
-
- void sentinel_Invariant(const void *p) nothrow
- {
- }
-
-
- void *sentinel_add(void *p) nothrow
- {
- return p;
- }
-
-
- void *sentinel_sub(void *p) nothrow
- {
- return p;
- }
-}
-
-debug (MEMSTOMP)
-unittest
-{
- import core.memory;
- auto p = cast(uint*)GC.malloc(uint.sizeof*3);
- assert(*p == 0xF0F0F0F0);
- p[2] = 0; // First two will be used for free list
- GC.free(p);
- assert(p[2] == 0xF2F2F2F2);
-}
-
-debug (SENTINEL)
-unittest
-{
- import core.memory;
- auto p = cast(ubyte*)GC.malloc(1);
- assert(p[-1] == 0xF4);
- assert(p[ 1] == 0xF5);
-/*
- p[1] = 0;
- bool thrown;
- try
- GC.free(p);
- catch (Error e)
- thrown = true;
- p[1] = 0xF5;
- assert(thrown);
-*/
-}
-
-unittest
-{
- import core.memory;
-
- // https://issues.dlang.org/show_bug.cgi?id=9275
- GC.removeRoot(null);
- GC.removeRoot(cast(void*)13);
-}
-
-// improve predictability of coverage of code that is eventually not hit by other tests
-unittest
-{
- import core.memory;
- auto p = GC.malloc(260 << 20); // new pool has 390 MB
- auto q = GC.malloc(65 << 20); // next chunk (larger than 64MB to ensure the same pool is used)
- auto r = GC.malloc(65 << 20); // another chunk in same pool
- assert(p + (260 << 20) == q);
- assert(q + (65 << 20) == r);
- GC.free(q);
- // should trigger "assert(bin == B_FREE);" in mark due to dangling pointer q:
- GC.collect();
- // should trigger "break;" in extendNoSync:
- size_t sz = GC.extend(p, 64 << 20, 66 << 20); // trigger size after p large enough (but limited)
- assert(sz == 325 << 20);
- GC.free(p);
- GC.free(r);
- r = q; // ensure q is not trashed before collection above
-
- p = GC.malloc(70 << 20); // from the same pool
- q = GC.malloc(70 << 20);
- r = GC.malloc(70 << 20);
- auto s = GC.malloc(70 << 20);
- auto t = GC.malloc(70 << 20); // 350 MB of 390 MB used
- assert(p + (70 << 20) == q);
- assert(q + (70 << 20) == r);
- assert(r + (70 << 20) == s);
- assert(s + (70 << 20) == t);
- GC.free(r); // ensure recalculation of largestFree in nxxt allocPages
- auto z = GC.malloc(75 << 20); // needs new pool
-
- GC.free(p);
- GC.free(q);
- GC.free(s);
- GC.free(t);
- GC.free(z);
- GC.minimize(); // release huge pool
-}
-