From ecf740cfa7a3da55224be9d62060e9f4bde0f2b8 Mon Sep 17 00:00:00 2001 From: somzzz Date: Mon, 22 Jan 2018 02:38:12 -0800 Subject: [PATCH 1/5] GC initialized on first alloc --- src/gc/proxy.d | 118 ++++++++++++++++++++++++++++++++++++++++++------ src/rt/dmain2.d | 1 - 2 files changed, 104 insertions(+), 15 deletions(-) diff --git a/src/gc/proxy.d b/src/gc/proxy.d index 68877e1489..5cefb55ea1 100644 --- a/src/gc/proxy.d +++ b/src/gc/proxy.d @@ -25,28 +25,78 @@ private static import core.memory; alias BlkInfo = core.memory.GC.BlkInfo; + extern (C) void thread_init(); + extern (C) void thread_term(); + + import core.internal.spinlock; + static SpinLock instanceLock; + + /* + * tlsInstance is a hack to ensure that only the thread initializing the GC + * can use GC functions before the initialization is completed. When it is completed, + * the GC object created is atomically stored in the __gshared instance. + * + * This is necessary because calling thead_init is part of the gc_init routine. + * thread_init, in turn, calls GC.enable, GC.disable and GC.malloc (through the new keyword) + */ + static GC tlsInstance; + __gshared GC instance; __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) + import core.atomic : atomicLoad, atomicStore; + + auto pinstance = cast(shared(GC)*) &instance; + + // using double-checked locking + if (atomicLoad(*pinstance) is null) { - import core.stdc.stdio : fprintf, stderr; - import core.stdc.stdlib : exit; + instanceLock.lock(); + scope(exit) instanceLock.unlock(); + + if (atomicLoad(*pinstance) is null) + { + config.initialize(); + ManualGC.initialize(tlsInstance); + ConservativeGC.initialize(tlsInstance); + + if (tlsInstance is null) + { + 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); + } + + // NOTE: The GC must initialize the thread library + // before its first collection. + thread_init(); + + atomicStore(*pinstance, cast(shared GC) tlsInstance); + } + } + } - 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,130 +111,170 @@ 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 (instance !is null) + { + 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() { + if (tlsInstance) + { + tlsInstance.enable(); + return; + } + gc_init_nothrow(); instance.enable(); } void gc_disable() { + if (tlsInstance) + { + tlsInstance.disable(); + return; + } + gc_init_nothrow(); instance.disable(); } void gc_collect() nothrow { + if (instance is null) return; instance.collect(); } void gc_minimize() nothrow { + gc_init_nothrow(); instance.minimize(); } uint gc_getAttr( void* p ) nothrow { + if (instance is null) return 0; return instance.getAttr(p); } uint gc_setAttr( void* p, uint a ) nothrow { + assert(instance !is null); return instance.setAttr(p, a); } uint gc_clrAttr( void* p, uint a ) nothrow { + assert(instance !is null); return instance.clrAttr(p, a); } void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { + if (tlsInstance) + return tlsInstance.malloc(sz, ba, ti); + + gc_init_nothrow(); return instance.malloc(sz, ba, ti); } BlkInfo gc_qalloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { + gc_init_nothrow(); return instance.qalloc( sz, ba, ti ); } void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { + gc_init_nothrow(); return instance.calloc( sz, ba, ti ); } void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { + gc_init_nothrow(); return instance.realloc( p, sz, ba, ti ); } size_t gc_extend( void* p, size_t mx, size_t sz, const TypeInfo ti = null ) nothrow { + assert(instance !is null); return instance.extend( p, mx, sz,ti ); } size_t gc_reserve( size_t sz ) nothrow { + gc_init_nothrow(); return instance.reserve( sz ); } void gc_free( void* p ) nothrow @nogc { + assert(instance !is null); return instance.free( p ); } void* gc_addrOf( void* p ) nothrow @nogc { + if (instance is null) return null; return instance.addrOf( p ); } size_t gc_sizeOf( void* p ) nothrow @nogc { + if (instance is null) return 0; return instance.sizeOf( p ); } BlkInfo gc_query( void* p ) nothrow { + if (instance is null) return BlkInfo.init; return instance.query( p ); } core.memory.GC.Stats gc_stats() nothrow { + gc_init_nothrow(); return instance.stats(); } void gc_addRoot( void* p ) nothrow { + gc_init_nothrow(); return instance.addRoot( p ); } void gc_addRange( void* p, size_t sz, const TypeInfo ti = null ) nothrow { + gc_init_nothrow(); return instance.addRange( p, sz, ti ); } void gc_removeRoot( void* p ) nothrow { + assert(instance !is null); return instance.removeRoot( p ); } void gc_removeRange( void* p ) nothrow { + assert(instance !is null); return instance.removeRange( p ); } void gc_runFinalizers( in void[] segment ) nothrow { + assert(instance !is null); return instance.runFinalizers( segment ); } bool gc_inFinalizer() nothrow { + if (instance is null) return false; return instance.inFinalizer(); } diff --git a/src/rt/dmain2.d b/src/rt/dmain2.d index 086e9ada80..616f4e92d0 100644 --- a/src/rt/dmain2.d +++ b/src/rt/dmain2.d @@ -197,7 +197,6 @@ extern (C) int rt_init() // in other druntime systems. _d_initMonoTime(); thread_init(); - gc_init(); initStaticDataGC(); lifetime_init(); rt_moduleCtor(); From 07e37cc70da50bae3329f8b44c255b6e09146638 Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Wed, 7 Feb 2018 13:35:53 +0100 Subject: [PATCH 2/5] relax ordering for double-checked locking - need acquire & release for the read and part that are not protected by the mutex - need raw to avoid any caching/optimization by the compiler --- src/gc/proxy.d | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gc/proxy.d b/src/gc/proxy.d index 5cefb55ea1..870a292ac7 100644 --- a/src/gc/proxy.d +++ b/src/gc/proxy.d @@ -50,17 +50,17 @@ extern (C) void gc_init() { - import core.atomic : atomicLoad, atomicStore; + import core.atomic : atomicLoad, atomicStore, MemoryOrder; auto pinstance = cast(shared(GC)*) &instance; // using double-checked locking - if (atomicLoad(*pinstance) is null) + if (atomicLoad!(MemoryOrder.acq)(*pinstance) is null) { instanceLock.lock(); scope(exit) instanceLock.unlock(); - if (atomicLoad(*pinstance) is null) + if (atomicLoad!(MemoryOrder.raw)(*pinstance) is null) { config.initialize(); ManualGC.initialize(tlsInstance); @@ -83,7 +83,7 @@ extern (C) // before its first collection. thread_init(); - atomicStore(*pinstance, cast(shared GC) tlsInstance); + atomicStore!(MemoryOrder.rel)(*pinstance, cast(shared GC) tlsInstance); } } } From 7952d947cc61033be3df7661706bc835d5efa973 Mon Sep 17 00:00:00 2001 From: somzzz Date: Mon, 12 Feb 2018 10:56:55 -0800 Subject: [PATCH 3/5] Using ProtoGC before real GC init --- mak/SRCS | 1 + src/gc/impl/proto/gc.d | 192 +++++++++++++++++++++++++++++++++++++++++ src/gc/proxy.d | 114 ++++++------------------ src/rt/dmain2.d | 2 + test/nogc.d | 5 ++ 5 files changed, 226 insertions(+), 88 deletions(-) create mode 100644 src/gc/impl/proto/gc.d create mode 100644 test/nogc.d 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..b6f3f7e4e8 --- /dev/null +++ b/src/gc/impl/proto/gc.d @@ -0,0 +1,192 @@ + +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 +{ + __gshared Array!Root roots; + __gshared Array!Range ranges; + + 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 + { + gc_init_nothrow(); + gc_addRoot(p); + } + + void removeRoot(void* p) nothrow @nogc + { + } + + @property RootIterator rootIter() return @nogc + { + return &rootsApply; + } + + private int rootsApply(scope int delegate(ref Root) nothrow dg) + { + return 0; + } + + void addRange(void* p, size_t sz, const TypeInfo ti = null) nothrow @nogc + { + gc_init_nothrow(); + gc_addRange(p, sz, ti); + } + + void removeRange(void* p) nothrow @nogc + { + } + + @property RangeIterator rangeIter() return @nogc + { + return &rangesApply; + } + + private int rangesApply(scope int delegate(ref Range) nothrow dg) + { + 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 870a292ac7..04df33ec9b 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,67 +26,44 @@ private static import core.memory; alias BlkInfo = core.memory.GC.BlkInfo; - extern (C) void thread_init(); - extern (C) void thread_term(); - import core.internal.spinlock; static SpinLock instanceLock; - /* - * tlsInstance is a hack to ensure that only the thread initializing the GC - * can use GC functions before the initialization is completed. When it is completed, - * the GC object created is atomically stored in the __gshared instance. - * - * This is necessary because calling thead_init is part of the gc_init routine. - * thread_init, in turn, calls GC.enable, GC.disable and GC.malloc (through the new keyword) - */ - static GC tlsInstance; - - __gshared GC instance; + __gshared bool isInstanceInit = false; + __gshared GC instance = new ProtoGC(); __gshared GC proxiedGC; // used to iterate roots of Windows DLLs } extern (C) { - void gc_init() { import core.atomic : atomicLoad, atomicStore, MemoryOrder; - auto pinstance = cast(shared(GC)*) &instance; - - // using double-checked locking - if (atomicLoad!(MemoryOrder.acq)(*pinstance) is null) + instanceLock.lock(); + if (!isInstanceInit) { - instanceLock.lock(); - scope(exit) instanceLock.unlock(); + auto protoInstance = instance; + config.initialize(); + ManualGC.initialize(instance); + ConservativeGC.initialize(instance); - if (atomicLoad!(MemoryOrder.raw)(*pinstance) is null) + if (instance == protoInstance) { - config.initialize(); - ManualGC.initialize(tlsInstance); - ConservativeGC.initialize(tlsInstance); - - if (tlsInstance is null) - { - import core.stdc.stdio : fprintf, stderr; - import core.stdc.stdlib : exit; + 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); + 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); - } - - // NOTE: The GC must initialize the thread library - // before its first collection. - thread_init(); - - atomicStore!(MemoryOrder.rel)(*pinstance, cast(shared GC) tlsInstance); + // Shouldn't get here. + assert(0); } + + isInstanceInit = true; } + instanceLock.unlock(); } void gc_init_nothrow() nothrow @@ -111,170 +89,130 @@ 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. - if (instance !is null) - { - instance.collectNoStack(); // not really a 'collect all' -- still scans - // static data area, roots, and ranges. + 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() { - if (tlsInstance) - { - tlsInstance.enable(); - return; - } - gc_init_nothrow(); instance.enable(); } void gc_disable() { - if (tlsInstance) - { - tlsInstance.disable(); - return; - } - gc_init_nothrow(); instance.disable(); } void gc_collect() nothrow { - if (instance is null) return; instance.collect(); } void gc_minimize() nothrow { - gc_init_nothrow(); instance.minimize(); } uint gc_getAttr( void* p ) nothrow { - if (instance is null) return 0; return instance.getAttr(p); } uint gc_setAttr( void* p, uint a ) nothrow { - assert(instance !is null); return instance.setAttr(p, a); } uint gc_clrAttr( void* p, uint a ) nothrow { - assert(instance !is null); return instance.clrAttr(p, a); } void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { - if (tlsInstance) - return tlsInstance.malloc(sz, ba, ti); - - gc_init_nothrow(); return instance.malloc(sz, ba, ti); } BlkInfo gc_qalloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { - gc_init_nothrow(); return instance.qalloc( sz, ba, ti ); } void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { - gc_init_nothrow(); return instance.calloc( sz, ba, ti ); } void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { - gc_init_nothrow(); return instance.realloc( p, sz, ba, ti ); } size_t gc_extend( void* p, size_t mx, size_t sz, const TypeInfo ti = null ) nothrow { - assert(instance !is null); return instance.extend( p, mx, sz,ti ); } size_t gc_reserve( size_t sz ) nothrow { - gc_init_nothrow(); return instance.reserve( sz ); } void gc_free( void* p ) nothrow @nogc { - assert(instance !is null); return instance.free( p ); } void* gc_addrOf( void* p ) nothrow @nogc { - if (instance is null) return null; return instance.addrOf( p ); } size_t gc_sizeOf( void* p ) nothrow @nogc { - if (instance is null) return 0; return instance.sizeOf( p ); } BlkInfo gc_query( void* p ) nothrow { - if (instance is null) return BlkInfo.init; return instance.query( p ); } core.memory.GC.Stats gc_stats() nothrow { - gc_init_nothrow(); return instance.stats(); } - void gc_addRoot( void* p ) nothrow + void gc_addRoot( void* p ) nothrow @nogc { - gc_init_nothrow(); 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 { - gc_init_nothrow(); return instance.addRange( p, sz, ti ); } void gc_removeRoot( void* p ) nothrow { - assert(instance !is null); return instance.removeRoot( p ); } void gc_removeRange( void* p ) nothrow { - assert(instance !is null); return instance.removeRange( p ); } void gc_runFinalizers( in void[] segment ) nothrow { - assert(instance !is null); return instance.runFinalizers( segment ); } bool gc_inFinalizer() nothrow { - if (instance is null) return false; return instance.inFinalizer(); } diff --git a/src/rt/dmain2.d b/src/rt/dmain2.d index 616f4e92d0..6202be9c8b 100644 --- a/src/rt/dmain2.d +++ b/src/rt/dmain2.d @@ -58,6 +58,7 @@ extern (C) void _d_monitor_staticctor(); extern (C) void _d_monitor_staticdtor(); extern (C) void _d_critical_init(); extern (C) void _d_critical_term(); +extern (C) void protogc_init(); extern (C) void gc_init(); extern (C) void gc_term(); extern (C) void thread_init() @nogc; @@ -197,6 +198,7 @@ extern (C) int rt_init() // in other druntime systems. _d_initMonoTime(); thread_init(); + // TODO: fixme - calls GC.addRange -> Initializes GC initStaticDataGC(); lifetime_init(); rt_moduleCtor(); diff --git a/test/nogc.d b/test/nogc.d new file mode 100644 index 0000000000..918f6fc5fb --- /dev/null +++ b/test/nogc.d @@ -0,0 +1,5 @@ +extern(C) __gshared string[] rt_options = [ "gcopt=gc:conservative" ]; + +void main() @nogc +{ +} From 6d7fefde1254463302b8facfc783e375321f7c7c Mon Sep 17 00:00:00 2001 From: somzzz Date: Tue, 13 Feb 2018 09:28:40 -0800 Subject: [PATCH 4/5] addRange/addRoot lazy init --- src/gc/impl/proto/gc.d | 60 ++++++++++++++++++++++++++++---- src/gc/proxy.d | 17 +++++---- src/rt/dmain2.d | 1 - test/exceptions/src/unknown_gc.d | 4 +++ test/nogc.d | 2 +- 5 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/gc/impl/proto/gc.d b/src/gc/impl/proto/gc.d index b6f3f7e4e8..1419e0ce90 100644 --- a/src/gc/impl/proto/gc.d +++ b/src/gc/impl/proto/gc.d @@ -31,8 +31,27 @@ private class ProtoGC : GC { - __gshared Array!Root roots; - __gshared Array!Range ranges; + 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() { @@ -141,14 +160,24 @@ class ProtoGC : GC return typeof(return).init; } + void addRoot(void* p) nothrow @nogc { - gc_init_nothrow(); - gc_addRoot(p); + 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 @@ -158,17 +187,31 @@ class ProtoGC : GC 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 { - gc_init_nothrow(); - gc_addRange(p, sz, ti); + 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 @@ -178,6 +221,11 @@ class ProtoGC : GC private int rangesApply(scope int delegate(ref Range) nothrow dg) { + foreach (ref r; ranges) + { + if (auto result = dg(r)) + return result; + } return 0; } diff --git a/src/gc/proxy.d b/src/gc/proxy.d index 04df33ec9b..2469b666e7 100644 --- a/src/gc/proxy.d +++ b/src/gc/proxy.d @@ -38,8 +38,6 @@ extern (C) { void gc_init() { - import core.atomic : atomicLoad, atomicStore, MemoryOrder; - instanceLock.lock(); if (!isInstanceInit) { @@ -48,7 +46,7 @@ extern (C) ManualGC.initialize(instance); ConservativeGC.initialize(instance); - if (instance == protoInstance) + if (instance is protoInstance) { import core.stdc.stdio : fprintf, stderr; import core.stdc.stdlib : exit; @@ -61,6 +59,8 @@ extern (C) assert(0); } + // Transfer all ranges and roots to the real GC. + (cast(ProtoGC) protoInstance).term(); isInstanceInit = true; } instanceLock.unlock(); @@ -89,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() diff --git a/src/rt/dmain2.d b/src/rt/dmain2.d index 6202be9c8b..6d99c098a6 100644 --- a/src/rt/dmain2.d +++ b/src/rt/dmain2.d @@ -58,7 +58,6 @@ extern (C) void _d_monitor_staticctor(); extern (C) void _d_monitor_staticdtor(); extern (C) void _d_critical_init(); extern (C) void _d_critical_term(); -extern (C) void protogc_init(); extern (C) void gc_init(); extern (C) void gc_term(); extern (C) void thread_init() @nogc; 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 index 918f6fc5fb..b3f75804a9 100644 --- a/test/nogc.d +++ b/test/nogc.d @@ -1,4 +1,4 @@ -extern(C) __gshared string[] rt_options = [ "gcopt=gc:conservative" ]; +extern(C) __gshared string[] rt_options = [ "gcopt=gc:non-existing" ]; void main() @nogc { From c0254664658d1f2bd7da411fdd224f0aa09e3522 Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Wed, 14 Feb 2018 10:00:08 +0100 Subject: [PATCH 5/5] add changelog for lazy gc init --- changelog/lazy-gc-init.dd | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/lazy-gc-init.dd 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.