Asynchronous Exceptions
published: Mon, 21-Mar-2005 | updated: Thu, 27-Oct-2005
This article is an interesting email that was sent out to the CLR team at Microsoft about asynchronous exceptions. The email is, shall we say, information-dense. It’s targeted at the Framework developers, but there are some ideas we can take from this as application developers.
1. Don’t write code like this:
try { // blah } catch (Exception ex) { // do something with ex, such as log it throw; }
since you may catch something you really can’t do anything with. For example, even the logging code may fail in interesting ways in the face of some exceptions.
So always catch specific exceptions, such as SQLException instances. This link shows the exceptions thrown from the Framework (although I think the information shown here is for Whidbey — Visual Studio 2005 — only).
And if you remove the throw statement from the catch block (thereby swallowing the exception) you may leave the application as a whole in an unstable state.
2. A corollary: never throw instances of Exception. Always use a user-defined exception class.
3. Try not to throw exceptions from a constructor, especially if you’ve allocated non-memory resources prior to the throw. The finalizer (and if you are allocating non-memory resources, you should have a finalizer as well as implementing IDisposable) will run at some indeterminate time later.
4. Use the using
syntax to create objects that implement
IDisposable. It’s very likely that the compiler code generation or the
JITter will be enhanced in this instance to ensure that objects are
properly (deterministically) disposed even in the face of asynchronous
exceptions.
The problem here is that if you have code like this:
using (Foo foo = new Foo()) { // blah }
an asynchronous exception may be thrown in between the constructor
completing (and therefore the object being created and alive) and
the assignment to foo
. If the assignment didn't take
place, the using statement is
not entered and the newly-created object becomes unreferenced, to be
collected at some indeterminate time later. And presumably because
IDisposable is being implemented, there's a finalizer in there
somewhere, and this will only get called, much much later.
Now, there is nothing we can do about this at the moment, so just
ignore it. But, if those awfully nice people in the C# team (or maybe
in the Framework team) do their stuff, they may fix this issue with
using
.
5. The JITter does some heroic funky stuff to ensure that a monitor allocated with a lock statement is released no matter what.
6. Code is guaranteed not to be interrupted by an asynchronous exception in a finally or a catch block. Interesting (as in, I didn’t know this), but we shouldn’t take advantage of it in our code.
(via Brad Abrams)