[pypy-svn] r34749 - in pypy/dist/pypy: . interpreter module/_stackless module/_stackless/test

arigo at codespeak.net arigo at codespeak.net
Sat Nov 18 23:09:41 CET 2006


Author: arigo
Date: Sat Nov 18 23:09:38 2006
New Revision: 34749

Added:
   pypy/dist/pypy/module/_stackless/clonable.py   (contents, props changed)
   pypy/dist/pypy/module/_stackless/test/test_clonable.py   (contents, props changed)
Modified:
   pypy/dist/pypy/conftest.py
   pypy/dist/pypy/interpreter/mixedmodule.py
   pypy/dist/pypy/module/_stackless/__init__.py
   pypy/dist/pypy/module/_stackless/interp_clonable.py
Log:
(cfbolz a couple of days ago, arigo)

Rewrite the app-level clonable coroutine by reusing as much existing
code as possible, with a mixin, instead of copy-n-paste.

Debugging of a stackless+framework pypy-c is in-progress...


Modified: pypy/dist/pypy/conftest.py
==============================================================================
--- pypy/dist/pypy/conftest.py	(original)
+++ pypy/dist/pypy/conftest.py	Sat Nov 18 23:09:38 2006
@@ -113,6 +113,9 @@
     def unpackiterable(self, itr):
         return list(itr)
 
+    def is_true(self, obj):
+        return bool(obj)
+
 
 class OpErrKeyboardInterrupt(KeyboardInterrupt):
     pass

Modified: pypy/dist/pypy/interpreter/mixedmodule.py
==============================================================================
--- pypy/dist/pypy/interpreter/mixedmodule.py	(original)
+++ pypy/dist/pypy/interpreter/mixedmodule.py	Sat Nov 18 23:09:38 2006
@@ -88,7 +88,15 @@
             if '__doc__' not in loaders:
                 loaders['__doc__'] = cls.get__doc__
 
