On 9/21/21 6:58 AM, Johan wrote:
> On Monday, 20 September 2021 at 18:49:12 UTC, Steven Schveighoffer wrote:
> I feel like this might not necessarily be an issue, because technically, you aren't using c
any more, so it can be deallocated immediately. But right in our documentation here it lists ways to alleviate this:
If pointers to D garbage collector allocated memory are passed to C functions, it's critical to ensure that the memory will not be collected by the garbage collector before the C function is done with it. This is accomplished by:
* Making a copy of the data using core.stdc.stdlib.malloc() and passing the copy instead.
* Leaving a pointer to it on the stack (as a parameter or automatic variable), as the garbage collector will scan the stack.
* Leaving a pointer to it in the static data segment, as the garbage collector will scan the static data segment.
* Registering the pointer with the garbage collector with the std.gc.addRoot() or std.gc.addRange() calls.
This to me seems like "leaving a pointer to it on the stack". I'm not sure how else I would do that specifically? Plus, this option is the only "free" one -- the others all require much more complication. Adding a pointer to the stack is free. It's just, I don't know how to tell the compiler to do that besides declaring it.
First: the use of "stack" here is wrong and confusing. It should be "local storage" (notorious error throughout the spec). Indeed, what is done in your example is putting the pointer in local storage. The scope of that local storage is until the end of function scope (in your example). I don't think (in LDC) that we track the lifetime of variables in that way, so what is done is that the optimizer just looks at last point of use. This is similar to how Java behaves:
https://stackoverflow.com/questions/39285108/can-java-garbage-collect-variables-before-end-of-scope
Yikes, that's quite aggressive. It says even a method can be in progress on the thing and it's collected early.
> As per language spec, the D compilers are non-compliant on this point. So a decision is needed to either change the language spec, or to complain with the D compilers to fix it.
I would say if you can somehow find a way to trigger the optimizer not to avoid that stack push, in all compilers, we should do that. IMO, the cost of a stack pointer is minimal compared to the surprising result that we currently see. But I don't know enough about compilers implementation to know whether this is a reasonable ask.
Regardless of whether it's spec or implementation, something needs to change. This is why I asked the question without any context first, to have everyone think about what they expect to happen before finding out what actually happens. I'm surprised so many expected the current behavior, I did not.
I just thought of a possible easy and effective way to ensure the thing isn't collected early:
struct Pin(T)
{
T t;
@nogc nothrow pure @safe ~this() {}
alias t this;
}
...
// usage
auto c = Pin!C(new C); // now it needs to be held until the scope ends
This seems to work on LDC with -O3 to prevent the early collection, so maybe it is sound? If this is a valid mechanism to ensure it's saved, maybe it can be added to Phobos and the spec updated to recommend that.
-Steve