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.

Reply via email to