Destructor nonsense on dlang.org

Mehrdad wfunction at hotmail.com
Thu May 24 22:07:46 PDT 2012


On Thursday, 24 May 2012 at 16:06:23 UTC, Andrei Alexandrescu 
wrote:
> It does matter because a destructor may use an object that has 
> just been destroyed.
>
> Andrei


Andrei: .NET has this exact problem, and handles it pretty well.

There are two types of "Dispose()": manual and automatic.

Whenever you're wrapping some unmanaged resource (e.g. file 
handle), you wrap it inside some managed object (e.g. 
SafeFileHandle).

Then you embed _that_ resource in the actual object you want 
(e.g. FileStream).

Now, there are two ways a FileStream can get destroyed:

1. Through a manual call to FileStream.Dispose(). In this case, 
all embedded objects (e.g. SafeFileHandle) are *guaranteed* to be 
valid, so we simply flush the file and call 
SafeFileHandle.Dispose() to dispose of the managed resources, and 
then dispose of all the unmanaged resources (which are primitive 
fields, guaranteed to be accessible). Furthermore, the object 
suppresses its own finalizer.

2. Through a garbage-collected call to ~FileStream(). In this 
case, the managed resources such as SafeFileHandle will be (or is 
already) destroyed SEPARATELY, and so we do _NOT_ access them. We 
ONLY dispose of the unmanaged resources, if any, and let the 
managed resources take care of themselves.


It's a pretty well-defined sequence, and it works well in 
practice.

(Of course, you don't actually _need_ this double-indirection 
here: You could instead just wrap the unmanaged resource 
manually, and do everything in FileStream. The reason for the 
double-indirection is something slightly unrelated. I was just 
explaining how to take care of the managed resource disposal 
problem that you mentioned.)


You could point out that, in this case, the FileStream doesn't 
flush its buffers  before the file handle is destroyed, if the GC 
collects the object.

That problem is solvable in two ways, although .NET simply chose 
to not worry about it, as far as I know:

1. Simply wrap the handle inside FileStream. Since it will be 
unmanaged, you can access it during disposal.

2. If that isn't possible, keep a _strong_, *unmanaged* reference 
to your _managed_ SafeFileHandle object. (This is accomplished 
through acquiring a cookie from the GC.) Because of this, 
SafeFileHandle will NOT be destroyed before FileStream. You can 
then use this fact to access SafeFileHandle inside FileStream's 
finalizer, through the unmanaged (but safe) cookie.



tl;dr: It's a completely solved problem in .NET; there really 
shouldn't be any issues with it in D either.


More information about the Digitalmars-d mailing list