www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Explicitly avoid GC of objects?

reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
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
parent reply Benjamin Schaaf <ben.schaaf gmail.com> writes:
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
parent Johan Engelen <j j.nl> writes:
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:
 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.
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, ...
 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/DumVNF
 In 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