Software Testing Blog

Resources vs exceptions

Here’s a question I get fairly frequently:

I always use the using statement to ensure that my unmanaged resources are cleaned up. But suppose I have two resources created in a single using statement (Foo is a class that implements IDisposable):

using(Foo fooAlpha = new Foo(), fooBravo = new Foo())
{
  ... use fooAlpha and fooBravo
}

What if the constructor for fooBravo throws an exception? Am I guaranteed that the resource associated with fooAlpha is disposed?

Good question.

The immediate answer is no. Suppose that the second constructor call throws, and there is an exception filter already on the stack which invokes Environment.FailFast(). Filters run before finally blocks, so in that case the process would be immediately destroyed; no cleanup code runs because there is no process to run it in. Or suppose the filter code contains an infinite loop; in that case the finally block never runs either.

Moreover, the CLR specification says that an unhandled exception in a process is undefined behaviour, so if the exception thrown is unhandled then all bets are off; any behaviour is legal behaviour.

Let’s assume that we’re not in one of those crazy cases. In that case the answer is yes, you have that guarantee, but the general case is more complicated than you might think.

But we’re getting ahead of ourselves. Let’s describe precisely what the code means. The code above means the same thing as

using(Foo fooAlpha = new Foo())
{
  using(Foo fooBravo = new Foo())
  {
    ... use fooAlpha and fooBravo
  }
}

which in turn means the same as:

{
  Foo fooAlpha = new Foo();
  try
  {
    Foo fooBravo = new Foo();
    try
    {
      ... use fooAlpha and fooBravo ...
    }
    finally
    {
       appropriate code to dispose fooBravo here
    }
  }
  finally
  {
    appropriate code to dispose fooAlpha here
  }
}

Therefore we can answer “yes” to the question that was asked: if the second call to the Foo constructor throws then control is transferred to the finally which disposes fooAlpha. (Again, assuming no shenanigans that cause the finally block to not run at all because the process was nuked from orbit or some such thing.)

However, if we take a closer look we can see some problems. Actually we can show that there are problems even with only a single allocation! What precisely happens on this line?

  Foo fooAlpha = new Foo();

This is equivalent to:

  Foo fooAlpha;
  Foo temp = AllocateABlankFooOnTheHeap();
  temp.ctor();
  fooAlpha = temp;

As you can see, though the assignment of the reference to the variable is atomic, the operation as a whole is not atomic. What if a thread abort exception is thrown after the call to temp.ctor() but before the assignment to fooAlpha? We are not in the try block yet so control is never transferred to the finally. And even if it were, we haven’t filled in a value to fooAlpha yet, so the finally doesn’t have a reference to the thing that needs to be disposed!

Now you might say, well then, let’s make sure that Foo uses a destructor. A destructor runs even if a constructor threw an exception – which makes destructors hard to write correctly! So let’s make sure our destructor is correct even if a thread abort exception is thrown while control is in the constructor:

sealed class Foo : IDisposable
{
  private IntPtr handle = default(IntPtr); // a "zero" handle is invalid.
  private bool disposed = false;
  public Foo()
  {
    handle = UnmanagedCode.MakeResource();
  }
  ~Foo() { Dispose(false); }
  public void Dispose() { Dispose(true); }
  private void Dispose(bool fromDispose)
  {
    if (disposed) return;
    disposed = true;
    if (handle != default(IntPtr))
      UnmanagedCode.DestroyResource(handle);
    handle = default(IntPtr);
  }
}

Seems legit. Nothing can possibly go wrong here, right? Pop quiz: in this scenario, where a thread abort exception has been thrown at an arbitrary location in the constructor, which of these statements are true and which are false?

  • We never call DestroyResource on a zero handle.
  • We never call DestroyResource on the same non-zero handle twice.
  • If MakeResource returned a non-zero handle then we always call DestroyResource on that handle.

Before you read on, try to figure out which of those statements are true and which are false.

(Some commenters have correctly noted that the code given is not threadsafe for the scenario where we do not throw in the constructor. That’s correct, but the subject of how to deal with objects that might be disposed twice on two different threads is a good one for another day. Today I am concerned about the question of how exceptions interact with disposal.)



Welcome back. The correct answers are that the first two are true but the last statement is false. Again, non-atomicity bites us:

    handle = UnmanagedCode.MakeResource();

is equivalent to

    IntPtr temp = UnmanagedCode.MakeResource();
    handle = temp;

