[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