From 2253975d8e49f13378ed7b48d1ba8fe42f750c0e Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Fri, 29 Sep 2017 21:31:33 +0200 Subject: [PATCH 01/10] implement open GC registry - allows to add new GCs at link time --- mak/SRCS | 3 +- posix.mak | 6 +- src/gc/config.d | 18 ++- src/gc/impl/conservative/gc.d | 61 ++++---- src/gc/impl/manual/gc.d | 49 +++--- src/gc/proxy.d | 16 +- src/gc/register.c | 19 +++ src/gc/registry.d | 86 +++++++++++ test/init_fini/Makefile | 8 +- test/init_fini/src/custom_gc.d | 188 ++++++++++++++++++++++++ test/init_fini/src/register_custom_gc.c | 6 + 11 files changed, 380 insertions(+), 80 deletions(-) create mode 100644 src/gc/register.c create mode 100644 src/gc/registry.d create mode 100644 test/init_fini/src/custom_gc.d create mode 100644 test/init_fini/src/register_custom_gc.c diff --git a/mak/SRCS b/mak/SRCS index 2c7e6e15bb..4fa5f11f16 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -390,10 +390,11 @@ SRCS=\ src\gc\bits.d \ src\gc\config.d \ src\gc\gcinterface.d \ + src\gc\impl\conservative\gc.d \ src\gc\os.d \ src\gc\pooltable.d \ src\gc\proxy.d \ - src\gc\impl\conservative\gc.d \ + src\gc\registry.d \ src\gc\impl\manual\gc.d \ src\gc\impl\proto\gc.d \ \ diff --git a/posix.mak b/posix.mak index 853514997c..1579a3204a 100644 --- a/posix.mak +++ b/posix.mak @@ -123,7 +123,7 @@ SRCS:=$(subst \,/,$(SRCS)) # NOTE: a pre-compiled minit.obj has been provided in dmd for Win32 and # minit.asm is not used by dmd for Linux -OBJS= $(ROOT)/errno_c.o $(ROOT)/threadasm.o +OBJS= $(ROOT)/errno_c.o $(ROOT)/threadasm.o $(ROOT)/gc/register.o ifeq ($(OS),osx) ifeq ($(MODEL), 64) @@ -229,6 +229,10 @@ $(ROOT)/threadasm.o : src/core/threadasm.S @mkdir -p $(dir $@) $(CC) -c $(CFLAGS) $< -o$@ +$(ROOT)/gc/%.o : src/gc/%.c + @mkdir -p $(dir $@) + $(CC) -c $(CFLAGS) $< -o$@ + ######################## Create a shared library ############################## $(DRUNTIMESO) $(DRUNTIMESOLIB) dll: DFLAGS+=-version=Shared -fPIC diff --git a/src/gc/config.d b/src/gc/config.d index e431846d54..396627ac6d 100644 --- a/src/gc/config.d +++ b/src/gc/config.d @@ -34,10 +34,20 @@ struct Config void help() @nogc nothrow { - string s = "GC options are specified as whitespace separated assignments: + import gc.registry : registeredGCFactories; + + version (unittest) if (inUnittest) return; + + string s = "GC options are specified as white space separated assignments: disable:0|1 - start disabled (%d) profile:0|1|2 - enable profiling with summary when terminating program (%d) - gc:conservative|precise|manual - select gc implementation (default = conservative) + gc:".ptr, disable, profile); + foreach (i, entry; registeredGCFactories) + { + if (i) printf("|"); + printf("%.*s", cast(int) entry.name.length, entry.name.ptr); + } + printf(" - select gc implementation (default = conservative) initReserve:N - initial memory to reserve in MB (%lld) minPoolSize:N - initial and minimum pool size in MB (%lld) @@ -45,8 +55,8 @@ struct Config incPoolSize:N - pool size increment MB (%lld) heapSizeFactor:N - targeted heap size to used memory ratio (%g) cleanup:none|collect|finalize - how to treat live objects when terminating (collect) -"; - printf(s.ptr, disable, profile, cast(long)initReserve, cast(long)minPoolSize, +".ptr, + cast(long)initReserve, cast(long)minPoolSize, cast(long)maxPoolSize, cast(long)incPoolSize, heapSizeFactor); } diff --git a/src/gc/impl/conservative/gc.d b/src/gc/impl/conservative/gc.d index 3b87fdd2ba..20a9618390 100644 --- a/src/gc/impl/conservative/gc.d +++ b/src/gc/impl/conservative/gc.d @@ -103,6 +103,30 @@ alias GC gc_t; /* ============================ GC =============================== */ +// register GC in C constructor (_STI_) +extern(C) void _d_register_conservative_gc() +{ + import gc.registry; + registerGCFactory("conservative", &initialize); +} + +private GC initialize() +{ + import core.stdc.string: memcpy; + + 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(); + + return instance; +} + class ConservativeGC : GC { // For passing to debug code (not thread safe) @@ -124,42 +148,6 @@ class ConservativeGC : GC gcLock.lock(); } - - static void initialize(ref GC gc) - { - import core.stdc.string: memcpy; - - if ((config.gc != "precise") && (config.gc != "conservative")) - return; - - if (config.gc == "precise") - isPrecise = true; - - 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 != "precise") && (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 @@ -190,6 +178,7 @@ class ConservativeGC : GC cstdlib.free(gcx); gcx = null; } + cstdlib.free(cast(void*) this); } diff --git a/src/gc/impl/manual/gc.d b/src/gc/impl/manual/gc.d index 61902ba88e..727b9f22ee 100644 --- a/src/gc/impl/manual/gc.d +++ b/src/gc/impl/manual/gc.d @@ -35,39 +35,33 @@ static import core.memory; extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc; /* dmd @@@BUG11461@@@ */ -class ManualGC : GC +// register GC in C constructor (_STI_) +extern(C) void _d_register_manual_gc() { - __gshared Array!Root roots; - __gshared Array!Range ranges; - - static void initialize(ref GC gc) - { - import core.stdc.string; + import gc.registry; + registerGCFactory("manual", &initialize); +} - if (config.gc != "manual") - return; +private GC initialize() +{ + import core.stdc.string: memcpy; - auto p = cstdlib.malloc(__traits(classInstanceSize, ManualGC)); - if (!p) - onOutOfMemoryError(); + auto p = cstdlib.malloc(__traits(classInstanceSize, ManualGC)); + if (!p) + onOutOfMemoryError(); - auto init = typeid(ManualGC).initializer(); - assert(init.length == __traits(classInstanceSize, ManualGC)); - auto instance = cast(ManualGC) memcpy(p, init.ptr, init.length); - instance.__ctor(); + auto init = typeid(ManualGC).initializer(); + assert(init.length == __traits(classInstanceSize, ManualGC)); + auto instance = cast(ManualGC) memcpy(p, init.ptr, init.length); + instance.__ctor(); - gc = instance; - } - - static void finalize(ref GC gc) - { - if (config.gc != "manual") - return; + return instance; +} - auto instance = cast(ManualGC) gc; - instance.Dtor(); - cstdlib.free(cast(void*) instance); - } +class ManualGC : GC +{ + __gshared Array!Root roots; + __gshared Array!Range ranges; this() { @@ -75,6 +69,7 @@ class ManualGC : GC void Dtor() { + cstdlib.free(cast(void*) this); } void enable() diff --git a/src/gc/proxy.d b/src/gc/proxy.d index d66c6e92a1..279fd3ac64 100644 --- a/src/gc/proxy.d +++ b/src/gc/proxy.d @@ -18,6 +18,7 @@ import gc.impl.manual.gc; import gc.impl.proto.gc; import gc.config; import gc.gcinterface; +import gc.registry : createGCInstance; static import core.memory; @@ -42,11 +43,8 @@ extern (C) if (!isInstanceInit) { auto protoInstance = instance; - config.initialize(); - ManualGC.initialize(instance); - ConservativeGC.initialize(instance); - - if (instance is protoInstance) + auto newInstance = createGCInstance(config.gc); + if (newInstance is null) { import core.stdc.stdio : fprintf, stderr; import core.stdc.stdlib : exit; @@ -58,7 +56,7 @@ extern (C) // Shouldn't get here. assert(0); } - + instance = newInstance; // Transfer all ranges and roots to the real GC. (cast(ProtoGC) protoInstance).term(); isInstanceInit = true; @@ -109,9 +107,9 @@ extern (C) break; } - ManualGC.finalize(instance); - ConservativeGC.finalize(instance); - } + thread_term(); + + instance.Dtor(); } void gc_enable() diff --git a/src/gc/register.c b/src/gc/register.c new file mode 100644 index 0000000000..67b5fb2741 --- /dev/null +++ b/src/gc/register.c @@ -0,0 +1,19 @@ +/** + * This module is used to mark the GC registration functions as C constructors. + * + * Copyright: Copyright Martin Nowak 2017-. + * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Martin Nowak + * Source: $(DRUNTIMESRC src/gc/init.c) + */ + +int _dummy_ref_to_link_gc_register_constructor; // dummy var referenced by GC so this files gets linked + +extern void _d_register_conservative_gc(); +extern void _d_register_manual_gc(); + +__attribute__ ((constructor)) static void register_gcs() +{ + _d_register_conservative_gc(); + _d_register_manual_gc(); +} diff --git a/src/gc/registry.d b/src/gc/registry.d new file mode 100644 index 0000000000..246ad4bc24 --- /dev/null +++ b/src/gc/registry.d @@ -0,0 +1,86 @@ +/** + * Contains a registry for GC factories. + * + * Copyright: Copyright Digital Mars 2016. + * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Martin Nowak + */ +module gc.registry; + +import gc.gcinterface : GC; + +/*@nogc nothrow:*/ + +/** + * A factory function that instantiates an implementation of the GC interface. + * The instance is supposed to free itself upon calling it's Dtor function. + * + * The factory should print an error and abort the program if it + * cannot successfully initialize the GC instance. + */ +alias GCFactory = GC function(); + +/** + * Register a GC factory under the given `name`. This function must be called + * from a C constructor before druntime is initialized. + * + * To use the registered GC, it's name must be specified gcopt runtime option, + * e.g. by passing $(TT, --DRT-gcopt=gc:my_gc_name) as application argument. + * + * Params: + * name = name of the GC implementation; should be unique + * factory = function to instantiate the implementation + * Note: The registry does not perform synchronization, as registration is + * assumed to be executed serially, as is the case for C constructors. + * See_Also: + * $(LINK2 https://dlang.org/spec/garbage.html#gc_config, Configuring the Garbage Collector) + */ +void registerGCFactory(string name, GCFactory factory) nothrow @nogc +{ + import core.stdc.stdlib : realloc; + + auto ptr = cast(Entry*)realloc(entries.ptr, (entries.length + 1) * Entry.sizeof); + entries = ptr[0 .. entries.length + 1]; + entries[$ - 1] = Entry(name, factory); +} + +/** + * Called during runtime initialization to initialize a GC instance of given `name`. + * + * Params: + * name = name of the GC to instantiate + * Returns: + * The created GC instance or `null` if no factory for that name was registered + */ +GC createGCInstance(string name) +{ + import core.stdc.stdlib : free; + + foreach (entry; entries) + { + if (entry.name != name) + continue; + auto instance = entry.factory(); + // only one GC at a time for now, so free the registry to not leak + free(entries.ptr); + entries = null; + return instance; + } + return null; +} + +// list of all registerd GCs +package(gc) const(Entry[]) registeredGCFactories(scope int dummy=0) nothrow @nogc +{ + return entries; +} + +private: + +struct Entry +{ + string name; + GCFactory factory; +} + +__gshared Entry[] entries; diff --git a/test/init_fini/Makefile b/test/init_fini/Makefile index 6f01a13cd0..0b2ac7f41b 100644 --- a/test/init_fini/Makefile +++ b/test/init_fini/Makefile @@ -1,6 +1,6 @@ include ../common.mak -TESTS:=thread_join runtime_args test18996 +TESTS:=thread_join runtime_args test18996 custom_gc .PHONY: all clean all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) @@ -10,8 +10,12 @@ $(ROOT)/%.done: $(ROOT)/% $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) @touch $@ +$(ROOT)/custom_gc: $(ROOT)/register_custom_gc.o $(ROOT)/%: $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $< + $(QUIET)$(DMD) $(DFLAGS) -of$@ $^ + +$(ROOT)/register_custom_gc.o: $(SRC)/register_custom_gc.c + $(QUIET)$(CC) -c $(CFLAGS) -o $@ $^ clean: rm -rf $(ROOT) diff --git a/test/init_fini/src/custom_gc.d b/test/init_fini/src/custom_gc.d new file mode 100644 index 0000000000..7c46984c94 --- /dev/null +++ b/test/init_fini/src/custom_gc.d @@ -0,0 +1,188 @@ +import gc.registry; +import gc.gcinterface; +import core.stdc.stdlib; + +static import core.memory; + +extern (C) __gshared string[] rt_options = ["gcopt=gc:malloc"]; + +extern (C) void register_mygc() +{ + registerGCFactory("malloc", &MallocGC.initialize); +} + +/** Simple GC that requires any pointers passed to it's API + to point to start of the allocation. + */ +class MallocGC : GC +{ +nothrow @nogc: + static GC initialize() + { + import core.stdc.string : memcpy; + + __gshared ubyte[__traits(classInstanceSize, MallocGC)] buf; + + auto init = typeid(MallocGC).initializer(); + assert(init.length == buf.length); + auto instance = cast(MallocGC) memcpy(buf.ptr, init.ptr, init.length); + instance.__ctor(); + return instance; + } + + this() + { + } + + void Dtor() + { + } + + void enable() + { + } + + void 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 mask; + } + + uint clrAttr(void* p, uint mask) nothrow + { + return mask; + } + + void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow + { + return sentinelAdd(.malloc(size + sentinelSize), size); + } + + BlkInfo qalloc(size_t size, uint bits, const TypeInfo ti) nothrow + { + return BlkInfo(malloc(size, bits, ti), size); + } + + void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow + { + return sentinelAdd(.calloc(1, size + sentinelSize), size); + } + + void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow + { + return sentinelAdd(.realloc(p - sentinelSize, size + sentinelSize), size); + } + + size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow + { + return 0; + } + + size_t reserve(size_t size) nothrow + { + return 0; + } + + void free(void* p) nothrow + { + free(p - sentinelSize); + } + + void* addrOf(void* p) nothrow + { + return p; + } + + size_t sizeOf(void* p) nothrow + { + return query(p).size; + } + + BlkInfo query(void* p) nothrow + { + return p ? BlkInfo(p, sentinelGet(p)) : BlkInfo.init; + } + + core.memory.GC.Stats stats() nothrow + { + return core.memory.GC.Stats.init; + } + + void addRoot(void* p) nothrow @nogc + { + } + + void removeRoot(void* p) nothrow @nogc + { + } + + @property RootIterator rootIter() @nogc + { + return null; + } + + void addRange(void* p, size_t sz, const TypeInfo ti) nothrow @nogc + { + } + + void removeRange(void* p) nothrow @nogc + { + } + + @property RangeIterator rangeIter() @nogc + { + return null; + } + + void runFinalizers(in void[] segment) nothrow + { + } + + bool inFinalizer() nothrow + { + return false; + } + +private: + // doesn't care for alignment + static void* sentinelAdd(void* p, size_t value) + { + *cast(size_t*) p = value; + return p + sentinelSize; + } + + static size_t sentinelGet(void* p) + { + return *cast(size_t*)(p - sentinelSize); + } + + enum sentinelSize = size_t.sizeof; +} + +void main() +{ + // test array append cache + char[] s; + foreach (char c; char.min .. char.max + 1) + s ~= c; +} diff --git a/test/init_fini/src/register_custom_gc.c b/test/init_fini/src/register_custom_gc.c new file mode 100644 index 0000000000..6118f471de --- /dev/null +++ b/test/init_fini/src/register_custom_gc.c @@ -0,0 +1,6 @@ +extern void register_mygc(); + +__attribute__((constructor)) static void xxx_ctor() +{ + register_mygc(); +} From 146d3fcb93834625d85a6b0dc64bc4cdc24b91e5 Mon Sep 17 00:00:00 2001 From: Martin Nowak Date: Sun, 1 Oct 2017 13:25:24 +0200 Subject: [PATCH 02/10] turn GC.Dtor into normal destructor - that way any invariant won't be called on that GC instance after freeing itself --- src/gc/gcinterface.d | 6 ------ src/gc/impl/conservative/gc.d | 2 +- src/gc/impl/manual/gc.d | 2 +- src/gc/proxy.d | 2 +- src/gc/registry.d | 3 ++- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/gc/gcinterface.d b/src/gc/gcinterface.d index b659498201..bdcae8b9e9 100644 --- a/src/gc/gcinterface.d +++ b/src/gc/gcinterface.d @@ -38,12 +38,6 @@ struct Range interface GC { - - /* - * - */ - void Dtor(); - /** * */ diff --git a/src/gc/impl/conservative/gc.d b/src/gc/impl/conservative/gc.d index 20a9618390..703f6add6e 100644 --- a/src/gc/impl/conservative/gc.d +++ b/src/gc/impl/conservative/gc.d @@ -164,7 +164,7 @@ class ConservativeGC : GC } - void Dtor() + ~this() { version (linux) { diff --git a/src/gc/impl/manual/gc.d b/src/gc/impl/manual/gc.d index 727b9f22ee..d6eda0b7c0 100644 --- a/src/gc/impl/manual/gc.d +++ b/src/gc/impl/manual/gc.d @@ -67,7 +67,7 @@ class ManualGC : GC { } - void Dtor() + ~this() { cstdlib.free(cast(void*) this); } diff --git a/src/gc/proxy.d b/src/gc/proxy.d index 279fd3ac64..fd6ff6b90f 100644 --- a/src/gc/proxy.d +++ b/src/gc/proxy.d @@ -109,7 +109,7 @@ extern (C) thread_term(); - instance.Dtor(); + destroy(instance); } void gc_enable() diff --git a/src/gc/registry.d b/src/gc/registry.d index 246ad4bc24..58cc544f23 100644 --- a/src/gc/registry.d +++ b/src/gc/registry.d @@ -13,7 +13,8 @@ import gc.gcinterface : GC; /** * A factory function that instantiates an implementation of the GC interface. - * The instance is supposed to free itself upon calling it's Dtor function. + * In case the instance was allocated on the C heap, it is supposed to + * free itself upon calling it's destructor. * * The factory should print an error and abort the program if it * cannot successfully initialize the GC instance. From 448f8b969e37fb8353e5886a6cac3ed2939120cd Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Mon, 24 Dec 2018 13:38:46 +0100 Subject: [PATCH 03/10] fix rebase --- src/gc/config.d | 4 +--- src/gc/impl/conservative/gc.d | 2 +- src/gc/impl/manual/gc.d | 2 +- src/gc/proxy.d | 7 +++---- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/gc/config.d b/src/gc/config.d index 396627ac6d..cdedfc5ee6 100644 --- a/src/gc/config.d +++ b/src/gc/config.d @@ -36,9 +36,7 @@ struct Config { import gc.registry : registeredGCFactories; - version (unittest) if (inUnittest) return; - - string s = "GC options are specified as white space separated assignments: + printf("GC options are specified as white space separated assignments: disable:0|1 - start disabled (%d) profile:0|1|2 - enable profiling with summary when terminating program (%d) gc:".ptr, disable, profile); diff --git a/src/gc/impl/conservative/gc.d b/src/gc/impl/conservative/gc.d index 703f6add6e..4fa31e8f84 100644 --- a/src/gc/impl/conservative/gc.d +++ b/src/gc/impl/conservative/gc.d @@ -104,7 +104,7 @@ alias GC gc_t; /* ============================ GC =============================== */ // register GC in C constructor (_STI_) -extern(C) void _d_register_conservative_gc() +extern(C) pragma(crt_constructor) void _d_register_conservative_gc() { import gc.registry; registerGCFactory("conservative", &initialize); diff --git a/src/gc/impl/manual/gc.d b/src/gc/impl/manual/gc.d index d6eda0b7c0..e6a15c2285 100644 --- a/src/gc/impl/manual/gc.d +++ b/src/gc/impl/manual/gc.d @@ -36,7 +36,7 @@ static import core.memory; extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc; /* dmd @@@BUG11461@@@ */ // register GC in C constructor (_STI_) -extern(C) void _d_register_manual_gc() +extern(C) pragma(crt_constructor) void _d_register_manual_gc() { import gc.registry; registerGCFactory("manual", &initialize); diff --git a/src/gc/proxy.d b/src/gc/proxy.d index fd6ff6b90f..09003515d5 100644 --- a/src/gc/proxy.d +++ b/src/gc/proxy.d @@ -42,6 +42,7 @@ extern (C) instanceLock.lock(); if (!isInstanceInit) { + config.initialize(); auto protoInstance = instance; auto newInstance = createGCInstance(config.gc); if (newInstance is null) @@ -106,10 +107,8 @@ extern (C) instance.runFinalizers((cast(ubyte*)null)[0 .. size_t.max]); break; } - - thread_term(); - - destroy(instance); + destroy(instance); + } } void gc_enable() From be2cff90ac539086d24300ffa9620614faac7563 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Mon, 24 Dec 2018 17:44:00 +0100 Subject: [PATCH 04/10] use pragma(crt_constructor) instead of registration via C --- posix.mak | 2 +- src/gc/proxy.d | 18 ++++++++++++++++-- src/gc/register.c | 19 ------------------- test/init_fini/Makefile | 6 +----- test/init_fini/src/custom_gc.d | 7 ++++++- test/init_fini/src/register_custom_gc.c | 6 ------ test/init_fini/win64.mak | 13 +++++++++++++ win64.mak | 5 ++++- 8 files changed, 41 insertions(+), 35 deletions(-) delete mode 100644 src/gc/register.c delete mode 100644 test/init_fini/src/register_custom_gc.c create mode 100644 test/init_fini/win64.mak diff --git a/posix.mak b/posix.mak index 1579a3204a..91bea95650 100644 --- a/posix.mak +++ b/posix.mak @@ -123,7 +123,7 @@ SRCS:=$(subst \,/,$(SRCS)) # NOTE: a pre-compiled minit.obj has been provided in dmd for Win32 and # minit.asm is not used by dmd for Linux -OBJS= $(ROOT)/errno_c.o $(ROOT)/threadasm.o $(ROOT)/gc/register.o +OBJS= $(ROOT)/errno_c.o $(ROOT)/threadasm.o ifeq ($(OS),osx) ifeq ($(MODEL), 64) diff --git a/src/gc/proxy.d b/src/gc/proxy.d index 09003515d5..8742f1e96d 100644 --- a/src/gc/proxy.d +++ b/src/gc/proxy.d @@ -13,8 +13,6 @@ */ module gc.proxy; -import gc.impl.conservative.gc; -import gc.impl.manual.gc; import gc.impl.proto.gc; import gc.config; import gc.gcinterface; @@ -37,11 +35,27 @@ private extern (C) { + // do not import GC modules, they might add a dependency to this whole module + void _d_register_conservative_gc(); + void _d_register_manual_gc(); + + // if you don't want to include the default GCs, replace during link by another implementation + void* register_default_gcs() + { + pragma(inline, false); + // do not call, they register implicitly through pragma(crt_constructor) + // avoid being optimized away + auto reg1 = &_d_register_conservative_gc; + auto reg2 = &_d_register_manual_gc; + return reg1 < reg2 ? reg1 : reg2; + } + void gc_init() { instanceLock.lock(); if (!isInstanceInit) { + register_default_gcs(); config.initialize(); auto protoInstance = instance; auto newInstance = createGCInstance(config.gc); diff --git a/src/gc/register.c b/src/gc/register.c deleted file mode 100644 index 67b5fb2741..0000000000 --- a/src/gc/register.c +++ /dev/null @@ -1,19 +0,0 @@ -/** - * This module is used to mark the GC registration functions as C constructors. - * - * Copyright: Copyright Martin Nowak 2017-. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Martin Nowak - * Source: $(DRUNTIMESRC src/gc/init.c) - */ - -int _dummy_ref_to_link_gc_register_constructor; // dummy var referenced by GC so this files gets linked - -extern void _d_register_conservative_gc(); -extern void _d_register_manual_gc(); - -__attribute__ ((constructor)) static void register_gcs() -{ - _d_register_conservative_gc(); - _d_register_manual_gc(); -} diff --git a/test/init_fini/Makefile b/test/init_fini/Makefile index 0b2ac7f41b..846966f763 100644 --- a/test/init_fini/Makefile +++ b/test/init_fini/Makefile @@ -10,12 +10,8 @@ $(ROOT)/%.done: $(ROOT)/% $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(RUN_ARGS) @touch $@ -$(ROOT)/custom_gc: $(ROOT)/register_custom_gc.o $(ROOT)/%: $(SRC)/%.d - $(QUIET)$(DMD) $(DFLAGS) -of$@ $^ - -$(ROOT)/register_custom_gc.o: $(SRC)/register_custom_gc.c - $(QUIET)$(CC) -c $(CFLAGS) -o $@ $^ + $(QUIET)$(DMD) $(DFLAGS) -of$@ $< clean: rm -rf $(ROOT) diff --git a/test/init_fini/src/custom_gc.d b/test/init_fini/src/custom_gc.d index 7c46984c94..5d53fef473 100644 --- a/test/init_fini/src/custom_gc.d +++ b/test/init_fini/src/custom_gc.d @@ -6,11 +6,16 @@ static import core.memory; extern (C) __gshared string[] rt_options = ["gcopt=gc:malloc"]; -extern (C) void register_mygc() +extern (C) pragma(crt_constructor) void register_mygc() { registerGCFactory("malloc", &MallocGC.initialize); } +extern (C) void register_default_gcs() +{ + // remove default GCs +} + /** Simple GC that requires any pointers passed to it's API to point to start of the allocation. */ diff --git a/test/init_fini/src/register_custom_gc.c b/test/init_fini/src/register_custom_gc.c deleted file mode 100644 index 6118f471de..0000000000 --- a/test/init_fini/src/register_custom_gc.c +++ /dev/null @@ -1,6 +0,0 @@ -extern void register_mygc(); - -__attribute__((constructor)) static void xxx_ctor() -{ - register_mygc(); -} diff --git a/test/init_fini/win64.mak b/test/init_fini/win64.mak new file mode 100644 index 0000000000..160e8c7c68 --- /dev/null +++ b/test/init_fini/win64.mak @@ -0,0 +1,13 @@ +# built from the druntime top-level folder +# to be overwritten by caller +DMD=dmd +MODEL=64 +DRUNTIMELIB=druntime64.lib + +test: custom_gc + +custom_gc: + $(DMD) -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) test\init_fini\src\custom_gc.d + custom_gc.exe + del custom_gc.exe custom_gc.obj + diff --git a/win64.mak b/win64.mak index afb876f8d2..e70c5d52a5 100644 --- a/win64.mak +++ b/win64.mak @@ -108,10 +108,13 @@ test_stdcpp: test_gc: $(MAKE) -f test\gc\win64.mak "DMD=$(DMD)" MODEL=$(MODEL) "VCDIR=$(VCDIR)" DRUNTIMELIB=$(DRUNTIME) "CC=$(CC)" test +custom_gc: + $(MAKE) -f test\init_fini\win64.mak "DMD=$(DMD)" MODEL=$(MODEL) "VCDIR=$(VCDIR)" DRUNTIMELIB=$(DRUNTIME) "CC=$(CC)" test + test_loadlib: $(DMD) -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIME) -run test\shared\src\loadlibwin.d -test_all: test_uuid test_aa test_hash test_stdcpp test_gc test_loadlib +test_all: test_uuid test_aa test_hash test_stdcpp test_gc custom_gc test_loadlib ################### zip/install/clean ########################## From 4b934a093d43bdfc9bc84fcd3a783186fc69c3c0 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Fri, 4 Jan 2019 11:10:11 +0100 Subject: [PATCH 05/10] don't use __gshared for ManualGC.roots/ranges --- src/gc/impl/manual/gc.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gc/impl/manual/gc.d b/src/gc/impl/manual/gc.d index e6a15c2285..5c6726a115 100644 --- a/src/gc/impl/manual/gc.d +++ b/src/gc/impl/manual/gc.d @@ -60,8 +60,8 @@ private GC initialize() class ManualGC : GC { - __gshared Array!Root roots; - __gshared Array!Range ranges; + Array!Root roots; + Array!Range ranges; this() { From de33f78f0752435fb219ec853ea26533cffcab78 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Fri, 4 Jan 2019 13:59:22 +0100 Subject: [PATCH 06/10] add profileStats to custom_gc --- test/init_fini/src/custom_gc.d | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/init_fini/src/custom_gc.d b/test/init_fini/src/custom_gc.d index 5d53fef473..effde0a17d 100644 --- a/test/init_fini/src/custom_gc.d +++ b/test/init_fini/src/custom_gc.d @@ -133,6 +133,11 @@ nothrow @nogc: return core.memory.GC.Stats.init; } + core.memory.GC.ProfileStats profileStats() nothrow + { + return typeof(return).init; + } + void addRoot(void* p) nothrow @nogc { } From f056eebd022ad4639efcb5d5b9b56fd8e1210288 Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Fri, 11 Jan 2019 18:24:25 +0100 Subject: [PATCH 07/10] fix memory corruption: must not free the GC, destroy overwrites class memory --- src/gc/impl/conservative/gc.d | 4 +++- src/gc/impl/manual/gc.d | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gc/impl/conservative/gc.d b/src/gc/impl/conservative/gc.d index 4fa31e8f84..8bbac79787 100644 --- a/src/gc/impl/conservative/gc.d +++ b/src/gc/impl/conservative/gc.d @@ -178,7 +178,9 @@ class ConservativeGC : GC cstdlib.free(gcx); gcx = null; } - cstdlib.free(cast(void*) this); + // TODO: cannot free as memory is overwritten and + // the monitor is still read in rt_finalize (called by destroy) + // cstdlib.free(cast(void*) this); } diff --git a/src/gc/impl/manual/gc.d b/src/gc/impl/manual/gc.d index 5c6726a115..c07e5aa09f 100644 --- a/src/gc/impl/manual/gc.d +++ b/src/gc/impl/manual/gc.d @@ -69,7 +69,9 @@ class ManualGC : GC ~this() { - cstdlib.free(cast(void*) this); + // TODO: cannot free as memory is overwritten and + // the monitor is still read in rt_finalize (called by destroy) + // cstdlib.free(cast(void*) this); } void enable() From 05503bd90e8dbc8155832074ac365bc4d23b949d Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Sun, 20 Jan 2019 09:11:17 +0100 Subject: [PATCH 08/10] register precise GC --- src/gc/impl/conservative/gc.d | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/gc/impl/conservative/gc.d b/src/gc/impl/conservative/gc.d index 8bbac79787..0d8137acfb 100644 --- a/src/gc/impl/conservative/gc.d +++ b/src/gc/impl/conservative/gc.d @@ -110,6 +110,12 @@ extern(C) pragma(crt_constructor) void _d_register_conservative_gc() registerGCFactory("conservative", &initialize); } +extern(C) pragma(crt_constructor) void _d_register_precise_gc() +{ + import gc.registry; + registerGCFactory("precise", &initialize_precise); +} + private GC initialize() { import core.stdc.string: memcpy; @@ -127,6 +133,12 @@ private GC initialize() return instance; } +private GC initialize_precise() +{ + ConservativeGC.isPrecise = true; + return initialize(); +} + class ConservativeGC : GC { // For passing to debug code (not thread safe) From 9602cfbc6293db3d2ae380e1b75193987e03ee9a Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Tue, 12 Feb 2019 07:30:34 +0100 Subject: [PATCH 09/10] remove unused makefile rule for gc/%.c --- posix.mak | 4 ---- 1 file changed, 4 deletions(-) diff --git a/posix.mak b/posix.mak index 91bea95650..853514997c 100644 --- a/posix.mak +++ b/posix.mak @@ -229,10 +229,6 @@ $(ROOT)/threadasm.o : src/core/threadasm.S @mkdir -p $(dir $@) $(CC) -c $(CFLAGS) $< -o$@ -$(ROOT)/gc/%.o : src/gc/%.c - @mkdir -p $(dir $@) - $(CC) -c $(CFLAGS) $< -o$@ - ######################## Create a shared library ############################## $(DRUNTIMESO) $(DRUNTIMESOLIB) dll: DFLAGS+=-version=Shared -fPIC From ee0c82c690a75c6a21272c93d7f49aa94319024e Mon Sep 17 00:00:00 2001 From: Rainer Schuetze Date: Tue, 12 Feb 2019 08:16:57 +0100 Subject: [PATCH 10/10] add changelog entry --- changelog/gc_registry.dd | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog/gc_registry.dd diff --git a/changelog/gc_registry.dd b/changelog/gc_registry.dd new file mode 100644 index 0000000000..46c500a60e --- /dev/null +++ b/changelog/gc_registry.dd @@ -0,0 +1,7 @@ +User supplied garbage collectors can now be linked with the runtime + +A GC registry has been implemented that allows to add +garbage collector implementations by just linking them into +the binary. See the +$(LINK2 $(ROOT_DIR)spec/garbage.html#gc_registry, documentation) +for details.