So, the first file is, of course, src/exceptions.c. We also care about src/ops/core.ops. One function in src/scheduler.c towards the end. I'll show you the important functions and then give a description of what's going on and the parts we care about. First check out 'op throw' in src/ops/core.ops: inline op throw(invar PMC) :flow { opcode_t * dest; opcode_t * const ret = expr NEXT(); PMC * resume = new_ret_continuation_pmc(interp, ret); VTABLE_set_attr_str(interp, $1, const_string(interp, "resume"), resume); dest = Parrot_ex_throw_from_op(interp, $1, resume); goto ADDRESS(dest); } NEXT() looks up the address of the next opcode. new_ret_continuation_pmc creates a new 'return continuation', which is a continuation that is intended to only be invoked once. It's clone() makes it into a "real continuation". We then both set it as an attribute of the exception and pass it as the second non-interp arg to Parrot_ex_throw_from_op? That's a little bit sketchy. It's probably related to needed functionality for throw_p_p. Should we also set the secont argument of throw_p_p as the attribute of $1? Dunno. SO! Parrot_ex_throw_from_op! It's a lot easier to type with ^P in vim. I need to remember to use that more often. First, we call find_exception_handler. find_exception_handler will EITHER return the exception handler if any was found, OR if not, it does all the magic error printing stuff. This is where "No exception handler and no message\n" is printed. This other stuff really should be refactored into a separate function, and then Parrot_ex_throw_from_op and Parrot_ex_throw_from_c can each choose whether to do this stuff or can magically resume in their separate ways if the exception is non-fatal. This is probably an easy task, as find_exception_handler is only called from those two places. The next weird thing is where we set up the handler to be called by using VTABLE_invoke. VTABLE_invoke returns an address to jump to that will run the given function, tha's fine, but we pass an argument, dest, which is the return continuation. Look at src/pmc/exceptionhandler.pmc's 'VTABLE opcode_t *invoke' though! It accept the 'next' parameter, but it doesn't do anything with it! That's kinda weird. It's completely unused there. Should it be? Do we need it for anything there? Again, dunno. Then we only initialize the arguments to the handler if handler->current_results. I'm not sure what current_results is. Ack tells me that it looks like a property of continuations. This needs more investigation. pass_exception_args is pretty normal, though, it just redispatches to parrot_pass_args_fromc with VARARGS magic. Then we say that if it's a C exception handler we jump directly to the handler. Otherwise, we return the address and 'op throw' goto()s it. I don't know how C handlers work yet and how they'll interact with resuming. Pretty understandable so far, yes? No? Maybe? Man, this habit of ask for confirmation of understanding is REALLY hard to avoid. I ask that about every five minutes while teaching. It's kind of annoying when I notice myself doing it in other places. ANYWAY, moving on. There's one more important part. Parrot_cx_find_handler_local. This is in src/scheduler.c:722 Parrot_cx_find_handler_local is the function Allison and I are talking about on the ML. The logic is like this: Accept a task(exception,event,anything). Check if it's an Exception and if it has already been handled once (rethrow). If so, we have an iterator of handlers that we already built once. Otherwise, get a handler iterator from CONTEXT(interp)->handlers. Kick the iterator until we get a non-null handler. Call handler->can_handle(task) # discussed shortly If that returns true, mark the handler as already used (if the task is an exception) and return the handler. If not found, look harder, then give up and return null. An issue here is handler->can_handle(). All this does is return true if the task isa Exception and the handler hasn't already been used once. This "mark as used" is to prevent to rethrow loop, but it gets in the way badly when you try to use a handler multiple times legitimately, after having resumed at least once. One option would be find a way to have the handler explicitly mark itself unused after handling but before continuing. This is ugly, and might not be very feasible. Probably not a good choice to follow up on. You'll notice I didn't talk about Parrot_ex_throw_from_c yet. I'm not very comfortable with it. Check out the POD: =item Exceptions thrown from C and caught by a continuation-based handler are resumable at the level of a C instruction. When handled, they return the exception object. Any values returned from the handler to the C code that threw the exception can be stored in the exception's payload. =cut This is all well and good in theory, and it SEEMS to be possible, but it's kind of sketchy for a couple of reasons. One is the find_exception_handler refactoring, which is easy enough to deal with. One other issue is that it's marked PARROT_DOES_NOT_RETURN, and in several places it looks like it's used with the assumption that it can never return. I'm having trouble finding a good test case. we probably just need a place where it's used to throw a warning or something, maybe? The biggest issue is that since Parrot_ex_throw_from_c can call a PIR exception handler, do we then want to try to support resuming from PIR to C-level instructions? We need a continuation object that when invoked returns Parrot_ex_throw_from_c. Can we do that? Anyway, harass me about questions now, or something. Future me will be wanting feedback on this. Say hi to him for me, btw. KTHXBAI!