-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Forwards-incompatible with resumable exceptions #104
Comments
Generalising the exception proposal with resumption was the starting point for our work on effect handlers (which are more or less exceptions with resumption). That generalisation sounds attractive at first, and I have made sure that the proposal remains extensible in that direction. However, once you get into the details it turns out to be less attractive than one thinks at first, since for reasons of both performance and semantics (see below), you'll need a whole set of annotations distinguishing resumable from non-resumable that ultimately bifurcates the instruction set anyway. Plus other reasons I gave in my talk. The problem of conditional cancellation is mostly orthogonal to filtering and more complicated. The naive addition of resumption to a language like C++ will definitely wreak havoc; the same is true for any language with finally or equivalent mechanisms. The only sane design is one where resumable exceptions are distinguished from unresumable ones, and so are their handlers. Throwing a resumable exception must not invoke finalisers. Instead, a resumable handler that decides to abandon its resumption has to unwind the stack explicitly, e.g., by injecting an actual exception at the resumption point. Such a design is compatible with the current exception proposal, our effect handlers design works that way. |
No, from what I can find in the control literature (though I still have a lot left to read), you just need to separate stack walking (e.g. finding handlers) from stack unwinding, like you do in this sentence:
So why not use such a pattern here?
Unresumable exceptions are a special case of resumable exceptions (e.g. inhabitable resume type), so a coherent design would treat the two alike. Your compatibility plan is to make an entirely new kind of event that can encode both resumable and unresumable exceptions by separating handler code from clean-up code (like I'm advocating for here). This new kind of event will completely ignore the current That's not really forwards-compatibility. That's a plan to abandon this for more general constructs with a backwards-compatibility patch for legacy code. |
Not once you start thinking about the different performance implications. Resumable handlers need to run on a new stack. Unless you're thinking of some limited form of resumption that isn't sufficient to encode any interesting control abstraction.
AFAICT, this is the only way to be compatible with code (legacy or otherwise) that isn't aware of resumption. An exception handler or finaliser in such code must be bypassed by a resumable exception/event, as any other semantics would break the code's assumptions. |
Here is how you run generators on the same stack, a technique that is easily adapted to resumable exceptions (assuming you and I mean the same thing by that term).
Right. Once you add resumable exceptions, you'll need to add As for handler code for different exception events, you would bypass them anyways, regardless of whether they're for resumable exceptions or for unresumable exceptions. So given that you will have to eventually add |
That scheme only works if (1) continuations cannot outlive their handler, (2) the continuation can only be invoked in the syntactic scope of the handler and (3) the coroutine can only yield in the syntactic scope of its definition. IOW, it's limited to shallow generators where continuations aren't first-class -- way too constrained for resumable exceptions or effect handler and the control abstractions you'd want to encode with them, such as lightweight threads.
You don't need non-bypassed handlers before you have resumption. If you add those later you end up with a design close to what I described. |
You listed a bunch of requirements that always hold for the standard Note that implementing generators with even one-shot continuations is extremely heavyweight. For every iteration you have to
That's a lot of steps for loop iteration, especially if this is a hot loop. Some of this can be optimized with JITing information (e.g. knowing that this is the only reference to the continuation so that locking is unnecessary), but we're supposed to not rely on JITing for reasonable performance.
I'm confused. In the statement you're responding to, I said you'd need to add bypassed handlers. Did you misread or mistype something? |
By the way, I just finished working out how to combine #105 with the continuations sketch you presented. The combination is a nice halfway point between the two and points to a nice way to resolve a corner-case problem I found in each of our sketches. I'm happy to write the sketch up for you somewhere, I'm just not sure where. #105 came to mind, but I was worried that putting it there would make it unclear that all of that works without continuations. |
Yes, for (shallow) generators, but we wouldn't want to make a special feature just for generators. To encode other common control abstractions you need either first-class resumption continuations or, equivalently, the ability to lambda-capture yield and resume instructions (as some languages allow). Both implies delimited continuations, and thereby creation of new stacks. |
Right. So for the features that do not need continuations to express them, we should have primitives that can express them (see #105, which is not at all specialized to generators). And for features that do need to continuations, we have an extension to support stack allocation, pausing, stepping, and running, which are the low-level primitives that combine with the stack primitives in #105 to support continuations and/or yield. (For example, |
#105 now has the primitives for supporting algebraic effects. Hopefully tomorrow it will have some more primitives for working with stacks/continuations that are inexpressible with algebraic effects. |
* Polish table.init exec spec * table.set and table.init don't have the same imm. * Update document/core/exec/instructions.rst Co-Authored-By: Andreas Rossberg <[email protected]>
While `linking.wast` tests that we can link modules that are importing and exporting mutable `externref` globals, there were no tests exercising `global.{get,set}` on mutable `externref` globals.
Resumable exceptions are useful for implementing a variety of language features. For example, C++'s
throw;
and Python'sraise;
(both without an exception argument) are easily implemented with resumable exceptions, obviating the need to maintain an stack of currently-being-handled exceptions. Regardless, based on the discussion in #102, it seemed that people would appreciate knowing that resumable exceptions are incompatible with the current proposal.To see why, first remember that resumable exceptions are still exceptions—the handler has no obligation to resume the exception and might just usurp control. With that in mind, consider the following psuedo-code:
At the point where
generate
is thrown, we have to walk up the stack to find out if it is resumed. So first we run handler-2. Let's say that doesn't handle the exception, in which case (according to the pattern prescribed/forced by the current design) handler-2 cleans up the resources acquired by g, in particular invoking~Resource()
on&rec2
. After cleaning up, handler-2 rethrows thegenerate
exception, which is then caught by handler-1. Let's say handler-1 resumes thegenerate
exception with the value928
, restoring control toh
just after thethrow
.h
then hands928
off torec2
, which then presumably fails in some way becauserec2
was destructed.To summarize, a principle of the current design is that filtering code, handling code, and clean-up code can only be bundled together into one unifying
catch
clause, but resumable exceptions realistically need clean-up code at-the-least to be separated from filtering and handling code so that, if the exception is resumed, the stack (or really program state) is still in a realistically usable state.The text was updated successfully, but these errors were encountered: