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