Friday, May 19, 2006

Resumable exceptions

One more thing I learned from Avi: there's a simpler way to implement resumable exceptions than the way I'd thought of. Having been perverted by my formative years as a Java programmer, I always assume the meaning of throw was carved in stone tablets:
  1. Thou shalt abandon thy current context.
  2. Thou shalt unwind the stack, verily, until thou findest the first handler.
So my solution involves capturing the continuation at the point of the throw, to allow the handler to reinvoke the continuation, effectively resuming at the point where the exception occurred. But why waste time unwinding the stack, if you're only going to put it right back in place? The Smalltalk solution (and I believe the Common Lisp solution as well) is to leave it up to the handler to decide whether to unwind the stack. In other words, throw (I think it's called raise in Smalltalk) simply causes the runtime to crawl up the stack, which it leaves intact, to find the first handler, and lets the handler decide whether to unwind, i.e., to blow away the portion of the stack between the throw point and the catch point.

I suppose this means the handler runs on top of the portion of the stack where the raise happened, and there must be some provision for what to do if the handler finishes without a control operation; presumably this would have the same behavior as resume nil, or however you'd express "don't unwind the stack; resume where the exception occurred; but I don't have any useful result value for the failed operation."


Bret said...

If you want your exception handler to pop you into an interactive debugger, or even just grab a stack trace, then it (or something) has to run on top of the thrower's stack frame. Otherwise, there's nothing left for the handler to debug!

Lua does not have resumable exceptions, but it does allow for this situation with the xpcall function:

xpcall( func, errFunc )

This calls the closure "func". If func throws, "errFunc" is called before the stack unwinds, and then xpcall returns func's exception object.

Anonymous said...

Dan Friedman, Chris Haynes, and Kent Dybvig played around with these ideas in their Proposal for Exception Handling in Scheme, in which they discuss a couple of interesting variations on the standard exception-handling semantics, including one that I think is fairly similar to what you describe.
-- Richard

John Clements said...

In fact, what you describe is (as I understand it) the current state of affairs in mzscheme, where the 'current-exception-handler' can decide whether or not to invoke an escape continuation. That is, there's a hook (determined by a parameter, and therefore itself determined dynamically) that allows you to grab things before it goes to the leap.

Anonymous said...

Structured Exception Handling (a Microsoft extension to C) also supports continuable exceptions. One use is transparently extending a thread's stack during execution.

SEH uses a linked list of filter functions interested in exceptions that goes up the call stack. After an exception is raised, the first pass calls each filter function in order going up the stack until one decides to either handle the exception by returning EXCEPTION_EXECUTE_HANDLER or ignore the exception and continue at the point where it occured (EXCEPTION_CONTINUE_EXECUTION). If the exception is handled, a second pass is done to actually unwind the stack and do cleanup by calling those functions again.