Re: Rationale behind unwind-protect and double errors



ulrich@xxxxxxxxxxxxxxxxxxxxxxxxxx (Ulrich Neumerkel) writes:

Now an error is signaled in the protected-form and (part of) the state
for cleanup-form is corrupted (related to the signalled error).
The cleanup-form thus reports its own seemingly unrelated error.

No. The original error has already been handled by this point.

Lisp's condition system is much more powerful and sensible than the
C++/Java/Python/etc. try/catch exception handling system. Let's examine
that system for a bit before I explain why Lisp is different. If you're
very unlucky, I'll also explain why Java's exceptions are quite so
godawful slow. (I'll pick on Java here because it's an easy target.
Err..., no, because it's fairly well known and representative of a
number of languages with similar exception handling facilities -- it's
better known than Python and it's much simpler than C++. But it is also
an easy target -- and GLS should have known better.)

So: an error occurs in our Java program: an exception is constructed (if
necessary) and thrown. The runtime system takes over and looks at the
call stack. It unwinds the stack until it finds an active `try' block.
It looks to see whether the `try' block has attached a `catch' clause
which matches the exception, or a `finally' clause. If the latter, it
executes the stuff in the `finally' block, and continues the search[1]. If
the former, it executes the matching handler. In both of these cases,
the `catch' or `finally' code is executed in the same dynamic context as
the immediately surrounding stuff -- any functions called within the
`try' are dead and gone. They can't come back.

The most important point to notice here is that, in a `finally' block,
the exception hasn't yet been caught. When the `finally' code finishes,
we'll resume the search for a matching handler.

Right. Lisp is different. (Take that out of context and use it as a
slogan.) Lisp, at least in this regard, is /better/. When a condition
is signalled (ooh, new words -- think `exception is thrown' if it makes
you feel happier, though the concepts are subtly different), the SIGNAL
(or ERROR, or whatever) function looks at a table of active handlers,
and picks the most recent handler which matches, and invokes it. There
and then. No unwinding, nothing. Not even a hint of UNWIND-PROTECT.

The handler can do all sorts of things now. It can return, for
instance, in which case the system looks for another handler; if we run
out of handlers, the program might continue merrily on its away,
blithely assuming that everything has been fixed. Or the handler might
make a nonlocal jump -- GO, RETURN, THROW, or INVOKE-RESTART -- in which
case we start unwinding stack frames and doing UNWIND-PROTECT stuff.
But the critical difference here is that the condition handlers have
/already/ had their chance to do something about the condition, and
we're just finding a safe place to continue execution.

(HANDLER-CASE does do unwinding before it invokes your handler; but
if you cared, you'd use HANDLER-BIND instead, right?)

So, why do Java's exceptions suck? It's because Java exception objects
(java.lang.Throwable things) carry a stack backtrace with them, which
exception handlers might look at and do things with. But by the time
the exception handler's been called, some of the stack frames have
already been discarded. Therefore the runtime must reify the stack in
advance (or at least build the backtrace while it's unwinding the
stack).

Lisp doesn't need to do this. If you register a condition handler using
HANDLER-BIND, it gets called in the context of the whatever signalled
the condition -- so the stack is still there for poking, prodding,
fiddling with or printing. Or debugging, of course.

It's a crying shame that other languages haven't adopted Lisp's clear
separation between handling conditions and resuming execution.

[1] Java discards its exception if a `finally' block `ends abruptly' --
e.g., returns, or throws an exception. This really will lose
exceptions. Sorry: Java blows goats.

In a programming situation the debuggerhook break - going into the
first signaled error might be a good reaction. But offline, with
some toplevel that briefly logs the error and recovers, priorities
are differnt.

But that's not a problem. The toplevel HANDLER-BINDs its handler; the
condition is signalled, the handler logs its message, and then THROWs or
INVOKE-RESTARTs from some safe place. If some UNWIND-PROTECT form in
the way finds itself in a bad state and signals a condition, well, that
gets handled too. Nothing is lost; everything is good.

-- [mdw]
.



Relevant Pages

  • Re: Frame-based exception handling problem on Server 2008
    ... If all you want is a signal, then would a vectored exception handler work? ... Cygwin's exception handling. ... "continue search" value then there would be no reason to even read ... converted to POSIX signals. ...
    (microsoft.public.win32.programmer.kernel)
  • Re: Try Finally...
    ... Now we can protect every more or less specialized action ... an error indication (exception or error code) has to be ... an according exception handler can be inserted into the code. ... resources, as they are related to specific actions. ...
    (comp.lang.pascal.delphi.misc)
  • Re: Frame-based exception handling problem on Server 2008
    ... Can't you return ExceptionContinueSearch without doing any unwinding ... exception handler got called shows that these exception handlers don't ... implementing a sparse data structure by using the OS's support for guard ...
    (microsoft.public.win32.programmer.kernel)
  • Re: Interrupts under Linux Re: Embedding Forth
    ... Not more complicated than straightforward interrupts, ... CODE (EXCEPTION) ... Two system calls: install handler, ... I'm far from intimidated by Unix signals. ...
    (comp.lang.forth)
  • Re: WinForms bug? ThreadException not invoked on Exceptions in system code pre-processing the me
    ... Message Queue and invoking the appropriate handler. ... if an Exception is raised during the PRE-PROCESSING of a Windows ... And because any handling will probably fail if there is no memory, the CLR will terminate the application without attempting to call the ApplicationException or UnhandledException events. ...
    (microsoft.public.dotnet.framework.windowsforms)