The assignment is typically atomic, but the larger operation of call-a-method-and-assign-the-result is not. What if a thread abort exception is thrown between those two statements? In that case the unmanaged resource has been allocated, but since handle is never filled in, it is still zero! When the destructor runs the field will be zero and the handle is leaked, even though we made a finalizer and used a using statement!

How do you solve this problem? Generally speaking, you don’t. Rather, you decide that you’re willing to live with the fact that in extraordinarily rare cases, a thread abort exception can cause a resource to leak. This is yet another reason why thread aborts are a worst practice and should only be done as a last resort, but that’s a subject for another day. If you absolutely positively must be sure that a resource is freed even in those extremely rare situations then there are a variety of advanced techniques you can use to constrain the locations in which thread abort exceptions may happen, ensure atomicity of assignments, and so on; they are well beyond the scope of this article.

In summary: the using statement makes a good-faith effort to be polite and free a resource early, but it does not provide a guarantee. There are rare circumstances in which a disposable object can be created in a using block’s initializer but never disposed, and in some of those situations the destructor won’t save you either unless you are extremely clever about how you wrote your constructor. If you absolutely positively cannot under any circumstances tolerate a leaked resource then you have a difficult problem on your hands and will need advanced tools. Start by thoroughly understanding the SafeHandle base class and work your way up from there.


As always, if you have questions about a bug you’ve found in a C, C++, C# or Java program that you think would make a good episode of ATBG, please send your question along with a small reproducer of the problem to TheBugGuys@Coverity.com. We cannot promise to answer every question or solve every problem, but we’ll take a selection of the best questions that we can answer and address them on the dev testing blog every couple of weeks.

  1. Shouldnt all 3 be false. There is nothing preventing two threads from calling dispose simultaneously, both initially passing the first check for if(disposed) and therefore depending on execution order both calling DestroyResource on the same handle or one calling DestroyResource on IntPtr.Zero.

    1. Park: I should clarify. The supposition of the scenario is that a thread abort exception has been thrown during the constructor. In that case what code is running Dispose on another thread? How did that other thread get a reference to the object to call Dispose on? The only way that could happen is if the constructor, say, caches a copy of “this” in a static field, but that doesn’t happen here.

  2. #2 would be false if “Dispose” is called from two threads at the same time though. It’s possible for a thread to see that “disposed” is false, but then yield to another thread before setting it to true. Both threads would then go on to release the non-zero handle twice.

    #2 is only true if “Dispose” is only ever called from a single thread.

    1. See my response to Park above. I was not intending to say that any possible pattern of calling this code never disposes twice. I was trying to point out that this code does not achieve all the things we want it to achieve even in the limited scenario where an exception is thrown during the constructor.

  3. In a very unlikely theoretical way, I think statement [2] “We never destroy the same non-zero handle twice” is incorrect.

    As far as I can see, nothing prevents the C# compiler, the JIT or the hardware to move the “disposed = true” at the end of the method (the field is not volatile).

    Once you accept that (unlikely) event, then a ThreadAbort may happen between DestroyResource and “disposed = true”. Consequently, DestroyResource will be called a second time by the destructor.

  4. I’m wondering, what happens if a ThreadAbortException is raised when the object is being disposed? Will the destructor then attempt to run Dispose(false) again? Seems that if that’s the case, then #2 would false if an exception is raised just after the resource is destroyed, but before the handle is zeroed out.

    1. Alex I’m not quite sure I understand your scenario. The finalizers run on their own dedicated thread; are you asking what happens when that thread is aborted? Terrible, terrible things happen when that thread is aborted; it’s owned by the CLR, not by any user code, and should not be aborted.

    2. I think your scenario is: the constructor succeeds. Dispose() is called normally on a user thread. If Dispose gets as far as setting Disposed to true and then the thread is aborted, the finalizer thread will then skip the disposal and the object is never disposed.

      My intention in this article was to discuss the races that can happen due to failure in the constructor. But as you point out, my code is also inadequate in the face of races in the disposal code. Also, good practice would be to suppress finalization if an object is disposed normally, as that improves GC performance.

      Long story short, this is all messed up. Getting disposal logic perfect is hard.

      1. Yes, that’s exactly the scenario I was thinking of. I realize it’s not per the rules for this excercise, but it’s the only way I could think of invalidating your second point.

        But now I’m very curious about the sort of horrible things that happen if a finalizer were to call Thread.CurrentThread.Abort(). Does this kill the GC, or that runs on yet another thread?

  5. I am going to inject some Thread.CurrentThread.Abort()s into callbacks for giggles and see what happens. Like ~C( ) or the timer callback etc.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Current day month ye@r *