-    buildloaders = classmethod(buildloaders) 
+    buildloaders = classmethod(buildloaders)
+
+    def extra_interpdef(self, name, spec):
+        cls = self.__class__
+        pkgroot = cls.__module__
+        loader = getinterpevalloader(pkgroot, spec)
+        space = self.space
+        w_obj = loader(space)
+        space.setattr(space.wrap(self), space.wrap(name), w_obj)
 
     def get__file__(cls, space): 
         """ NOT_RPYTHON. 

Modified: pypy/dist/pypy/module/_stackless/__init__.py
==============================================================================
--- pypy/dist/pypy/module/_stackless/__init__.py	(original)
+++ pypy/dist/pypy/module/_stackless/__init__.py	Sat Nov 18 23:09:38 2006
@@ -14,7 +14,6 @@
     interpleveldefs = {
         'tasklet'    : 'interp_stackless.tasklet',
         'coroutine'  : 'coroutine.AppCoroutine',
-        #'clonable'   : 'clonable.ClonableCoroutine',
         'greenlet'   : 'interp_greenlet.AppGreenlet',
         'usercostate': 'composable_coroutine.W_UserCoState',
     }
@@ -24,8 +23,11 @@
         # are not yet directly supported
         from pypy.module._stackless.coroutine import post_install as post_install_coro
         post_install_coro(self)
-        #from pypy.module._stackless.clonable import post_install as post_install_clonable
-        #post_install_clonable(self)
         from pypy.module._stackless.interp_greenlet import post_install as post_install_greenlet
         post_install_greenlet(self)
 
+        if self.space.config.translation.gc in ('framework', 'stacklessgc'):
+            from pypy.module._stackless.clonable import post_install as post_install_clonable
+            self.extra_interpdef('clonable', 'clonable.AppClonableCoroutine')
+            self.extra_interpdef('fork',     'clonable.fork')
+            post_install_clonable(self)

Added: pypy/dist/pypy/module/_stackless/clonable.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/module/_stackless/clonable.py	Sat Nov 18 23:09:38 2006
@@ -0,0 +1,103 @@
+from pypy.interpreter.error import OperationError
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app, ObjSpace, W_Root
+from pypy.module._stackless.coroutine import AppCoroutine, AppCoState
+from pypy.module._stackless.coroutine import makeStaticMethod
+from pypy.module._stackless.interp_coroutine import AbstractThunk
+from pypy.module._stackless.interp_clonable import InterpClonableMixin
+
+
+class AppClonableCoroutine(AppCoroutine, InterpClonableMixin):
+
+    def hello(self):
+        self.hello_local_pool()
+        AppCoroutine.hello(self)
+
+    def goodbye(self):
+        AppCoroutine.goodbye(self)
+        self.goodbye_local_pool()
+
+    def descr_method__new__(space, w_subtype):
+        co = space.allocate_instance(AppClonableCoroutine, w_subtype)
+        costate = AppClonableCoroutine._get_state(space)
+        AppClonableCoroutine.__init__(co, space, state=costate)
+        return space.wrap(co)
+
+    def _get_state(space):
+        return space.fromcache(AppClonableCoState)
+    _get_state = staticmethod(_get_state)
+
+    def w_getcurrent(space):
+        return space.wrap(AppClonableCoroutine._get_state(space).current)
+    w_getcurrent = staticmethod(w_getcurrent)
+
+    def w_clone(self):
+        space = self.space
+        costate = self.costate
+        if costate.current is self:
+            raise OperationError(space.w_RuntimeError,
+                                 space.wrap("clone() cannot clone the "
+                                            "current coroutine"
+                                            "; use fork() instead"))
+        copy = AppClonableCoroutine(space, state=costate)
+        self.clone_into(copy)
+        return space.wrap(copy)
+
+    def descr__reduce__(self, space):
+        raise OperationError(space.w_TypeError,
+                             space.wrap("_stackless.clonable instances are "
+                                        "not picklable"))
+
+
+AppClonableCoroutine.typedef = TypeDef("clonable", AppCoroutine.typedef,
+    __new__    = interp2app(AppClonableCoroutine.descr_method__new__.im_func),
+    getcurrent = interp2app(AppClonableCoroutine.w_getcurrent),
+    clone      = interp2app(AppClonableCoroutine.w_clone),
+    __reduce__ = interp2app(AppClonableCoroutine.descr__reduce__,
+                            unwrap_spec=['self', ObjSpace]),
+)
+
+class AppClonableCoState(AppCoState):
+    def post_install(self):
+        self.current = self.main = AppClonableCoroutine(self.space,
+                                                        is_main=True,
+                                                        state=self)
+
+def post_install(module):
+    makeStaticMethod(module, 'clonable', 'getcurrent')
+    space = module.space
+    AppClonableCoroutine._get_state(space).post_install()
+
+# ____________________________________________________________
+
+class ForkThunk(AbstractThunk):
+    def __init__(self, coroutine):
+        self.coroutine = coroutine
+        self.newcoroutine = None
+    def call(self):
+        oldcoro = self.coroutine
+        self.coroutine = None
+        newcoro = AppClonableCoroutine(oldcoro.space, state=oldcoro.costate)
+        oldcoro.clone_into(newcoro)
+        self.newcoroutine = newcoro
+
+def fork(space):
+    """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.
+    """
+    costate = AppClonableCoroutine._get_state(space)
+    current = costate.current
+    if current is costate.main:
+        raise OperationError(space.w_RuntimeError,
+                             space.wrap("cannot fork() in the main "
+                                        "clonable coroutine"))
+    thunk = ForkThunk(current)
+    coro_fork = AppClonableCoroutine(space, state=costate)
+    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 space.wrap(thunk.newcoroutine)
+fork.unwrap_spec = [ObjSpace]

Modified: pypy/dist/pypy/module/_stackless/interp_clonable.py
==============================================================================
--- pypy/dist/pypy/module/_stackless/interp_clonable.py	(original)
+++ pypy/dist/pypy/module/_stackless/interp_clonable.py	Sat Nov 18 23:09:38 2006
@@ -6,31 +6,46 @@
 
 from pypy.tool import stdlib_opcode as pythonopcode
 
-class InterpClonableCoroutine(Coroutine):
+
+class InterpClonableMixin:
     local_pool = None
+    _mixin_ = True
 
-    def hello(self):
+    def hello_local_pool(self):
         if we_are_translated():
             self.saved_pool = gc_swap_pool(self.local_pool)
 
-    def goodbye(self):
+    def goodbye_local_pool(self):
         if we_are_translated():
             self.local_pool = gc_swap_pool(self.saved_pool)
 
-    def clone(self):
+    def clone_into(self, copy):
         if not we_are_translated():
             raise NotImplementedError
-        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 = InterpClonableCoroutine(self.costate)
         copy.parent = self.parent
         copy.frame, copy.local_pool = gc_clone(self.frame, self.local_pool)
+
+
+class InterpClonableCoroutine(Coroutine, InterpClonableMixin):
+
+    def hello(self):
+        self.hello_local_pool()
+
+    def goodbye(self):
+        self.goodbye_local_pool()
+
+    def clone(self):
+        # hack, this is overridden in AppClonableCoroutine
+        if self.getcurrent() is self:
+            raise RuntimeError("clone() cannot clone the current coroutine; "
+                               "use fork() instead")
+        copy = InterpClonableCoroutine(self.costate)
+        self.clone_into(copy)
         return copy
 
 

Added: pypy/dist/pypy/module/_stackless/test/test_clonable.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/module/_stackless/test/test_clonable.py	Sat Nov 18 23:09:38 2006
@@ -0,0 +1,83 @@
+from pypy.conftest import gettestobjspace, option
+import py, sys
+
+# app-level testing of coroutine cloning
+
+class AppTestClonable:
+
+    def setup_class(cls):
+        if not option.runappdirect:
+            py.test.skip('pure appdirect test (run with -A)')
+        cls.space = space = gettestobjspace(usemodules=('_stackless',))
+        if not space.is_true(space.appexec([], """():
+            import _stackless
+            return hasattr(_stackless, 'clonable')
+        """)):
+            py.test.skip('no _stackless.clonable')
+
+
+    def test_solver(self):
+        import new, sys
+
+        mod = new.module('mod')
+        sys.modules['mod'] = mod
+        try:
+            exec '''
+import _stackless
+
+class Fail(Exception):
+    pass
+
+class Success(Exception):
+    pass
+
+def first_solution(func):
+    global next_answer
+    co = _stackless.clonable()
+    co.bind(func)
+    pending = [(co, None)]
+    while pending:
+        co, next_answer = pending.pop()
+        try:
+            co.switch()
+        except Fail:
+            pass
+        except Success, e:
+            return e.args[0]
+        else:
+            # zero_or_one() called, clone the coroutine
+            co2 = co.clone()
+            pending.append((co2, 1))
+            pending.append((co, 0))
+    raise Fail("no solution")
+
+pending = []
+main = _stackless.clonable.getcurrent()
+
+def zero_or_one():
+    main.switch()
+    return next_answer
+
+# ____________________________________________________________
+
+invalid_prefixes = {
+    (0, 0): True,
+    (0, 1, 0): True,
+    (0, 1, 1): True,
+    (1, 0): True,
+    (1, 1, 0, 0): True,
+    }
+
+def example():
+    test = []
+    for n in range(5):
+        test.append(zero_or_one())
+        if tuple(test) in invalid_prefixes:
+            raise Fail
+    raise Success(test)
+
+res = first_solution(example)
+assert res == [1, 1, 0, 1, 0]
+''' in mod.__dict__
+        finally:
+            del sys.modules['mod']



More information about the Pypy-commit mailing list