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 . . . The attached patch detects cases where a continuation tries to enter a runloop different from the one that is executing, and prints a warning to stderr. It shows that there is only one such case in the present test suite, where t/src/extend.t:13 creates an exception handler in C, and hence outside of any run loop. But this particular idiom is not problematic, so I tweaked this case to suppress the message. So the purpose of this patch is to give a heads-up to anyone encountering this problem in the future; they should notice the message right before their code starts behaving bizarrely. Does anyone see a reason why I should not commit this? -- Bob Rogers http://rgrjr.dyndns.org/
Diffs between last version checked in and current workfile(s): Index: include/parrot/sub.h =================================================================== --- include/parrot/sub.h (revision 13642) +++ include/parrot/sub.h (working copy) @@ -118,6 +118,7 @@ struct Parrot_Context *from_ctx; /* sub, this cont is returning from */ opcode_t *current_results; /* ptr into code with get_results opcode full continuation only */ + int runloop_id; /* id of the creating runloop. */ } * parrot_cont_t; #define PMC_cont(pmc) ((parrot_cont_t)PMC_struct_val(pmc)) Index: include/parrot/interpreter.h =================================================================== --- include/parrot/interpreter.h (revision 13642) +++ include/parrot/interpreter.h (working copy) @@ -195,8 +195,7 @@ * have been activated */ UINTVAL errors; /* fatals that can be turned off */ UINTVAL trace_flags; - UINTVAL recursion_depth; /* Sub call resursion depth */ - int runloop_level; /* for reentering run loop */ + UINTVAL recursion_depth; /* Sub call recursion depth */ /* * new call scheme and introspective variables */ @@ -361,6 +360,9 @@ struct parrot_exception_t *exc_free_list; /* and free list */ PMC ** exception_list; /* precreated exception objects */ + int current_runloop_level; /* for reentering run loop */ + int current_runloop_id; + struct _Thread_data *thread_data; /* thread specific items */ UINTVAL recursion_limit; /* Sub call resursion limit */ Index: include/parrot/exceptions.h =================================================================== --- include/parrot/exceptions.h (revision 13642) +++ include/parrot/exceptions.h (working copy) @@ -157,7 +157,6 @@ long error; /* exception_type_enum */ STRING *msg; /* may be NULL */ void *resume; /* opcode_t* for resume or NULL */ - int runloop_level; /* for reentering run loop */ struct parrot_exception_t *prev; /* interpreters handler stack */ long language; /* what is this? */ long system; /* what is this? */ Index: src/register.c =================================================================== --- src/register.c (revision 13642) +++ src/register.c (working copy) @@ -243,7 +243,6 @@ ctx->warns = old->warns; ctx->errors = old->errors; ctx->trace_flags = old->trace_flags; - ctx->runloop_level = old->runloop_level; ctx->pred_offset = old->pred_offset; ctx->current_HLL = old->current_HLL; ctx->current_namespace = old->current_namespace; Index: src/inter_run.c =================================================================== --- src/inter_run.c (revision 13642) +++ src/inter_run.c (working copy) @@ -34,13 +34,26 @@ */ #define STACKED_EXCEPTIONS 1 +/* #define RUNLOOP_TRACE 1 */ +static int +runloop_id_counter = 0; /* for synthesizing runloop ids. */ + void runops(Interp *interpreter, size_t offs) { volatile size_t offset = offs; + int old_runloop_id = interpreter->current_runloop_id; + int our_runloop_level = ++interpreter->current_runloop_level; + int our_runloop_id = ++runloop_id_counter; - CONTEXT(interpreter->ctx)->runloop_level++; + /* It is OK if the runloop ID overflows; we only ever test it for equality, + so the chance of collision is slight. */ + interpreter->current_runloop_id = our_runloop_id; +#ifdef RUNLOOP_TRACE + fprintf(stderr, "[entering loop %d, level %d]\n", + interpreter->current_runloop_id, our_runloop_level); +#endif /* * STACKED_EXCEPTIONS are necessary to catch exceptions in reentered * run loops, e.g. if a delegate methods throws an exception @@ -50,10 +63,14 @@ #endif { new_internal_exception(interpreter); - interpreter->exceptions->runloop_level = - CONTEXT(interpreter->ctx)->runloop_level; if (setjmp(interpreter->exceptions->destination)) { /* an exception was thrown */ + interpreter->current_runloop_level = our_runloop_level; + interpreter->current_runloop_id = our_runloop_id; +#ifdef RUNLOOP_TRACE + fprintf(stderr, "[exception; back to loop %d, level %d]\n", + our_runloop_id, our_runloop_level); +#endif offset = handle_exception(interpreter); /* update profile for exception execution time */ if (interpreter->profile && @@ -67,27 +84,21 @@ } } + runops_int(interpreter, offset); + /* - * XXX this is broken - * - the runloop_level has to be in the interpreter struct - * - the exception loop level must be part of the exception - * handler - */ - if (1 || interpreter->exceptions->runloop_level == - CONTEXT(interpreter->ctx)->runloop_level) { - /* if we are coming from an exception and it was thrown deeper - * in a nested run loop, we just leave this loop - */ - runops_int(interpreter, offset); - } - /* * pop off exception and put it onto the free list * s. above */ if (STACKED_EXCEPTIONS) { free_internal_exception(interpreter); } - CONTEXT(interpreter->ctx)->runloop_level--; +#ifdef RUNLOOP_TRACE + fprintf(stderr, "[exiting loop %d, level %d]\n", + our_runloop_id, our_runloop_level); +#endif + interpreter->current_runloop_level = --our_runloop_level; + interpreter->current_runloop_id = old_runloop_id; /* * not yet - this needs classifying of exceptions and handlers * so that only an exit handler does catch this exception Index: src/sub.c =================================================================== --- src/sub.c (revision 13642) +++ src/sub.c (working copy) @@ -141,6 +141,7 @@ cc->to_ctx = to_ctx; cc->from_ctx = CONTEXT(interp->ctx); + cc->runloop_id = 0; CONTEXT(interp->ctx)->ref_count++; if (to) { cc->seg = to->seg; @@ -171,6 +172,7 @@ struct Parrot_cont * const cc = mem_sys_allocate(sizeof(struct Parrot_cont)); cc->to_ctx = CONTEXT(interp->ctx); cc->from_ctx = NULL; /* filled in during a call */ + cc->runloop_id = 0; cc->seg = interp->code; cc->current_results = NULL; cc->address = NULL; Index: src/pmc/continuation.pmc =================================================================== --- src/pmc/continuation.pmc (revision 13642) +++ src/pmc/continuation.pmc (working copy) @@ -132,6 +132,7 @@ PObj_custom_mark_destroy_SETALL(ret); cc = new_continuation(INTERP, cc_self); + cc->runloop_id = cc_self->runloop_id; PMC_struct_val(ret) = cc; PMC_pmc_val(ret) = PMC_pmc_val(SELF); return ret; @@ -169,6 +170,7 @@ PObj_get_FLAGS(SELF) |= PObj_private1_FLAG; cc->address = value; + cc->runloop_id = interpreter->current_runloop_id; if (pos && *pos == PARROT_OP_get_results_pc) { cc->current_results = pos; } @@ -226,6 +228,16 @@ parrot_context_t *ctx; opcode_t *pc; + if (interpreter->current_runloop_id != cc->runloop_id + /* it's ok if we are exiting to "runloop 0"; there is no such + runloop, but the only continuation that thinks it came from + runloop 0 is for the return from the initial sub call. */ + && cc->runloop_id != 0) { + fprintf(stderr, "[oops; continuation %p of type %d " + "is trying to jump from runloop %d to runloop %d]\n", + SELF, SELF->vtable->base_type, + interpreter->current_runloop_id, cc->runloop_id); + } #if CTX_LEAK_DEBUG if (Interp_debug_TEST(interpreter, PARROT_CTX_DESTROY_DEBUG_FLAG)) { fprintf(stderr, Index: src/inter_create.c =================================================================== --- src/inter_create.c (revision 13642) +++ src/inter_create.c (working copy) @@ -213,6 +213,8 @@ SET_NULL_P(interpreter->DOD_registry, PMC *); /* create exceptions list */ + interpreter->current_runloop_level = 0; + interpreter->current_runloop_id = 0; Parrot_init_exceptions(interpreter); /* register assembler/compilers */ Index: t/pmc/exception.t =================================================================== --- t/pmc/exception.t (revision 13642) +++ t/pmc/exception.t (working copy) @@ -6,7 +6,7 @@ use warnings; use lib qw( . lib ../lib ../../lib ); use Test::More; -use Parrot::Test tests => 29; +use Parrot::Test tests => 30; =head1 NAME @@ -653,3 +653,36 @@ done. OUTPUT +local $TODO = 'runloop shenanigans'; +# stringification is handled by a vtable method, which runs in a second +# runloop. when an error in the method tries to go to a Error_Handler defined +# outside it, it winds up going to the inner runloop, giving strange results. +pir_output_is(<<'CODE', <<'OUTPUT', 'clear_eh out of context (2)'); +.sub main :main + $P0 = get_hll_global ['Foo'], 'load' + $P0() + $P0 = new 'Foo' + push_eh catch + $S0 = $P0 + clear_eh + say "huh?" + .return() + +catch: + say "caught" + .return() +.end + +.namespace ['Foo'] + +.sub load + $P0 = newclass 'Foo' +.end + +.sub __get_string :method + $P0 = new .Exception + throw $P0 +.end +CODE +caught +OUTPUT Index: t/src/extend.t =================================================================== --- t/src/extend.t (revision 13642) +++ t/src/extend.t (working copy) @@ -510,11 +510,13 @@ sub = Parrot_find_global_cur(interpreter, name); if (setjmp(jb.destination)) { - PIO_eprintf(interpreter, "caught\n"); + PIO_eprintf(interpreter, "caught\n"); } else { - push_new_c_exception_handler(interpreter, &jb); - Parrot_call_sub(interpreter, sub, "v"); + interpreter->current_runloop_id++; /* pretend the EH was pushed + by the sub call. */ + push_new_c_exception_handler(interpreter, &jb); + Parrot_call_sub(interpreter, sub, "v"); } PIO_eprintf(interpreter, "back\n"); End of diffs.