[pypy-commit] pypy stacklet: Starting work on greenlets.

arigo noreply at buildbot.pypy.org
Fri Aug 19 14:53:27 CEST 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: stacklet
Changeset: r46633:448721060198
Date: 2011-08-19 14:44 +0200
http://bitbucket.org/pypy/pypy/changeset/448721060198/

Log:	Starting work on greenlets.

diff --git a/lib_pypy/greenlet.py b/lib_pypy/greenlet.py
--- a/lib_pypy/greenlet.py
+++ b/lib_pypy/greenlet.py
@@ -1,1 +1,135 @@
-from _stackless import greenlet
+import _continuation, sys
+
+
+# ____________________________________________________________
+# Exceptions
+
+class GreenletExit(Exception):
+    """This special exception does not propagate to the parent greenlet; it
+can be used to kill a single greenlet."""
+
+error = _continuation.error
+
+# ____________________________________________________________
+# Helper function
+
+def getcurrent():
+    "Returns the current greenlet (i.e. the one which called this function)."
+    try:
+        return _tls.current
+    except AttributeError:
+        # first call in this thread: current == main
+        _green_create_main()
+        return _tls.current
+
+# ____________________________________________________________
+# The 'greenlet' class
+
+_continulet = _continuation.continulet
+
+class greenlet(_continulet):
+    getcurrent = staticmethod(getcurrent)
+    error = error
+    GreenletExit = GreenletExit
+    __main = False
+    __started = False
+
+    def __init__(self, run=None):
+        if run is not None:
+            self.run = run
+
+    def switch(self, *args):
+        "Switch execution to this greenlet, optionally passing the values "
+        "given as argument(s).  Returns the value passed when switching back."
+        current = getcurrent()
+        target = self
+        if not target.is_pending() and not target.__main:
+            if not target.__started:
+                _continulet.__init__(target, _greenlet_start, args)
+                args = None
+                target.__started = True
+            else:
+                # already done, go to main instead
+                xxxx
+                target = _tls.main
+        #
+        try:
+            if current.__main:
+                if target.__main:
+                    # switch from main to main
+                    pass
+                else:
+                    # enter from main to target
+                    print 'main -> target =', target
+                    args = _continulet.switch(target, args)
+            else:
+                if target.__main:
+                    # leave to go to target=main
+                    print 'current =', current, '-> main'
+                    args = _continulet.switch(current, args)
+                else:
+                    # switch from non-main to non-main
+                    print 'current =', current, '-> target =', target
+                    args = _continulet.switch(current, args, to=target)
+        finally:
+            _tls.current = current
+            print 'current is now', current
+        #
+        print 'got args =', args
+        if len(args) == 1:
+            return args[0]
+        else:
+            return args
+
+    def throw(self, typ=GreenletExit, val=None, tb=None):
+        "raise exception in greenlet, return value passed when switching back"
+        if self.__state_dead():
+            # dead greenlet: turn GreenletExit into a regular return
+            if (isinstance(typ, type(GreenletExit)) and
+                issubclass(typ, GreenletExit)):
+                if val is None:
+                    return self.switch(typ())
+                if isinstance(val, GreenletExit):
+                    return self.switch(val)
+            if isinstance(typ, GreenletExit):
+                return self.switch(typ)
+        #
+        _tls.passaround_exception = (typ, val, tb)
+        return self.switch()
+
+    __nonzero__ = _continulet.is_pending
+
+##    dead     = property(__state_dead)
+
+    @property
+    def parent(self):
+        # Don't support nesting for now.
+        if self.__main:
+            return None
+        else:
+            return _tls.main
+
+# ____________________________________________________________
+# Internal stuff
+
+try:
+    from thread import _local
+except ImportError:
+    class _local(object):    # assume no threads
+        pass
+
+_tls = _local()
+
+def _green_create_main():
+    # create the main greenlet for this thread
+    gmain = greenlet.__new__(greenlet)
+    gmain._greenlet__main = True
+    gmain._greenlet__started = True
+    _tls.main = gmain
+    _tls.current = gmain
+
+def _greenlet_start(greenlet, args):
+    print 'starting', greenlet, 'with args =', args
+    _tls.current = greenlet
+    res = greenlet.run(*args)
+    return (res,)
diff --git a/pypy/module/test_lib_pypy/test_greenlet.py b/pypy/module/test_lib_pypy/test_greenlet.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/test_lib_pypy/test_greenlet.py
@@ -0,0 +1,21 @@
+from pypy.conftest import gettestobjspace
+
+
+class AppTestGreenlet:
+    def setup_class(cls):
+        cls.space = gettestobjspace(usemodules=['_continuation'])
+
+    def test_simple(self):
+        from greenlet import greenlet
+        lst = []
+        def f():
+            lst.append(1)
+            greenlet.getcurrent().parent.switch()
+            lst.append(3)
+        g = greenlet(f)
+        lst.append(0)
+        g.switch()
+        lst.append(2)
+        g.switch()
+        lst.append(4)
+        assert lst == range(5)


More information about the pypy-commit mailing list