[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