What should happen here?
Steven Schveighoffer
schveiguy at gmail.com
Tue Sep 21 12:02:05 UTC 2021
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](https://dlang.org/spec/interfaceToC.html#storage_allocation) 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:
```d
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
More information about the Digitalmars-d
mailing list