From: Leopold Toetsch <[EMAIL PROTECTED]> Date: Thu, 27 Jul 2006 20:50:18 +0200
Am Donnerstag, 27. Juli 2006 19:44 schrieb Matt Diephouse: > Running this gives: > > caught > No exception to pop. PIR code running on behalf of a vtable (or MMD) function is implemented by entering a secondary runloop (see src/pmc/delegate.pmc). The C code and the extra runloop is acting as a Continuation barrier . . . Ouch. I've experimented some time ago to get at least exceptions working by rewinding runloops also, but have failed so far. IIUC, this is not even theoretically possible, at least not without putting heavy constraints on the code that a secondary runloop can run [2]. Last night I thought about capturing the identity of the current runloop in each closure, e.g. by adding a C<jmp_buf> to C<struct Parrot_cont>, so that invoking the closure would return to the runloop in which it was created. Then it occurred to me that, once the secondary runloop exited, invoking the continuation would make Parrot jump off into never-never land. Of course, this also affects calling actions (or dynamic-wind thunks, or whatever we are calling them this week). As a result, my "Partial fix to make closures invoke actions" patch of Wednesday is clearly not the right thing; please consider it withdrawn. There's no way to get full Continuations working around such C code barriers, except by *not* entering secondary runloops at all for these cases[1]. This could be achieved by (optionally) returning a new PC for all vtable/MMD functions that is, by changing the internal (C) calling conventions of all the PMC code. leo [1] we can't avoid that for e.g. custom sort functions, but these are special enough that we could restrict these. I see a solution for simpler cases, that might even work for custom sort functions, though it's certainly not painless. Here's what I would like to do for calling actions: In order to call back into bytecode, the C code must set up a call using the original runloop that invokes more C code on exit in order to resume the stack rewinding. This can be done with a C function hook in the continuation. Stack rewinding would therefore need to be split up into a number of thunks that do the rewinding magic, running between episodes of bytecode: T1. The entrypoint (which could be Continuation:invoke) scans down the dynamic environment looking for an action to run. If it finds one, it sets it up to run, calling T2 when it exits. If not, call T3 directly. T2. After running an action, we need to look for the next action. If we find it, set it up to run and call T2 again on exit. Otherwise, call T3 directly. T3. We have reached the bottom, and can start scanning up, if necessary, using the same logic. (Since at present we don't call actions on the way up, this is a noop for now.) T4. Having finally gotten to the destination environment, resume execution from the bytecode pointed to by the sub. Keeping track of state between thunk calls would be pretty messy. Unless the state is garbage-collected, it'd be hard not to leak memory when the rewinding is aborted because the bytecode happens to call some other continuation. Does this sound sane? Beyond that, I have no clue how to rescue the delegate, ParrotClass, and ParrotObject pmclasses. In principal, this strategy could be applied to the general case. But I find it really hard to imagine rewriting *every* call to a vtable method to allow for the possibility that it might call into bytecode . . . -- Bob Rogers http://rgrjr.dyndns.org/ [2] It might be possible for Exception_Handler, being a restricted sort of continuation, but I assume that is no longer interesting, as the new PDD23 design doesn't use them.