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.