diff --git a/changelog/lazy-gc-init.dd b/changelog/lazy-gc-init.dd new file mode 100644 index 0000000000..cf33158843 --- /dev/null +++ b/changelog/lazy-gc-init.dd @@ -0,0 +1,3 @@ +the garbage collector is now lazily initialized on first use + +The runtime now lazily initializes the GC on first use, thus allowing applications that do not use the GC to skip its initialization. diff --git a/mak/SRCS b/mak/SRCS index 8a877c9703..d7f74b919d 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -298,6 +298,7 @@ SRCS=\ src\gc\proxy.d \ src\gc\impl\conservative\gc.d \ src\gc\impl\manual\gc.d \ + src\gc\impl\proto\gc.d \ \ src\rt\aApply.d \ src\rt\aApplyR.d \ diff --git a/src/gc/impl/proto/gc.d b/src/gc/impl/proto/gc.d new file mode 100644 index 0000000000..1419e0ce90 --- /dev/null +++ b/src/gc/impl/proto/gc.d @@ -0,0 +1,240 @@ + +module gc.impl.proto.gc; + +import gc.config; +import gc.gcinterface; + +import rt.util.container.array; + +import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; +static import core.memory; + +extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc; /* dmd @@@BUG11461@@@ */ + +private +{ + extern (C) void gc_init_nothrow() nothrow @nogc; + extern (C) void gc_term(); + + extern (C) void gc_enable() nothrow; + extern (C) void gc_disable() nothrow; + + extern (C) void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow; + extern (C) void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow; + extern (C) BlkInfo gc_qalloc( size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow; + extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow; + extern (C) size_t gc_reserve( size_t sz ) nothrow; + + extern (C) void gc_addRange( void* p, size_t sz, const TypeInfo ti = null ) nothrow @nogc; + extern (C) void gc_addRoot( void* p ) nothrow @nogc; +} + +class ProtoGC : GC +{ + Array!Root roots; + Array!Range ranges; + + // Call this function when initializing the real GC + // upon ProtoGC term. This function should be called + // after the real GC is in place. + void term() + { + // Transfer all ranges + foreach (ref r; ranges) + { + // Range(p, p + sz, cast() ti) + gc_addRange(r.pbot, r.ptop - r.pbot, r.ti); + } + + // Transfer all roots + foreach (ref r; roots) + { + gc_addRoot(r.proot); + } + } + + this() + { + } + + void Dtor() + { + } + + void enable() + { + gc_init_nothrow(); + gc_enable(); + } + + void disable() + { + gc_init_nothrow(); + gc_disable(); + } + + void collect() nothrow + { + } + + void collectNoStack() nothrow + { + } + + void minimize() nothrow + { + } + + uint getAttr(void* p) nothrow + { + return 0; + } + + uint setAttr(void* p, uint mask) nothrow + { + return 0; + } + + uint clrAttr(void* p, uint mask) nothrow + { + return 0; + } + + void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow + { + gc_init_nothrow(); + return gc_malloc(size, bits, ti); + } + + BlkInfo qalloc(size_t size, uint bits, const TypeInfo ti) nothrow + { + gc_init_nothrow(); + return gc_qalloc(size, bits, ti); + } + + void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow + { + gc_init_nothrow(); + return gc_calloc(size, bits, ti); + } + + void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow + { + gc_init_nothrow(); + return gc_realloc(p, size, bits, ti); + } + + size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow + { + return 0; + } + + size_t reserve(size_t size) nothrow + { + gc_init_nothrow(); + return reserve(size); + } + + void free(void* p) nothrow @nogc + { + if (p) assert(false, "Invalid memory deallocation"); + } + + void* addrOf(void* p) nothrow @nogc + { + return null; + } + + size_t sizeOf(void* p) nothrow @nogc + { + return 0; + } + + BlkInfo query(void* p) nothrow + { + return BlkInfo.init; + } + + core.memory.GC.Stats stats() nothrow + { + return typeof(return).init; + } + + + void addRoot(void* p) nothrow @nogc + { + roots.insertBack(Root(p)); + } + + void removeRoot(void* p) nothrow @nogc + { + foreach (ref r; roots) + { + if (r is p) + { + r = roots.back; + roots.popBack(); + return; + } + } + assert(false); + } + + @property RootIterator rootIter() return @nogc + { + return &rootsApply; + } + + private int rootsApply(scope int delegate(ref Root) nothrow dg) + { + foreach (ref r; roots) + { + if (auto result = dg(r)) + return result; + } + return 0; + } + + void addRange(void* p, size_t sz, const TypeInfo ti = null) nothrow @nogc + { + ranges.insertBack(Range(p, p + sz, cast() ti)); + } + + void removeRange(void* p) nothrow @nogc + { + foreach (ref r; ranges) + { + if (r.pbot is p) + { + r = ranges.back; + ranges.popBack(); + return; + } + } + assert(false); + } + + @property RangeIterator rangeIter() return @nogc + { + return &rangesApply; + } + + private int rangesApply(scope int delegate(ref Range) nothrow dg) + { + foreach (ref r; ranges) + { + if (auto result = dg(r)) + return result; + } + return 0; + } + + void runFinalizers(in void[] segment) nothrow + { + } + + bool inFinalizer() nothrow + { + return false; + } +} diff --git a/src/gc/proxy.d b/src/gc/proxy.d index 68877e1489..2469b666e7 100644 --- a/src/gc/proxy.d +++ b/src/gc/proxy.d @@ -15,6 +15,7 @@ module gc.proxy; import gc.impl.conservative.gc; import gc.impl.manual.gc; +import gc.impl.proto.gc; import gc.config; import gc.gcinterface; @@ -25,28 +26,55 @@ private static import core.memory; alias BlkInfo = core.memory.GC.BlkInfo; - __gshared GC instance; - __gshared GC proxiedGC; // used to iterate roots of Windows DLLs + import core.internal.spinlock; + static SpinLock instanceLock; + __gshared bool isInstanceInit = false; + __gshared GC instance = new ProtoGC(); + __gshared GC proxiedGC; // used to iterate roots of Windows DLLs } - extern (C) { - void gc_init() { - config.initialize(); - ManualGC.initialize(instance); - ConservativeGC.initialize(instance); - if (instance is null) + instanceLock.lock(); + if (!isInstanceInit) { - import core.stdc.stdio : fprintf, stderr; - import core.stdc.stdlib : exit; + auto protoInstance = instance; + config.initialize(); + ManualGC.initialize(instance); + ConservativeGC.initialize(instance); + + if (instance is protoInstance) + { + import core.stdc.stdio : fprintf, stderr; + import core.stdc.stdlib : exit; + + fprintf(stderr, "No GC was initialized, please recheck the name of the selected GC ('%.*s').\n", cast(int)config.gc.length, config.gc.ptr); + instanceLock.unlock(); + exit(1); + + // Shouldn't get here. + assert(0); + } + + // Transfer all ranges and roots to the real GC. + (cast(ProtoGC) protoInstance).term(); + isInstanceInit = true; + } + instanceLock.unlock(); + } - fprintf(stderr, "No GC was initialized, please recheck the name of the selected GC ('%.*s').\n", cast(int)config.gc.length, config.gc.ptr); - exit(1); + void gc_init_nothrow() nothrow + { + scope(failure) + { + import core.internal.abort; + abort("Cannot initialize the garbage collector.\n"); + assert(0); } + gc_init(); } void gc_term() @@ -61,11 +89,14 @@ extern (C) // NOTE: Due to popular demand, this has been re-enabled. It still has // the problems mentioned above though, so I guess we'll see. - instance.collectNoStack(); // not really a 'collect all' -- still scans - // static data area, roots, and ranges. + if (isInstanceInit) + { + instance.collectNoStack(); // not really a 'collect all' -- still scans + // static data area, roots, and ranges. - ManualGC.finalize(instance); - ConservativeGC.finalize(instance); + ManualGC.finalize(instance); + ConservativeGC.finalize(instance); + } } void gc_enable() @@ -158,12 +189,12 @@ extern (C) return instance.stats(); } - void gc_addRoot( void* p ) nothrow + void gc_addRoot( void* p ) nothrow @nogc { return instance.addRoot( p ); } - void gc_addRange( void* p, size_t sz, const TypeInfo ti = null ) nothrow + void gc_addRange( void* p, size_t sz, const TypeInfo ti = null ) nothrow @nogc { return instance.addRange( p, sz, ti ); } diff --git a/src/rt/dmain2.d b/src/rt/dmain2.d index 086e9ada80..6d99c098a6 100644 --- a/src/rt/dmain2.d +++ b/src/rt/dmain2.d @@ -197,7 +197,7 @@ extern (C) int rt_init() // in other druntime systems. _d_initMonoTime(); thread_init(); - gc_init(); + // TODO: fixme - calls GC.addRange -> Initializes GC initStaticDataGC(); lifetime_init(); rt_moduleCtor(); diff --git a/test/exceptions/src/unknown_gc.d b/test/exceptions/src/unknown_gc.d index 43e1e6453f..f135c1b733 100644 --- a/test/exceptions/src/unknown_gc.d +++ b/test/exceptions/src/unknown_gc.d @@ -1,5 +1,9 @@ +import core.memory; + extern(C) __gshared string[] rt_options = [ "gcopt=gc:unknowngc" ]; void main() { + // GC initialized upon first call -> Unknown GC error is thrown + GC.enable(); } diff --git a/test/nogc.d b/test/nogc.d new file mode 100644 index 0000000000..b3f75804a9 --- /dev/null +++ b/test/nogc.d @@ -0,0 +1,5 @@ +extern(C) __gshared string[] rt_options = [ "gcopt=gc:non-existing" ]; + +void main() @nogc +{ +}