[pypy-commit] pypy stacklet: Starting on the new _stacklet module...

arigo noreply at buildbot.pypy.org
Sat Aug 6 12:31:14 CEST 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: stacklet
Changeset: r46309:8d3f74f9011b
Date: 2011-08-06 11:15 +0200
http://bitbucket.org/pypy/pypy/changeset/8d3f74f9011b/

Log:	Starting on the new _stacklet module...

diff --git a/pypy/module/_stacklet/__init__.py b/pypy/module/_stacklet/__init__.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_stacklet/__init__.py
@@ -0,0 +1,15 @@
+from pypy.interpreter.mixedmodule import MixedModule
+
+
+class Module(MixedModule):
+    """
+    This module exposes stacklets directly.
+    """
+
+    appleveldefs = {
+        'error': 'app_stacklet.error',
+    }
+
+    interpleveldefs = {
+        'newstacklet': 'interp_stacklet.stacklet_new',
+    }
diff --git a/pypy/module/_stacklet/app_stacklet.py b/pypy/module/_stacklet/app_stacklet.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_stacklet/app_stacklet.py
@@ -0,0 +1,4 @@
+
+
+class error(Exception):
+    "Usage error of the _stacklet module."
diff --git a/pypy/module/_stacklet/interp_stacklet.py b/pypy/module/_stacklet/interp_stacklet.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_stacklet/interp_stacklet.py
@@ -0,0 +1,96 @@
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rlib import rstacklet
+from pypy.interpreter.error import OperationError
+from pypy.interpreter.executioncontext import ExecutionContext
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app
+
+
+class SThread(object):
+    def __init__(self, space):
+        w_module = space.getbuiltinmodule('_stacklet')
+        self.space = space
+        self.w_error = space.getattr(w_module, space.wrap('error'))
+        self.thrd = rstacklet.newthread()
+        if not self.thrd:
+            raise MemoryError
+
+    def __del__(self):
+        thrd = self.thrd
+        if thrd:
+            self.thrd = lltype.nullptr(rstacklet.thread_handle.TO)
+            rstacklet.deletethread(thrd)
+
+    def new_stacklet_object(self, space, h):
+        if not h:
+            start_state.sthread = None
+            start_state.w_callable = None
+            start_state.args = None
+            raise MemoryError
+        elif rstacklet.is_empty_handle(h):
+            return space.w_None
+        else:
+            return W_Stacklet(self, h)
+
+ExecutionContext.stacklet_thread = None
+
+
+class W_Stacklet(Wrappable):
+    def __init__(self, sthread, h):
+        self.sthread = sthread
+        self.h = h
+
+    def consume_handle(self):
+        h = self.h
+        if h:
+            self.h = lltype.nullptr(rstacklet.handle.TO)
+            return h
+        else:
+            space = self.sthread.space
+            raise OperationError(
+                self.sthread.w_error,
+                space.wrap("stacklet has already been resumed"))
+
+    def is_pending(self, space):
+        return space.newbool(bool(self.h))
+
+W_Stacklet.typedef = TypeDef(
+    'Stacklet',
+    __module__ = '_stacklet',
+    is_pending = interp2app(W_Stacklet.is_pending),
+    )
+
+
+class StartState:
+    sthread = None  # xxx a single global to pass around the function to start
+    w_callable = None
+    args = None
+start_state = StartState()
+
+def new_stacklet_callback(h, arg):
+    sthread = start_state.sthread
+    w_callable = start_state.w_callable
+    args = start_state.args
+    start_state.sthread = None
+    start_state.w_callable = None
+    start_state.args = None
+    #
+    space = sthread.space
+    args = args.prepend(space.wrap(W_Stacklet(sthread, h)))
+    w_result = space.call_args(w_callable, args)
+    #
+    assert isinstance(w_result, W_Stacklet)
+    return w_result.consume_handle()
+
+def stacklet_new(space, w_callable, __args__):
+    ec = space.getexecutioncontext()
+    sthread = ec.stacklet_thread
+    if not sthread:
+        sthread = ec.stacklet_thread = SThread(space)
+    start_state.sthread = sthread
+    start_state.w_callable = w_callable
+    start_state.args = __args__
+    h = rstacklet.new(sthread.thrd, new_stacklet_callback,
+                      lltype.nullptr(rffi.VOIDP.TO))
+    return sthread.new_stacklet_object(space, h)
diff --git a/pypy/module/_stacklet/test/test_stacklet.py b/pypy/module/_stacklet/test/test_stacklet.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_stacklet/test/test_stacklet.py
@@ -0,0 +1,19 @@
+from pypy.conftest import gettestobjspace
+
+
+class AppTestStacklet:
+    def setup_class(cls):
+        cls.space = gettestobjspace(usemodules=['_stacklet'])
+
+    def test_new_empty(self):
+        from _stacklet import newstacklet
+        #
+        def empty_callback(h):
+            assert h.is_pending()
+            seen.append(1)
+            return h
+        #
+        seen = []
+        h = newstacklet(empty_callback)
+        assert h is None
+        assert seen == [1]
diff --git a/pypy/rlib/rcoroutine.py b/pypy/rlib/rcoroutine.py
--- a/pypy/rlib/rcoroutine.py
+++ b/pypy/rlib/rcoroutine.py
@@ -29,6 +29,11 @@
 The type of a switch is determined by the target's costate.
 """
 
+import py; py.test.skip("fixme: rewrite using rlib.rstacklet")
+# XXX ^^^ the reason it is not done is that pypy.rlib.rcoroutine
+# plus pypy/module/_stackless look like faaaaaar too much code
+# to me :-(
+
 from pypy.rlib.rstack import yield_current_frame_to_caller
 from pypy.rlib.objectmodel import we_are_translated
 
diff --git a/pypy/rlib/rstacklet.py b/pypy/rlib/rstacklet.py
--- a/pypy/rlib/rstacklet.py
+++ b/pypy/rlib/rstacklet.py
@@ -3,12 +3,15 @@
 
 ###
 ### Note: stacklets do not reliably work on top of CPython, but well,
-### they seem to work fine after being translated...
+### they seem to work fine after being translated...  This is due
+### to the fact that on CPython, you get strange effects because the
+### PyThreadState is not explicitly handled when we start a new
+### stacklet or switch to another one, notably the 'frame' field.
 ###
 
 
 #cdir = py.path.local(pypydir) / 'translator' / 'c'
-cdir = '/home/arigo/hg/arigo/hack/pypy-hack/stacklet'
+cdir = '/home/arigo/hg/arigo/hack/pypy-hack/stacklet'    # XXX FIXME
 
 
 eci = ExternalCompilationInfo(
@@ -18,8 +21,8 @@
 )
 
 def llexternal(name, args, result):
-    return rffi.llexternal(name, args, result, compilation_info=eci)
-
+    return rffi.llexternal(name, args, result, compilation_info=eci,
+                           sandboxsafe=True)
 
 # ----- types -----
 


More information about the pypy-commit mailing list