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