saving exceptions

My previous post explained the need to provide debugging context with C++ exception objects and provided an easy way to manually incorporate arbitrary pieces of context. An interesting suggestion is to incorporate a stack trace into the exception. This time, I’ll look at exceptions in a multi-threaded environment.

Termination style exceptions trace back to the notion of the “undefined” symbol in abstract functional programming. Any expression containing the undefined symbol evaluates to the undefined symbol. In C++, an “expression” is meticulously assembled on the stack. A throw statement is tantamount to dropping the undefined symbol into the expression embodied by the stack. Instead of simply reducing to a single symbol, the throw statement maps to a new expression (beginning with the catch statement) through the following steps:

  1. squirrels away an arbitrary piece of state, pushing it onto the exception stack,
  2. abandons the partially evaluated expression embodied by the current call stack,
  3. and then evaluates a sequence of catch handlers.

Real applications are multi-threaded. Even if side effects are successfully managed in our C++ code, the program-is-an-expression model is not adequate anymore. We have to consider how an exception in one thread should relate to the execution of the other threads of the system.

Many multi-threaded scenarios can be mapped onto the “futures pattern”. A primary thread initiates an asynchronous action and retains the option to retrieve a result at a later time, through a proxy object that represents a “future” value. In this scenario, the correct behavior is obviously that an exception be transfered from the asynchronous actor back to the primary thread when it attempts to retrieve the actual “future value”.

Thus, we want a mechanism to transfer and continue the cancellation action of an exception from an asynchronous actor to a primary thread. This transfer is passive, meaning there is a phase after the exception passes out of the asynchronous actor and before it enters the primary thread. (It is interesting to note that Haskell has an active “throwTotransfer mechanism where a perpetrator thread can asynchronously inject an exception into a victim thread.)

Now - back to C++’s state squirreling. Our exception is held just outside the reach of the “catch” statement. We can only see the slice of it that corresponds to the catch declaration. This presents a bit of a problem when we try to implement something that transfers the exception between threads. The “re-throw” feature lets us transfer the hidden exception state between catch handlers within a thread. However, there is no mechanism that can either completely reveal the hidden state or transfer it to a second thread.

C++09 will solve this problem by adding a handful of functions to retrieve and manipulate an opaque pointer (exception_ptr) to a thrown exception.

Of course, we need this to work in 2007. The simplest approximation of the exception_ptr system is to add a “cloneable_exception” base class with “clone” and “rethrow” virtual functions. However, this approach does not address exceptions thrown from 3rd party libraries. It builds in a kind of obscolescene, since the cloneable_exception base class will be unnecessary once we move to C++09.

Anthony Williams has provided an implementation of exception_ptr for MSVC based on lower-level details of structured exception handling.

I have prototyped a portable implementation, available here. My system requires that all exception types be registered (this is automatic for exceptions thrown using boost::throw_exception). The implementation still needs mutual exclusion protection for access to the exception type registry and reference counts. It effectively adds an “external clone” function to all registered exception types.

Leave a Reply