From: Allison Randal <[EMAIL PROTECTED]>
   Date: Wed, 05 Jul 2006 00:02:53 -0700

   Bob Rogers wrote:
   >    If, as seems likely, exception bookkeeping is moved to a separate
   > stack in the interpreter (with or without dynamic-wind actions), then
   > C<bsr>/C<ret> addresses can stay in the Parrot_Context, and all of
   > pdd23_exceptions.pod that is quoted below ceases to be problematic.
   > Does that seem reasonable?

   Yeah, that's the current best solution (the last time Chip and I talked 
   on Saturday). I'd like avoid a stack entirely, but this is a 
   straightforward first attack on the problem. If we implement a separate 
   exception stack now and run into problems, we can refine and refactor later.

Were you thinking of using the same stack for dynamic binding context
and stack-winding actions as well?  Or would these go on separate
stacks?

   In what follows, I will speak of these collectively as the "dynamic
state stack", with the understanding that it may need to be pluralized.
(And the further understanding that continuations and coroutines can
make it branch, just like the Parrot_Context => RetContinuation "stack".
So maybe we need a better word?)

   It's an encouraging sign that we've got hold of a good idea when the 
   same solution occurs to others independently. :)

   Thanks,
   Allison

Well, yesterday I thought of a problem with this.  So maybe that just
means that we're all sharing the same delusion.  ;-}

   The snag is that calling a continuation would return to the
Parrot_Context with the return stack state as it was when the context
was last left, which could be different than when the continuation was
taken.  What I would expect to happen is that the continuation also
captures the return stack state, as part of the dynamic context, so I
would argue that this is a bug.

   This is currently not a problem with respect to error handling, since
the stack-unwinding done by C<throw> implicitly pops return addresses as
well, because it's the same stack.  However, it does fail now with other
uses of continuations, as shown by the first new test case in the
attached patch.  One could argue that maybe it is not worth fixing the
general case.  However, separating the stacks as proposed would extend
this bug to cover the C<throw> case as well, and it is probably not
acceptable to say that you can't depend on the return stack after error
recovery.  True?

   Continuations also don't run actions (as shown by the second new test
case), and they don't preserve error handlers for the same reason --
these bugs are much more serious.  Furthermore, I assume dynamic binding
would be similarly affected.  (In fact, I've been itching to fix all of
this for almost six months now, but I think it needs to be done as part
of a general cleanup of dynamic state bookkeeping -- I now have more
tuits, but I don't yet understand the changes you are proposing well
enough to get started.)

   The obvious solution is to save the current return stack TOS along
with that of the dynamic state stack(s) in the continuation, so that it
can be restored when the continuation is invoked.  The drawback is that
doing so complicates the storage management of return stack entries.
This is the same complication that must be borne by dynamic state stack
entries, but it seems a shame to have to extend it to something that
otherwise would obviously belong strictly to the Parrot_Context in which
it was pushed.

   So maybe there needs to be One Dynamic Stack (or whatever we call
it), so that we can unify the bookkeeping?  This would also require that
all of these things nest properly, but that's no change from the current
implementation -- and nobody seems to have complained.

                                        -- Bob

Index: t/pmc/continuation.t
===================================================================
--- t/pmc/continuation.t	(revision 13078)
+++ t/pmc/continuation.t	(working copy)
@@ -33,6 +33,114 @@
 ok 1
 OUT
 
+$TODO = "BUG: continuations don't preserve the control_stack.";
 
+pir_output_is(<<'CODE', <<'OUT', 'continuations preserve bsr/ret state.');
+## Here is a trace of execution, keyed by labels.
+##   L1:  bsr to rtn1
+## rtn1:  create a continuation that directs us to L6, and (we expect) captures
+##        captures the whole dynamic state, including the return address to L3.
+##   L3:  return back to main
+##   L4:  if we're here the first time, call rtn2
+## rtn2:  call the continuation from that routine.
+##   L6:  print "Continuation called." and return, which should take us . . .
+##   L3:  here the second time, where we print "done." and exit.
+.sub test_control_cont :main
+L1:
+	.local int return_count
+	.local pmc cont
+	return_count = 0
+	bsr rtn1
+L3:
+	unless return_count goto L4
+	print "done.\n"
+	end
+L4:
+	inc return_count
+	bsr rtn2
+	print "Oops; shouldn't have returned from rtn2.\n"
+	end
+L6:
+	print "Continuation called.\n"
+	ret
+rtn1:
+	print "Taking continuation.\n"
+	cont = new .Continuation
+	set_addr cont, L6
+	ret
+rtn2:
+	print "Calling continuation.\n"
+	cont()
+	ret
+.end
+CODE
+Taking continuation.
+Calling continuation.
+Continuation called.
+done.
+OUT
+
+pir_output_is(<<'CODE', <<'OUT', 'continuations call actions.');
+## the test_cont_action sub creates a continuation and passes it to _test_1
+## twice:  the first time returns normally, the second time returns via the
+## continuation.
+.sub test_cont_action :main
+	## debug 0x80
+	.local pmc cont
+	cont = new .Continuation
+	set_addr cont, continued
+	_test_1(4, cont)
+	_test_1("bar", cont)
+	print "oops; no "
+continued:
+	print "continuation called.\n"
+.end
+
+## set up C<pushaction> cleanup, and pass our arguments to _test_2.
+.sub _test_1
+	.param pmc arg1
+	.param pmc cont
+	print "_test_1\n"
+	.const .Sub $P43 = "___internal_test_1_0_"
+	pushaction $P43
+	$P50 = _test_2(arg1, cont)
+	print "got "
+	print $P50
+	print "\n"
+	.return ($P50)
+.end
+
+## cleanup sub used by _test_1, which just shows whether or not the action was
+## called at the right time.
+.sub ___internal_test_1_0_
+	.local pmc arg1
+	print "unwinding\n"
+	.return ()
+.end
+
+## return 3*n if n is an integer, else invoke the continuation.
+.sub _test_2
+	.param pmc n
+	.param pmc cont
+	typeof $I40, n
+	if $I40 != .Integer goto L3
+	$P44 = n_mul n, 3
+	.return ($P44)
+L3:
+	cont()
+.end
+CODE
+_test_1
+got 12
+unwinding
+_test_1
+unwinding
+continuation called.
+OUT
+
+
+$TODO = '';
+
+
 # remember to change the number of tests :-)
-BEGIN { plan tests => 1; }
+BEGIN { plan tests => 3; }

Reply via email to