Exception Destruction

Languages that support exceptions need to support destructors or they need to support a try/finally construct. Otherwise using exceptions is too difficult, because if you have some local state to clean up in a function, you have to catch and rethrow every exception.

The goal of exceptions in C++ is that code which does not throw an exception should be just as efficient as code which is compiled without any support for exceptions. Unfortunately, this is impossible. When any function can throw an exception, and when there are destructors which must be run if an exception is thrown, the compiler is limited in its ability to move instructions across function calls. Of course it is not generally possible to move instructions which change global or heap memory across a function call, but in the absence of exceptions it is generally possible to move instructions which do not change memory or which change only stack memory. This means that exceptions limit what the compiler is able to do, and it follows that compiling with exception support generates code which is less efficient than compiling without exception support.

Of course exceptions still have their uses, but lets consider programming without them (this is easy for me to imagine–I didn’t use exceptions in the gold linker). If you program without exceptions, how useful are destructors and/or try/finally? What comes to mind is functions with multiple return points, loops with multiple exits, and RAII coding.

C has neither destructors nor try/finally. Does it miss them? I would say yes. A common workaround I’ve seen is to change all return points and loop exit points to use a goto to a label which does cleanups.

The gcc compiler has an extension to C to support, in effect, destructors. You can use __attribute__ ((__cleanup__ (function))) with any local variable. When the variable goes out of scope, the function will be called, passing it the address of the variable. This is an effective extension, but it is not widely used.

5 Comments »

  1. gps said,

    October 15, 2008 @ 8:55 pm

    Looking the gcc cleanup variable attribute up I find this documentation:

    http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes

    It doesn’t answer one important question:

    When multiple variables in one scope have a cleanup function, what order are the cleanup functions called in?

  2. Ian Lance Taylor said,

    October 15, 2008 @ 9:01 pm

    The cleanup functions are called in the reverse of the order of declaration.

    In effect each variable with a cleanup attribute implicitly introduces a new try/finally scope. Everything which follows the declaration of the variable is in the try block, and the cleanup function is in the finally clause.

  3. Alan Stokes said,

    October 16, 2008 @ 2:15 am

    Languages that support *error handling* need to support destructors or they need to support a try/finally construct.

    Being able to specify cleanup just once is important, but is orthogonal to whether you signal an error via an exception or returning an error code.

  4. ncm said,

    October 18, 2008 @ 11:51 pm

    I’ve read and re-read this posting, and it seems about equally nuts (but in slightly varying ways, according to emphasis) each time. Maybe I don’t understand what instructions the compiler might (otherwise) move across which function calls, but can’t because of destructors, and how that would have much effect on performance. Aren’t the destructors that run in performance critical code inline, anyway, and subject to aggressive optimization? How are cleanup functions better, this way?

    The measurements I’ve heard of showed exception-using code substantially faster because many error checks after function calls could be deleted outright.

  5. Ian Lance Taylor said,

    October 20, 2008 @ 9:44 pm

    Alan: it is of course possible to implement error handling with single cleanup using goto. Lots of C code is written that way. In a language which doesn’t have exceptions, try/finally is just syntactic sugar: useful but not essential.

    ncm: it’s not the destructors which prevent moving instructions across function calls. It’s the fact that you can’t move an instruction from one exception region to another. That is, when using exceptions, all constructors and destructors must be completely run before or after a function call. It is impossible to spread the instructions for a constructor or destructor across a function call, because you will be in an inconsistent state if an exception is thrown.

    Cleanup functions aren’t any better, I just threw that in as a comment.

    You’re right, in some cases you will win because of skipping the error checks. In code like gold, I wouldn’t, because I don’t need to do error checks. I can just crash if something goes badly wrong.

RSS feed for comments on this post · TrackBack URI

Leave a Comment

You must be logged in to post a comment.