Exceptions are Easy. Except When They’re Not (Part 2)

Nothing is said which has not been said before. ~ Terence (195/185 BC-159 BC) Playwright of the Roman Republic

In a previous post, I posed two questions to my readers (all √(-1) of them): whether a body of code was exception safe, and if there was anything I needed to change to insure the code was consistent should an exception be thrown.

The pertinent code is reproduced here:

public class Foo
{
    // ...
    private void Load()
    {
        ServiceProxy<IFooConfig> proxy = null;
        try
        {
            proxy = ServiceHelper.CreateProxyFromServiceContractInf<IFooConfig>(
                           InfoSourceUri.ToString());

            IFooConfig config = proxy.ProxyContract;
            this.NumberOfFrobs = config.NumberOfFrobs;
            this.WhereToGetThem = config.FrobSource;
            for (var e in config.MiscellaneousEntries)
            {
                this.Miscellaneous.Add(e);
            }
        }
        catch (Exception ex)
        {

            // Report the error and then ...
            throw;
        }
        finally
        {
            proxy.CleanupProxy(); // Extension method that aborts if proxy is faulted or ignores if null
        }
    }
}

To answer the first question we need to understand what exception safety is. It’s generally broken down into 5 categories:

  • No guarantees — when an exception is thrown, the program crashes — often leaving persistent things in an indeterminant state.
  • Minimal (or “No Leak”) guarantee. The program doesn’t crash, and resources don’t leak, but there are no guarantees about side effects.
  • The basic (or “Weak”) guarantee: the invariants of the component are preserved, and no resources are leaked (called the “weak guarantee”, as the system is left in a safe but altered and unknown state). Recovery is only possible by introspection of the state of the objects involved.
  • The strong guarantee: that the operation has either completed successfully or when an exception is thrown, the state is exactly as it was before the operation started.
  • The no-throw guarantee: that the operation will not throw an exception.

Given those definitions, Foo.Load() provides at least the “minimal/no leak” guarantee for Foo. Since there aren’t any stated invariants about the class’ state, you could also argue it is in a “valid” state, giving the Basic/Weak guarantee.

The second question I asked points to the problem, though. As written, when an exception is thrown in Foo.Load(), the Foo instance will be in an unknown state. Which means it is unusable (or you have to break encapsulation and engage in some introspection to understand its state).

Let’s say Foo is used to configure something else in your system. It got the “NumberOfFrobs” value for that service, but if the call on the line that set “WhereToGetThem” threw an exception, the old value of WhereToGetThem (possibly) remains in that instance. Even worse, we’re not really sure where the situation broke down (what if the exception threw half-way through updating the Miscellaneous array — which portion is valid?).

This problem becomes very interesting when the behavior of an object depends on the state of several members of the object and only half are current.

There’s a very straightforward solution to this mess: Insure code supports the “strong exception guarantee. This means calls either succeed or they don’t change the system. Period. When we catch an exception from a method call, we know our program is in the state it was immediately prior to the call.

If this sounds familiar, it should — it’s just like working with transactional databases (or any other transactional system).

Do the error prone stuff first and then commit your changes in a block of code that can’t throw an exception:

    private void Load()
    {

        int nFrobs = 0;
        Uri where = null;
        Dictionary<string ,object> misc = null;

        ServiceProxy<IFooConfig> proxy = null;
        try
        {

            proxy = ServiceHelper.CreateProxyFromServiceContractInf<IFooConfig>(
               InfoSourceUri.ToString());

            IFooConfig config = proxy.ProxyContract;

            // exception-prone code:
            nFrobs = config.NumberOfFrobs;
            where = new Uri(config.FrobSource);
            misc = new Dictionary<string,object>(config.MiscellaneousEntries);

        }
        catch (Exception ex)
        {
            // Report the error and then ...
            throw;
        }
        finally
        {

            proxy.CleanupProxy(); // Extension method that aborts if proxy is
                                          // faulted or ignores if null
        }

        // NO THROW GUARANTEE required:
        // from this point on, the only valid code is code with
        // no-throw guarantee.  If default property setters can
        // throw, have a version that doesn’t (or set the field
        // directly).

        this.NumberOfFrobs = nFrobs;
        this.WhereToGetThem = where;
        this.Miscellaneous = misc;

    }

Note the comment near the end of the code — to have the strong guarantee, you must have some code that satisfies the no-throw guarantee (e.g., no exceptions are allowed to escape “no-throw” code — a good requirement for the IDisposable.Dispose() method, by the way).

That’s it. If you have a reference to a Foo, outside of Foo.Load(), it’s consistent.

If you’d like to dig further into this subject, David Abrahams originally wrote about this subject several years ago. While his original paper discussed it in the context of C++, the issues apply to .NET based languages as well.

No Comments yet »

RSS feed for comments on this post. TrackBack URI

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Comments links could be nofollow free.

Powered by WordPress with GimpStyle Theme design by Horacio Bella.
Entries and comments feeds. Valid XHTML and CSS.