[pypy-svn] r27661 - in pypy/dist/pypy: interpreter module/stackless module/stackless/test translator/c/test
arigo at codespeak.net
arigo at codespeak.net
Wed May 24 21:24:04 CEST 2006
Author: arigo
Date: Wed May 24 21:24:01 2006
New Revision: 27661
Added:
pypy/dist/pypy/module/stackless/interp_clonable.py (contents, props changed)
pypy/dist/pypy/module/stackless/test/test_interp_clonable.py (contents, props changed)
Modified:
pypy/dist/pypy/interpreter/executioncontext.py
pypy/dist/pypy/module/stackless/coroutine.py
pypy/dist/pypy/module/stackless/interp_coroutine.py
pypy/dist/pypy/module/stackless/test/test_interp_coroutine.py
pypy/dist/pypy/translator/c/test/test_stackless.py
Log:
Clonable coroutines. The first test passes, but fork() is still buggy.
Modified: pypy/dist/pypy/interpreter/executioncontext.py
==============================================================================
--- pypy/dist/pypy/interpreter/executioncontext.py (original)
+++ pypy/dist/pypy/interpreter/executioncontext.py Wed May 24 21:24:01 2006
@@ -45,17 +45,18 @@
coobj.is_tracing = 0
subcontext_new = staticmethod(subcontext_new)
- def subcontext_switch(self, current, next):
- current.framestack = self.framestack
- current.w_tracefunc = self.w_tracefunc
- current.w_profilefunc = self.w_profilefunc
- current.is_tracing = self.is_tracing
-
+ def subcontext_enter(self, next):
self.framestack = next.framestack
self.w_tracefunc = next.w_tracefunc
self.w_profilefunc = next.w_profilefunc
self.is_tracing = next.is_tracing
+ def subcontext_leave(self, current):
+ current.framestack = self.framestack
+ current.w_tracefunc = self.w_tracefunc
+ current.w_profilefunc = self.w_profilefunc
+ current.is_tracing = self.is_tracing
+
# coroutine: I think this is all, folks!
def get_builtin(self):
Modified: pypy/dist/pypy/module/stackless/coroutine.py
==============================================================================
--- pypy/dist/pypy/module/stackless/coroutine.py (original)
+++ pypy/dist/pypy/module/stackless/coroutine.py Wed May 24 21:24:01 2006
@@ -76,14 +76,19 @@
if self.frame is None:
raise OperationError(space.w_ValueError, space.wrap(
"cannot switch to an unbound Coroutine"))
- state = self.costate
- ec = space.getexecutioncontext()
- ec.subcontext_switch(state.current, self)
self.switch()
- ec.subcontext_switch(state.last, state.current)
+ state = self.costate
w_ret, state.w_tempval = state.w_tempval, space.w_None
return w_ret
+ def hello(self):
+ ec = self.space.getexecutioncontext()
+ ec.subcontext_enter(self)
+
+ def goodbye(self):
+ ec = self.space.getexecutioncontext()
+ ec.subcontext_leave(self)
+
def w_kill(self):
self.kill()
Added: pypy/dist/pypy/module/stackless/interp_clonable.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/module/stackless/interp_clonable.py Wed May 24 21:24:01 2006
@@ -0,0 +1,53 @@
+from pypy.module.stackless.interp_coroutine import Coroutine, AbstractThunk
+from pypy.rpython.rgc import gc_swap_pool, gc_clone
+
+
+class ClonableCoroutine(Coroutine):
+ local_pool = None
+
+ def hello(self):
+ self.saved_pool = gc_swap_pool(self.local_pool)
+
+ def goodbye(self):
+ self.local_pool = gc_swap_pool(self.saved_pool)
+
+ def clone(self):
+ if self.getcurrent() is self:
+ raise RuntimeError("clone() cannot clone the current coroutine; "
+ "use fork() instead")
+ if self.local_pool is None: # force it now
+ self.local_pool = gc_swap_pool(gc_swap_pool(None))
+ # cannot gc_clone() directly self, because it is not in its own
+ # local_pool. Moreover, it has a __del__, which cloning doesn't
+ # support properly at the moment.
+ copy = ClonableCoroutine(self.costate)
+ copy.parent = self.parent
+ copy.frame, copy.local_pool = gc_clone(self.frame, self.local_pool)
+ return copy
+
+
+class ForkThunk(AbstractThunk):
+ def __init__(self, coroutine):
+ self.coroutine = coroutine
+ self.newcoroutine = None
+ def call(self):
+ oldcoro = self.coroutine
+ self.coroutine = None
+ self.newcoroutine = oldcoro.clone()
+
+def fork():
+ """Fork, as in the Unix fork(): the call returns twice, and the return
+ value of the call is either the new 'child' coroutine object (if returning
+ into the parent), or None (if returning into the child). This returns
+ into the parent first, which can switch to the child later.
+ """
+ current = ClonableCoroutine.getcurrent()
+ if not isinstance(current, ClonableCoroutine):
+ raise RuntimeError("fork() in a non-clonable coroutine")
+ thunk = ForkThunk(current)
+ coro_fork = ClonableCoroutine()
+ coro_fork.bind(thunk)
+ coro_fork.switch()
+ # we resume here twice. The following would need explanations about
+ # why it returns the correct thing in both the parent and the child...
+ return thunk.newcoroutine
Modified: pypy/dist/pypy/module/stackless/interp_coroutine.py
==============================================================================
--- pypy/dist/pypy/module/stackless/interp_coroutine.py (original)
+++ pypy/dist/pypy/module/stackless/interp_coroutine.py Wed May 24 21:24:01 2006
@@ -39,8 +39,13 @@
self.current = self.main = self.last = None
def update(self, new):
- self.last, self.current = self.current, new
+ old = self.current
+ if old is not None:
+ old.goodbye()
+ self.last, self.current = old, new
frame, new.frame = new.frame, None
+ if new is not None:
+ new.hello()
return frame
class CoState(BaseCoState):
@@ -186,6 +191,12 @@
return costate.current
getcurrent = staticmethod(getcurrent)
+ def hello(self):
+ "Called when execution is transferred into this coroutine."
+
+ def goodbye(self):
+ "Called just before execution is transferred away from this coroutine."
+
costate = None
costate = CoState()
Added: pypy/dist/pypy/module/stackless/test/test_interp_clonable.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/module/stackless/test/test_interp_clonable.py Wed May 24 21:24:01 2006
@@ -0,0 +1,83 @@
+"""
+testing cloning
+"""
+
+from pypy.translator.c import gc
+from pypy.rpython.memory import gctransform
+from pypy.rpython.memory.test import test_transformed_gc
+from pypy.module.stackless.interp_coroutine import costate
+from pypy.module.stackless.interp_clonable import ClonableCoroutine
+from pypy.module.stackless.interp_clonable import AbstractThunk, fork
+
+
+class TestClonableCoroutine(test_transformed_gc.GCTest):
+
+ class gcpolicy(gc.StacklessFrameworkGcPolicy):
+ class transformerclass(gctransform.StacklessFrameworkGCTransformer):
+ GC_PARAMS = {'start_heap_size': 4096 }
+
+ def test_clone(self):
+ class T(AbstractThunk):
+ def __init__(self, result):
+ self.result = result
+ def call(self):
+ self.result.append(2)
+ costate.main.switch()
+ self.result.append(4)
+ def f():
+ result = []
+ coro = ClonableCoroutine()
+ coro.bind(T(result))
+ result.append(1)
+ coro.switch()
+ coro2 = coro.clone()
+ result.append(3)
+ coro2.switch()
+ result.append(5)
+ coro.switch()
+ result.append(6)
+ n = 0
+ for i in result:
+ n = n*10 + i
+ return n
+
+ run = self.runner(f)
+ res = run([])
+ assert res == 1234546
+
+ def test_fork(self):
+ import py; py.test.skip("in-progress")
+ class T(AbstractThunk):
+ def __init__(self, result):
+ self.result = result
+ def call(self):
+ localdata = [10]
+ self.result.append(2)
+ newcoro = fork()
+ localdata.append(20)
+ if newcoro is not None:
+ # in the parent
+ self.result.append(3)
+ newcoro.switch()
+ else:
+ # in the child
+ self.result.append(4)
+ localdata.append(30)
+ self.result.append(localdata != [10, 20, 30])
+ def f():
+ result = []
+ coro = ClonableCoroutine()
+ coro.bind(T(result))
+ result.append(1)
+ coro.switch()
+ result.append(5)
+ coro.switch() # resume after newcoro.switch()
+ result.append(6)
+ n = 0
+ for i in result:
+ n = n*10 + i
+ return n
+
+ run = self.runner(f)
+ res = run([])
+ assert res == 12340506
Modified: pypy/dist/pypy/module/stackless/test/test_interp_coroutine.py
==============================================================================
--- pypy/dist/pypy/module/stackless/test/test_interp_coroutine.py (original)
+++ pypy/dist/pypy/module/stackless/test/test_interp_coroutine.py Wed May 24 21:24:01 2006
@@ -15,10 +15,14 @@
backendopt = True
stacklessmode = True
gcpolicy = gc.BoehmGcPolicy
+ Coroutine = Coroutine
def setup_meth(self):
costate.__init__()
+ def _freeze_(self): # for 'self.Coroutine'
+ return True
+
def test_coroutine(self):
def g(lst, coros):
@@ -48,8 +52,8 @@
def f():
lst = [1]
coro_f = costate.main
- coro_g = Coroutine()
- coro_h = Coroutine()
+ coro_g = self.Coroutine()
+ coro_h = self.Coroutine()
coros = [coro_f, coro_g, coro_h]
thunk_g = T(g, lst, coros)
output('binding g after f set 1')
@@ -116,8 +120,8 @@
def f1(coro_f1):
lst = [1]
- coro_g = Coroutine()
- coro_h = Coroutine()
+ coro_g = self.Coroutine()
+ coro_h = self.Coroutine()
coros = [coro_f1, coro_g, coro_h]
thunk_g = T(g, lst, coros)
output('binding g after f1 set 1')
@@ -143,7 +147,7 @@
def f():
coro_f = costate.main
- coro_f1 = Coroutine()
+ coro_f1 = self.Coroutine()
thunk_f1 = T1(f1, coro_f1)
output('binding f1 after f set 1')
coro_f1.bind(thunk_f1)
@@ -171,7 +175,7 @@
costate.main.switch()
def f():
- coro_g = Coroutine()
+ coro_g = self.Coroutine()
thunk_g = T(g, 42)
coro_g.bind(thunk_g)
coro_g.switch()
@@ -182,7 +186,7 @@
coro_g.kill()
res *= 10
res |= coro_g.frame is None
- coro_g = Coroutine()
+ coro_g = self.Coroutine()
thunk_g = T(g, -42)
coro_g.bind(thunk_g)
try:
@@ -246,8 +250,8 @@
def pre_order_eq(t1, t2):
objects = []
- producer = Coroutine()
- consumer = Coroutine()
+ producer = self.Coroutine()
+ consumer = self.Coroutine()
producer.bind(Producer(t1, objects, consumer))
cons = Consumer(t2, objects, producer)
Modified: pypy/dist/pypy/translator/c/test/test_stackless.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_stackless.py (original)
+++ pypy/dist/pypy/translator/c/test/test_stackless.py Wed May 24 21:24:01 2006
@@ -14,14 +14,13 @@
def setup_class(cls):
import py
- if cls.gcpolicy is None:
+ if cls.gcpolicy in (None, gc.RefcountingGcPolicy):
# to re-enable this, remove the two characters 'gc' in the
# declaregcptrtype(rstack.frame_stack_top,...) call in
# rpython/extfunctable. Doing so breaks translator/stackless/.
import py
py.test.skip("stackless + refcounting doesn't work any more for now")
- else:
- assert cls.gcpolicy is gc.BoehmGcPolicy
+ elif cls.gcpolicy is gc.BoehmGcPolicy:
from pypy.translator.tool.cbuild import check_boehm_presence
if not check_boehm_presence():
py.test.skip("Boehm GC not present")
More information about the Pypy-commit
mailing list