I've had an idea.  One way to ensure that a handler is not in scope
when invoked (though possibly not the only way) is to keep the list of
active handlers in a dynamic variable binding.  The code for C<throw>
can then *rebind* that variable, popping handlers off (in a way that is
not destructive, i.e. leaves the value of old bindings untouched) before
invoking them.

   Here is pseudocode for a possible implementation:

        throw:
            maybe call a hook here for handler tracing.
            rebind the handlers list to a new copy of the value [1].
            while there are handlers left {
                pop the next handler from the current binding.
                invoke it.
            }
            pop the (now empty) new handler list binding.
            if nonfatal (because there's a continuation?) {
                just return.
            }
            elsif debugging is appropriate (defined how?) {
                invoke the debugger.
            }
            else {
                die hard.
            }

This has some advantages:

   1.  The set of bound handlers is easily introspected in PIR.  (Not
that introspecting closures is currently of much use, but that can
change.)

   2.  The handlers are removed from the dynamic environment without
affecting other aspects of the dynamic environment, and are
automatically restored to the right thing when C<throw> is exited,
either normally, or by calling a continuation, or whatever (but see
drawback 3 below).

   3.  If the above pseudocode is implemented in PIR as a library
routine (as it easily could be), then the C substrate doesn't have to
keep track of exceptions that are in the process of being thrown.

   4.  In fact, and unless I have missed something additional that Perl
6 requires, this makes C<throw> completely re-entrant, in that the
throwing of multiple exceptions could be nested, or even interleaved via
coroutines and/or continuation calling.

And also some drawbacks:

   1.  Exception handling might be slowed down somewhat wrt a pure C
implementation.

   2.  It makes PDD23 depend on dynamic binding, which is even more in
the "vapor" stage.

   3.  When a handler invokes a continuation, the the dynamic
environment will be unwound to the continuation's scope, causing
C<push_dynscope> thunks to be invoked *in the original handler context*.
If an inner C<push_dynscope> causes the same class of error, the very
same handler might be re-invoked.  This is logically consistent, but
wierd, and may not be acceptable [2].  If not, I'm not sure what to do
about it.

   However, I believe this gives the best semantics for the long haul.
Indeed, I can't think of another good way to implement this, though I
may be blinkered by my Lisp experience:  The pseudocode above could have
been written for the CMUCL implementation of SIGNAL.

   Thoughts?

                                        -- Bob Rogers
                                           (trying not to be a vapor designer)
                                           http://rgrjr.dyndns.org/

[1]  The copy can be avoided if the handlers are kept in a Pair list.

[2]  The ANSI CL spec is silent about this behavior (AFACS), but I
     believe that CMUCL does work this way.

Reply via email to