Author: Armin Rigo <[email protected]>
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
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit