The tailcall op forces the old context to be recycled, which causes
mighty peculiar things to happen if the sub had created any closures,
since those closures still refer to the just-freed context.  Sometimes
(but not always) the context is reused for the next call; when this
happens, it seems to lead to a situation where the context's outer_ctx
field points back to itself.  This causes Parrot_find_pad to loop
without bound.

   The attached PIR code illustrates the problem, if anyone cares to
check it out, but it may not work in other builds.  Sometimes, small
changes to distant parts of this code prevent the context from being
reused immediately, forestalling the bug.  Commenting out the assignment
to $P55 is one such change.  But if you do try it, you may also want to
apply the src/sub.c part of the attached patch, since this prevents
unbounded looping.  Enabling the ignored code also makes it clear what
is happening.

   The src/sub.c fix is intended just for debugging, though.  The
patch's minimal change [1] to src/ops/core.ops actually fixes the
problem, at the risk of possibly leaking memory, since I still don't
fully understand the life cycle of contexts.  However, I would argue
that any such leaks are independent problems that also need fixing.  If
there are no objections to this part of the patch, I will commit it
tomorrow.

   I didn't turn the test case into a proper regression test because I
didn't think it would be reliable enough.  Maybe a check that is
intermittent is better than none at all?  On the other hand, changes to
the context allocator could render it permanently useless . . .

                                        -- Bob Rogers
                                           http://rgrjr.dyndns.org/

[1]  Minimal even according to Shannon:  It changes only a single bit in
     the codebase.  All that work for one lousy bit . . .

## Compiled by:  $Id: mini-compiler.lisp,v 1.80 2006/02/26 23:02:55 rogers Exp $

.sub _main :main
        debug 0x80
        .lex "MAIN-CONT", $P41
        $I42 = 10
        $P41 = new .Continuation
        set_addr $P41, L2
        goto L3
L2:
        get_results '(0)', $P45
        .return ($P45 :flat)
L3:
        .const .Sub $P49 = "___internal_main_test_"
        newclosure $P48, $P49
        .return _try_it($I42, $P48)
.end

.sub ___internal_main_test_ :outer('_main')
        .param pmc arg1
        print "[in test]\n"
        find_lex $P41, "MAIN-CONT"
        $P55 = new "Undef"
        if arg1 != 3 goto L3
        $P58 = arg1
        $P59 = arg1
        $P57 = n_mul $P58, $P59
        set_args '(0)', $P57
        tailcall $P41
L3:
        print "not "
        print arg1
        print "\n"
.end


.sub _try_it
        .param int n
        .param pmc closure
        $P42 = new "Undef"
        $P42 = 0
        goto L4
L2:
        closure($P42)
        $P42 = $P42 + 1
L4:
        if $P42 < n goto L2
.end
Index: src/ops/core.ops
===================================================================
--- src/ops/core.ops    (revision 11783)
+++ src/ops/core.ops    (working copy)
@@ -528,7 +528,7 @@
        PObj_get_FLAGS(ccont) &= ~SUB_FLAG_TAILCALL;
        --ctx->recursion_depth;
        ctx->caller_ctx = caller_ctx->caller_ctx;
-       Parrot_free_context(interpreter, caller_ctx, 1);
+       Parrot_free_context(interpreter, caller_ctx, 0);
         interpreter->current_args = NULL;
     }
     goto ADDRESS(pc);
Index: src/sub.c
===================================================================
--- src/sub.c   (revision 11783)
+++ src/sub.c   (working copy)
@@ -405,7 +405,12 @@
     while (1) {
         lex_pad = ctx->lex_pad;
         outer = ctx->outer_ctx;
-        if (!outer)
+#ifdef IGNORE
+        fprintf(stderr, "[Parrot_find_pad: looking in "
+                        "ctx %p pad %p for %p, outer %p]\n",
+                ctx, lex_pad, lex_name, outer);
+#endif
+        if (!outer || outer == ctx)
             return lex_pad;
         if (!PMC_IS_NULL(lex_pad)) {
             if (VTABLE_exists_keyed_str(interpreter, lex_pad, lex_name))

Reply via email to