RSS 2.0

Personal Info:

Joe Send mail to the author(s) leads the architecture of an experimental OS's developer platform, where he is also chief architect of its programming language. His current mission is to enable writing large-scale software that is reliable, secure, and scalable by-construction. Before this, Joe founded the Parallel Extensions to .NET project. He has been granted 19 patents, with 49 pending. When not working, Joe enjoys travelling with his wife, writing books, writing music, studying music theory & mathematics, and doing anything involving food & wine.

My books

My music

Disclaimer:
The content of this site are my own personal opinions and do not represent my employer's view in anyway.

© 2012, Joe Duffy

 
 Monday, November 21, 2005

I just replied to a set of questions on Brad's blog. But I then thought perhaps the information would be more generally interesting to my readers. So here it is:

In response to the two issues you brought up on your first reply:

  1. Failure to mention "chain-to-base" for Dispose in the Framework Design Guidelines Book;
  2. Question about when it is safe to call methods on base types within a Finalize method.

Update: First, I have to mention something up front. I failed to mention that almost nobody should write Dispose/Finalize any longer. SafeHandle is the best way to protect resources that span (or outlive) a process, beginning in 2.0. This alleviates the details of implementing this pattern and gives you reliability guarantees that you otherwise wouldn't get (i.e. critical finalization). If you need to do some form of pooling or asserting on failure-to-Dispose, this is still an option; but all "real" resource cleanup should be encapsulated in a SafeHandle.

Re (1):

If we neglect to say a derived class must chain to its base class Dispose method (if it has one), that’s a book-bug/omission. If you're writing a Dispose(bool) your preference is to call base.Dispose(bool), flowing the bool value to the base method; but if one doesn’t exist, a base.Dispose() suffices; otherwise, you simply must call base.Dispose().

This is important because Dispose implies cleaning up resources; if you don’t chain to the base type, clearly you are going to leak those resources. And it might be worse than just nondeterministic cleanup (when the user expected deterministic). Presumably Dispose on the derived class does a GC.SuppressFinalize(this), meaning the base type will never get placed into the Finalize queue, and thus won't release its resources (well, until process exit). The user of this class would notice this as unbounded resource consumption. I suspect the bug would be incredibly hard to find, too.

Re (2):

Generally, you should not make complex method calls from your Finalize method that could result in (accidentally) trying to use a resource which has already been disposed during the destruction process. This is ordinarily more of a concern during virtual method calls, where the most derived type's version is chosen dynamically; if the derived type has overridden a method and tries to use its own resources (which were already relinquished because of destruction ordering), bad things will happen. Calling a base method isn't nearly so dangerous, unless you do it after chaining the base type's destructor. The original document from which the book text was derived acknowledged that Dispose's use of this practice is risky and goes against the general advice.

But we made an exception for Dispose because it is a carefully controlled pattern. (This was in the original document.) Those writing types to follow the pattern are usually more sophisticated users that will feel more comfortable analyzing the call graphs. And virtual calls during destruction aren’t nearly as dangerous as virtual calls during construction. You’re typically concerned that a resource will be used before it’s been initialized (i.e. in the construction case), but presumably code called from your Finalize will be resilient to uninitialized state and isn’t going to make further virtual methods (which might introduce problems). Clearly if this isn't the case--and it could be difficult to verify that it is through test coverage--you will run into bugs, perhaps manifesting as crashing the Finalizer thread.

Note that the book contains an abridged (and more clear/scrubbed) version of the original document: http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=88e62cdf-5919-4ac7-bc33-20c06ae539ae. That document is actually quite a mess, doesn't stay on point, and bombards the reader with way too many details. I'm glad it got chopped up for the book.

 

Recent Entries:

Search:

Browse by Date:
<November 2005>
SunMonTueWedThuFriSat
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

Browse by Category:

Notables: