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))