What should happen here?

Steven Schveighoffer schveiguy at gmail.com
Sat Sep 25 17:46:01 UTC 2021


On 9/24/21 8:12 PM, Walter Bright wrote:
> On 9/20/2021 11:26 AM, Steven Schveighoffer wrote:
>> [...]
> 
> 1, 2, or 3 are all valid outcomes.
> 
> The lifetime of a GC allocated object ends when there are no longer live 
> references to it. Live references can end before the scope ends, this is 
> called "non-lexical scoping". That does not imply that that is when the 
> class destructor is run. The class destructor runs at some arbitrary 
> time *after* there are no longer references to it.

Right, but the optimizer is working against that.

For example:

```d
auto c = new C;
.... // a whole bunch of other code

c.method; // not necessarily still live here.
```

Why would it not be live there? Because the method might be inlined, and 
the compiler might determine at that point that none of the data inside 
the method is needed, and therefore the object is no longer needed.

So technically, it's not live after the original allocation. But this is 
really hard for a person to determine. Imagine having the GC collect 
your object when the object is clearly "used" later?

This is why I made this thread. It's easy to explain why it's happening, 
but it's really hard to follow the instructions "leave a pointer on the 
stack" as noted in the spec.

> 
> The GC is not obligated to run a collection cycle upon program 
> termination (a laxity intended to improve shutdown performance), and 
> hence it is not obliged to run the class destructors.

Yeah, that's not really the focus here, but it's a good point to make.

> 
> The inevitable consequence of this is:
> 
> Do *not* use the GC to manage non-memory resources.

When D uses the GC for delegates, classes, etc, and you want to hook to 
those things via C callbacks, this advice falls flat.

Basically, you are saying, when using your OS primitives, don't use D.

> 
> But if you must do it anyway, use the "destroy" and "free" GC special 
> functions. Of course, if you decide to use these functions, it's up to 
> you to ensure resources are free'd exactly once.
> 
> https://dlang.org/phobos/object.html#.destroy
> https://dlang.org/phobos/core_memory.html#.GC.free

Don't recommend free. Just use destroy.

> 
> P.S. A live reference is one where there is a future use of it. A dead 
> reference is one where there isn't a future use. The `c` variable is a 
> dead reference immediately after it is initialized, which is why 
> optimizers delete the assignment. The `new C` return value is dead as 
> soon as it is created.

It would be good for the spec to define what a "live" reference is. 
Currently, the GC talks about stacks, heap and registers. And I think it 
should be clear about whether using a reference later should be 
considered making it "live", as it is not currently when optimized.

> P.P.S. Attempting to deduce the GC's rules from observing its behavior 
> is very likely a path to frustration and errors, because its observed 
> behavior will not make sense (and will appear random) unless one 
> understands the above explanation.

Yes, it is difficult to ensure a collection doesn't occur or does occur. 
But clearly if it does occur and you don't expect it to, it's not doing 
what you thought it was. If it's not doing what you thought it was (e.g. 
keeping the object live), but it doesn't get collected due to some other 
reason, then it's hard to prove things conclusively.

I've resolved to reading the assembly instead of using the measured 
collection, at least to see if the compiler works around efforts to keep 
things live, as that is more reliable. However, it's harder to figure out.

> P.P.S. Do not conflate class destructors with struct destructors. The 
> latter follow RAII rules, the former does not.

Actually, this is not strictly true. Structs allocated on the heap do 
not follow RAII rules, and stack allocations of structs are a completely 
different issue.

-Steve


More information about the Digitalmars-d mailing list