Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r47204:db9617836395 Date: 2011-09-11 15:51 +0200 http://bitbucket.org/pypy/pypy/changeset/db9617836395/
Log: Proper handling of 'f_back' in continulets. diff --git a/pypy/module/_continuation/interp_continuation.py b/pypy/module/_continuation/interp_continuation.py --- a/pypy/module/_continuation/interp_continuation.py +++ b/pypy/module/_continuation/interp_continuation.py @@ -5,6 +5,7 @@ from pypy.interpreter.baseobjspace import Wrappable from pypy.interpreter.typedef import TypeDef from pypy.interpreter.gateway import interp2app +from pypy.interpreter.pycode import PyCode class W_Continulet(Wrappable): @@ -30,6 +31,7 @@ start_state.origin = self start_state.w_callable = w_callable start_state.args = __args__ + self.bottomframe = make_fresh_frame(self.space) self.sthread = build_sthread(self.space) try: self.h = self.sthread.new(new_stacklet_callback) @@ -52,7 +54,6 @@ start_state.clear() raise geterror(self.space, "continulet not initialized yet") ec = self.check_sthread() - saved_topframeref = ec.topframeref # start_state.origin = self if to is None: @@ -74,8 +75,6 @@ start_state.clear() raise getmemoryerror(self.space) # - ec = sthread.ec - ec.topframeref = saved_topframeref return get_result() def descr_switch(self, w_value=None, w_to=None): @@ -123,13 +122,21 @@ # ____________________________________________________________ +# Continulet objects maintain a dummy frame object in order to ensure +# that the 'f_back' chain is consistent. We hide this dummy frame +# object by giving it a dummy code object with hidden_applevel=True. class State: def __init__(self, space): + from pypy.interpreter.astcompiler.consts import CO_OPTIMIZED self.space = space w_module = space.getbuiltinmodule('_continuation') self.w_error = space.getattr(w_module, space.wrap('error')) self.w_memoryerror = OperationError(space.w_MemoryError, space.w_None) + self.dummy_pycode = PyCode(space, 0, 0, 0, CO_OPTIMIZED, + '', [], [], [], '', + '<bottom of continulet>', 0, '', [], [], + hidden_applevel=True) def geterror(space, message): cs = space.fromcache(State) @@ -139,6 +146,10 @@ cs = space.fromcache(State) return cs.w_memoryerror +def make_fresh_frame(space): + cs = space.fromcache(State) + return space.FrameClass(space, cs.dummy_pycode, None, None) + # ____________________________________________________________ @@ -178,9 +189,8 @@ # space = self.space try: - ec = self.sthread.ec - ec.topframeref = jit.vref_None - + assert self.sthread.ec.topframeref() is None + self.sthread.ec.topframeref = jit.non_virtual_ref(self.bottomframe) if start_state.propagate_exception is not None: raise start_state.propagate_exception # just propagate it further if start_state.w_value is not space.w_None: @@ -193,6 +203,7 @@ start_state.propagate_exception = e else: start_state.w_value = w_result + self.sthread.ec.topframeref = jit.vref_None start_state.origin = self start_state.destination = self return self.h @@ -205,6 +216,11 @@ start_state.origin = None start_state.destination = None self.h, origin.h = origin.h, h + # + current = sthread.ec.topframeref + sthread.ec.topframeref = self.bottomframe.f_backref + self.bottomframe.f_backref = origin.bottomframe.f_backref + origin.bottomframe.f_backref = current def get_result(): if start_state.propagate_exception: @@ -240,6 +256,9 @@ contlist.append(cont) # if len(contlist) > 1: - other = contlist[-1].h + otherh = contlist[-1].h + otherb = contlist[-1].bottomframe.f_backref for cont in contlist: - other, cont.h = cont.h, other + otherh, cont.h = cont.h, otherh + b = cont.bottomframe + otherb, b.f_backref = b.f_backref, otherb diff --git a/pypy/module/_continuation/test/test_stacklet.py b/pypy/module/_continuation/test/test_stacklet.py --- a/pypy/module/_continuation/test/test_stacklet.py +++ b/pypy/module/_continuation/test/test_stacklet.py @@ -312,7 +312,7 @@ res = f() assert res == 2002 - def test_f_back_is_None_for_now(self): + def test_f_back(self): import sys from _continuation import continulet # @@ -321,6 +321,7 @@ c.switch(sys._getframe(0).f_back) c.switch(sys._getframe(1)) c.switch(sys._getframe(1).f_back) + assert sys._getframe(2) is f3.f_back c.switch(sys._getframe(2)) def f(c): g(c) @@ -331,10 +332,21 @@ f2 = c.switch() assert f2.f_code.co_name == 'f' f3 = c.switch() - assert f3.f_code.co_name == 'f' - f4 = c.switch() - assert f4 is None - raises(ValueError, c.switch) # "call stack is not deep enough" + assert f3 is f2 + assert f1.f_back is f3 + def main(): + f4 = c.switch() + assert f4.f_code.co_name == 'main', repr(f4.f_code.co_name) + assert f3.f_back is f1 # not running, so a loop + def main2(): + f5 = c.switch() + assert f5.f_code.co_name == 'main2', repr(f5.f_code.co_name) + assert f3.f_back is f1 # not running, so a loop + main() + main2() + res = c.switch() + assert res is None + assert f3.f_back is None def test_traceback_is_complete(self): import sys @@ -609,6 +621,7 @@ assert res == "ok" def test_permute(self): + import sys from _continuation import continulet, permute # def f1(c1): @@ -617,14 +630,18 @@ return "done" # def f2(c2): + assert sys._getframe(1).f_code.co_name == 'main' permute(c1, c2) + assert sys._getframe(1).f_code.co_name == 'f1' return "ok" # c1 = continulet(f1) c2 = continulet(f2) - c1.switch() - res = c2.switch() - assert res == "done" + def main(): + c1.switch() + res = c2.switch() + assert res == "done" + main() def test_various_depths(self): skip("may fail on top of CPython") _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit