digitalmars.D.learn - Explicitly avoid GC of objects?
- =?iso-8859-1?Q?Robert_M._M=FCnch?= (23/23) May 21 2019 The D docs for interfacing with C state:
- Benjamin Schaaf (30/35) May 21 2019 I'll try to describe rules 2 and 3 as simply as possible: As long
- Johan Engelen (27/54) May 21 2019 If you don't actually access the pointer, the compiler may
The D docs for interfacing with C state: If pointers to D garbage collector allocated memory are passed to C functions, it's critical to ensure that that memory will not be collected by the garbage collector before the C function is done with it. This is accomplished by: 1. Making a copy of the data using core.stdc.stdlib.malloc() and passing the copy instead. 2. Leaving a pointer to it on the stack (as a parameter or automatic variable), as the garbage collector will scan the stack. 3. Leaving a pointer to it in the static data segment, as the garbage collector will scan the static data segment. 4. Registering the pointer with the garbage collector with the std.gc.addRoot() or std.gc.addRange() calls. 1 and 4 are pretty clear. Is there a trick to accomplish 2 when objects are created from different scopes which need to be kept? So, I have one function creating the objects and one using them. How can I keep things on the stack between these two functions? How is 3 done? Is this only useful for static variables? -- Robert M. Münch http://www.saphirion.com smarter | better | faster
May 21 2019
On Tuesday, 21 May 2019 at 11:54:08 UTC, Robert M. Münch wrote:Is there a trick to accomplish 2 when objects are created from different scopes which need to be kept? So, I have one function creating the objects and one using them. How can I keep things on the stack between these two functions? How is 3 done? Is this only useful for static variables?I'll try to describe rules 2 and 3 as simply as possible: As long as you can access the pointer to gc allocated memory in D it will not be freed. So whether that pointer lives on the stack: int* foo() { return new int; } void bar() { int* a = foo(); c_fn(a); } In static or thread local memory: int* a; __gshared int* b; void bar() { a = new int; c_fn(a); b = a; c_fn(b); } Or on the heap: class D { int* a; } void bar() { D d = new D(new int); c_fn(d.a); } Doesn't really matter.
May 21 2019
On Tuesday, 21 May 2019 at 13:23:54 UTC, Benjamin Schaaf wrote:On Tuesday, 21 May 2019 at 11:54:08 UTC, Robert M. Münch wrote:If you don't actually access the pointer, the compiler may optimize-out storage of that pointer and the garbage collector will then not see it. So this statement should read: "As long as _the GC_ can see the pointer to gc allocated memory in D it will not be freed". The GC is looking at registers, stack, static memory, GC allocated memory, ...Is there a trick to accomplish 2 when objects are created from different scopes which need to be kept? So, I have one function creating the objects and one using them. How can I keep things on the stack between these two functions? How is 3 done? Is this only useful for static variables?I'll try to describe rules 2 and 3 as simply as possible: As long as you can access the pointer to gc allocated memory in D it will not be freed.So whether that pointer lives on the stack: int* foo() { return new int; } void bar() { int* a = foo(); c_fn(a); }This is actually not GC safe [*]. The local storage `a` is optimized out, and the parameter to `c_fn` is passed in a register is not guaranteed to not be overwritten by `c_fn`. If after the overwrite, the GC is invoked (e.g. from other thread, or from deeper call tree in `c_fn`) then the memory may be freed. LDC, DMD, GDC, all 3 perform that optimization. So more care is needed here! https://d.godbolt.org/z/DumVNFIn static or thread local memory: int* a; __gshared int* b; void bar() { a = new int; c_fn(a); b = a; c_fn(b); }Also here, whole-program analysis/optimization may discover that `a` and `b` are really never used by anyone. Again, optimizing-out those storage spaces will make this code GC unsafe [*]. In this case, I think LTO and LTO visibility of the c_fn implementation would be needed to do that optimization and thus probably will be safe, for now (!). -Johan [*] The unsafety is a little tricky. If `c_fn` stores the pointer in a place where the GC can see it (registers, D static memory, ...) all is good. But if `c_fn` stores it in, say, a variable on the C side, removes it from register, and _then_ GC is invoked, that's when trouble may happen.
May 21 2019