-
-
Notifications
You must be signed in to change notification settings - Fork 411
GC Initialized on First Alloc #2057
Changes from all commits
ecf740c
07e37cc
7952d94
6d7fefd
c025466
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this call?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oops! It should have been |
||
| } | ||
|
|
||
| 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)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't seem thread-safe – are we sure there can never be a situation where ProtoGC sticks around long enough to be racy (e.g. after improving druntime thread startup to be nogc and using statically allocated Has this been thoroughly thought through for race conditions in general, especially considering architectures where loading the global
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a good point. Likely you need to take the lock. I'm wondering if adding/removing ranges or roots should simply initialize the GC at that point. I thought the instance should be atomically loaded, see Martin's suggestion here. But that doesn't help if you need atomic array appending. |
||
| } | ||
|
|
||
| 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; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that's what needs to be fixed, e.g. by storing the ranges in ProtoGC until we lazily initialize the real GC.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @MartinNowak what use cases is this serving? The win of ProtoGC is to get away without ever initializing the GC. But it's unlikely someone adds a bunch of ranges of interest to the GC but ends up doing no allocation. Also: Where would the array be allocated? Also: this has gotten really long in the teeth, improvements can be discussed later. |
||
| initStaticDataGC(); | ||
| lifetime_init(); | ||
| rt_moduleCtor(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| extern(C) __gshared string[] rt_options = [ "gcopt=gc:non-existing" ]; | ||
|
|
||
| void main() @nogc | ||
| { | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better add an internal
initGCmethod which must also replay all addRange/addRoot calls.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MartinNowak please not