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

Reply via email to