Hacker News new | ask | show | jobs
by deanfranks 5153 days ago
I don't think this would work on the code or compiler end. For the compiler, when an exception is thrown, the stack is unwound back to the most recent try so the exception handler can be run in the scope of the try. In order to implement this, the portion of the stack that is unwound would have to be saved somewhere and when the recover is executed, the stack would have to be unwound back to the try again, the saved portion of the stack would have to be "repushed" and execution would resume.

On the code end, how would the exception code know what failed and what to twiddle to fix things? The logic associated with the exception code would be extremely complex and tightly coupled to the "downstream" code. It would make much more sense to validate things before performing operations you know might fail to reduce the chances that an exception would occur. Remember that executing the throw portion of an exception is extremely expensive. Exceptions should only be used for exceptional cases, not as a flow control mechanism for a common execution path.

3 comments

  In order to implement this, the portion of the
  stack that is unwound would have to be saved
  somewhere and when the recover is executed, the
  stack would have to be unwound back to the try
  again, the saved portion of the stack would have
  to be "repushed" and execution would resume.
Which is precisely what continuations do. Not exactly an unsolved problem, and pretty much the reason why you can implement this in Lisp with call/cc.

  On the code end, how would the exception code know
  what failed and what to twiddle to fix things?
That's a more interesting question, but it's at least partially answered by "what does a normal catch block do?" Presumably, exceptions contain some information about what they are and why they happened.
I'm not super familiar with compiler internals, but wouldn't it be possible to push the exception-handling frame as an additional frame to the top of the stack instead of unwinding? This way the stack would be preserved. In addition, if the local symbols are kept around and the optimising compiler doesn't reuse stack space for multiple variables, wouldn't that allow binding the symbols of the exception throwing stack frame to either local variables in the exception handling scope or some sort of a dictionary?
It would be ugly. For many architectures, locals are referenced relative to a [stack] frame pointer. The local variables from the original context of the function containing the catch would need to accessible, and any additional local variables allocated during the execution of the catch could not be added to the area addressed by the frame pointer because that portion of the stack would contain return addresses and locals for the function throwing the exception, the exception data and any additional functions between the catch function and the throw function.

If is far from impossible to implement, but it would be messy.

IMHO try/catch blocks for this kind of retry logic would have to be very small in scope to be practical and maintainable. Any significant complexity in the scope of the try block (multiple nested functions, etc) is going to create two very tightly coupled sets of code non-obviously separated in the source. This kind of pathological coupling is evil.

Since the scope should be kept small, I would always opt for comprehensive sanity checking before the operation rather than complex, oddly coupled code that is almost impossible to test.

As long as you keep two stack pointers (one to the point where the exception was thrown, and then unwind the second to the try block) it shouldn't be that much of a problem. Just specify that you can either recover (at which point you change the state from where the exception is thrown and abandon the stack pointer at the try block) or catch (at which point you abandon the stack pointer at the throw statement and are free to call functions, etc. to otherwise recover from the exception) but not both.