Brian Parks


The (Abusing, Misusing) Stable Context Pattern

15 Oct 2015

How often have you written a piece of code that sets state (opens database connections, sets variables to let the rest of the world know what it's doing, etc.) at the beginning, does some processing, and then resets (or unsets) that state at the end of the function. Something like the following, which is arguably a subset of what I'm describing, but is realistically analogous to the piece of code that led me to this pattern (by fixing what may be one of the most embarrassing bugs of my career):

//C#
public void DoSomething()
{
    if (_ignore) return;
    _ignore = true;
    //heavy lifting here
    _ignore = false;
}

In my case, I wanted to ensure that the heavy lifting part of the method would only be running once at any given time (not for any integrity reasons or to prevent deadlocks or race conditions, but simply to prevent the same work from accidentally being done twice). Certainly if this method is called twice on two threads within "a short period of time" then the naive means of achieving synchronicity breaks down, so this bit of code may not be the best example of the pattern I'm proposing, but for sake of simplicity (and because it resembles my own code), let's assume that the code above always accomplishes what it looks like it accomplishes.

This is all well and good, until something in the heavy lifting throws an exception:

//C#
public void DoSomething()
{
    if (_ignore) return;
    _ignore = true;
    throw new Exception("Something broke");
    _ignore = false;
}

What happens now? The first time DoSomething runs, it will throw an Exception, and _ignore will never be set to false. From then on, every invocation of DoSomething will be short-circuited and heavy lifting will never be done again.

Of course, those of us well-versed in dealing with Exceptions will notice the obvious answer:

//C#
public void DoSomething()
{
    if (_ignore) return;
    _ignore = true;
    try {
        throw new Exception("Something broke");
    } catch {
        throw;
    } finally {
        _ignore = false;
    }
}

As obvious as it may be, it's at least as ugly. That's at least an extra 5 lines of code, most of which really don't add anything but clutter to the actual functionality. Sure it prevents a bug, but is it worth it?

Let's suppose it is worth it. However, our method depends on doing some other checks, such as the following:

//C#
public void DoSomething()
{
    if (_ignore) return;
    _ignore = true;
    if (SomeFunction() == null) return;
    try {
        throw new Exception("Something broke");
    } catch {
        throw;
    } finally {
        _ignore = false;
    }
}

Maybe SomeFunction() is expensive, so we're trying to only call it if we know we're going to use its result. Whatever the reason, we know we can't continue in some cases.

But wait! Now we've introduced the same bug we "fixed" with the finally block. And now our code is so muddied up that we can't really even see that what we intend is for _ignore to always get reset, regardless of how we exit the method.

This is the key here: we have defined a context inside which a particular state should be set, but outside of which that state should be reset. This should always be the case; it should be stable. And ideally, we should use syntax to our advantage to protect ourselves from ourselves.

If only there were a construct that enforced that, regardless of how a context is exited, some functionality occurred...

Thankfully, in C#, there is something that we can reuse, misuse, and abuse: the using keyword. The using keyword works with an object of any type that implements the IDisposable interface, scopes it to the block that follows, and calls the object's Dispose method before leaving the block. We can use this to record our state changes in a declarative fashion by creating a class that implements IDisposable:

//C#
class StableContext : IDisposable
{
    private Action _exitContext;
    public StableContext(Action onEnter, Action onExit)
    {
        onEnter();
        _exitContext = onExit;
    }
    public void Dispose()
    {
        _exitContext();
    }
}

With our example from above, we can use StableContext as follows:

//C#
public void DoSomething()
{
    if (_ignore) return;
    using (var context = new StableContext(
        onEnter: () => _ignore = true,
        onExit: () => _ignore = false
    ))
    {
        if (SomeFunction() == null) return;
        throw new Exception("Something broke");
    }
}

Clear, declarative, and concise. And it doesn't matter how you exit the context; the StableContext will always be disposed, which will always result in your state being reset.