Destructors are useless?
- From: Michi Henning <michi@xxxxxxxxx>
- Date: Wed, 27 Apr 2005 17:40:26 +1000
I've been having problem with destructors in the context of having ported C# code developed under .NET to Mono. What happens is that, on a dual-CPU machine, various parts of the code crash randomly (and rarely). This always happens during process shutdown, after some thread has called System.Environment.Exit(). Clearly, some sort of race condition.
Note that what follows only applies to destructors that are called when the process shuts down -- there is no problem with destructors that get called by the GC when the process is executing normally.
After a lot of debugging, I tracked it down to a destructor along the following lines:
~SomeClass
{
if (!_destroyed)
{
System.Console.Error.WriteLine("Forgot to destroy SomeClass");
}
}Under Mono, the attempt to write to the console crashes the process because, by the time destructor is called by the garbage collector, the I/O subsystem has been partially garbage collected already, and the process dies with a NullPointerException somewhere in the guts of the I/O subsystem.
Not a problem with .NET: in .NET, the console isn't destroyed until after everything else is destroyed. (For details, see: http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=88e62cdf-5919-4ac7-bc33-20c06ae539ae There is a comment about two-thirds of the way down the page by Brian Grunkemeyer to that effect.)
Except that I can't rely on this because, as far as I can see, the spec doesn't guarantee that the console will hang around during process shutdown, so it's not portable code.
Then I started reading the spec a bit more and found that destruction order is not guaranteed and that, if A refers to B, it's entirely possible for B to be finalized before A. So, that got me to thinking about what is actually legal to do from within a destructor, if that destructor may be called during process shutdown. Here is a list of things that are *not* legal to do:
- I cannot dereference anything. If I do, the memory for the object that is reference is guaranteed to still be there. However, that object may have been finalized already and, as a result, may no long be in working order if I call a method on it.
- I cannot call a static method on anything. The static method itself is guaranteed to be there. However, the implementation of the static method may depend on another static object that has been finalized already. See previous point.
- I cannot safely invoke a virtual method on my own object. That's because my own object may have a derived part, and that derived part may have been finalized already, and the virtual method may end up using something in the derived part that conceptually no longer exists.
So, that doesn't leave a lot I can do in a destructor, as far as I can see. Here is what I can do safely:
I can assign or read any of my own data members, and any of the accessible data members of my base class. That's about it.
So, I can assign null to all my data members that have reference type, just to be nice to the GC. But that really isn't all that essential in most circumstances.
I can read my own data members. To what purpose? Well, to assert that my program state is still in fine shape, of course:
~SomeClass()
{
System.Diagnostics.Assert(_myMember != null);
System.Diagnostics.Assert(_myOtherMember == null);
}Oops. I can't safely call a static method, because whatever the implementation of the static method uses may have been finalized already. Just as I can't safely write to the console, I can't safely assert either.
Of course, I have no real control over when destructors are called. In particular, I have not control over what destructors run when some thread calls Exit(). As a result, these restrictions apply to *all* destructors, not just those of static objects.
Hmmm... That leaves destructors completely useless. There is nothing, not even asserting, that I can do safely. Of course, that begs the question: why have destructors when I can't do anything with them? As far as I can see, the only legal statements inside a destructor are effectively no-ops.
Mystified,
Michi. .
- Follow-Ups:
- Re: Destructors are useless?
- From: Tom Porterfield
- Re: Destructors are useless?
- From: Dmytro Lapshyn [MVP]
- Re: Destructors are useless?
- Prev by Date: Re: Scope of an event handler?
- Next by Date: Re: C#: ComboBox Problem
- Previous by thread: Datetime stamp on TIFF image
- Next by thread: Re: Destructors are useless?
- Index(es):
Loading