[pypy-commit] pypy stacklet: Starting...

arigo noreply at buildbot.pypy.org
Fri Aug 5 19:25:13 CEST 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: stacklet
Changeset: r46294:8064be3dd602
Date: 2011-08-05 18:10 +0200
http://bitbucket.org/pypy/pypy/changeset/8064be3dd602/

Log:	Starting...

diff --git a/pypy/rlib/rstacklet.py b/pypy/rlib/rstacklet.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/rstacklet.py
@@ -0,0 +1,42 @@
+from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.translator.tool.cbuild import ExternalCompilationInfo
+
+###
+### Note: stacklets do not reliably work on top of CPython, but well,
+### they seem to work fine after being translated...
+###
+
+
+#cdir = py.path.local(pypydir) / 'translator' / 'c'
+cdir = '/home/arigo/hg/arigo/hack/pypy-hack/stacklet'
+
+
+eci = ExternalCompilationInfo(
+    include_dirs = [cdir],
+    includes = ['stacklet.h'],
+    separate_module_sources = ['#include "stacklet.c"\n'],
+)
+
+def llexternal(name, args, result):
+    return rffi.llexternal(name, args, result, compilation_info=eci)
+
+
+# ----- types -----
+
+handle = lltype.Signed
+thread_handle = lltype.Signed
+run_fn = lltype.Ptr(lltype.FuncType([handle, lltype.Signed], handle))
+
+# ----- constants -----
+
+EMPTY_STACK_HANDLE = -1
+
+# ----- functions -----
+
+newthread = llexternal('stacklet_newthread', [], thread_handle)
+deletethread = llexternal('stacklet_deletethread',[thread_handle], lltype.Void)
+
+new = llexternal('stacklet_new', [thread_handle, run_fn, lltype.Signed],
+                 handle)
+switch = llexternal('stacklet_switch', [thread_handle, handle], handle)
+destroy = llexternal('stacklet_destroy', [thread_handle, handle], lltype.Void)
diff --git a/pypy/rlib/test/test_rstacklet.py b/pypy/rlib/test/test_rstacklet.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/test/test_rstacklet.py
@@ -0,0 +1,219 @@
+from pypy.rlib import rstacklet, rrandom
+from pypy.rlib.rarithmetic import intmask
+from pypy.translator.c.test.test_standalone import StandaloneTests
+
+
+STATUSMAX = 5000
+
+
+class Runner:
+
+    def init(self, seed):
+        self.thrd = rstacklet.newthread()
+        self.random = rrandom.Random(seed)
+
+    def done(self):
+        rstacklet.deletethread(self.thrd)
+
+    TESTS = []
+    def here_is_a_test(fn, TESTS=TESTS):
+        TESTS.append((fn.__name__, fn))
+        return fn
+
+    @here_is_a_test
+    def test_new(self):
+        print 'start'
+        h = rstacklet.new(self.thrd, empty_callback, 123)
+        print 'end', h
+        assert h == -1
+
+    def nextstatus(self, nextvalue):
+        print 'expected nextvalue to be %d, got %d' % (nextvalue,
+                                                       self.status + 1)
+        assert self.status + 1 == nextvalue
+        self.status = nextvalue
+
+    @here_is_a_test
+    def test_simple_switch(self):
+        self.status = 0
+        h = rstacklet.new(self.thrd, switchbackonce_callback, 321)
+        assert h != rstacklet.EMPTY_STACK_HANDLE
+        self.nextstatus(2)
+        h = rstacklet.switch(runner.thrd, h)
+        self.nextstatus(4)
+        print 'end', h
+        assert h == rstacklet.EMPTY_STACK_HANDLE
+
+    @here_is_a_test
+    def test_various_depths(self):
+        self.tasks = [Task(i) for i in range(10)]
+        self.nextstep = -1
+        self.comefrom = -1
+        self.status = 0
+        while self.status < STATUSMAX or self.any_alive():
+            self.tasks[0].withdepth(self.random.genrand32() % 50)
+
+    def any_alive(self):
+        for task in self.tasks:
+            if task.h != 0:
+                return True
+        return False
+
+
+class Task:
+    def __init__(self, n):
+        self.n = n
+        self.h = 0
+
+    def withdepth(self, d):
+        if d > 0:
+            res = self.withdepth(d-1)
+        else:
+            res = 0
+            n = intmask(runner.random.genrand32() % 10)
+            if n == self.n or (runner.status >= STATUSMAX and
+                               runner.tasks[n].h == 0):
+                return 1
+
+            print "status == %d, self.n = %d" % (runner.status, self.n)
+            assert self.h == 0
+            assert runner.nextstep == -1
+            runner.status += 1
+            runner.nextstep = runner.status
+            runner.comefrom = self.n
+            runner.gointo = n
+            task = runner.tasks[n]
+            if task.h == 0:
+                # start a new stacklet
+                print "NEW", n
+                h = rstacklet.new(runner.thrd, variousstackdepths_callback, n)
+            else:
+                # switch to this stacklet
+                print "switch to", n
+                h = task.h
+                task.h = 0
+                h = rstacklet.switch(runner.thrd, h)
+
+            print "back in self.n = %d, coming from %d" % (self.n,
+                                                           runner.comefrom)
+            assert runner.nextstep == runner.status
+            runner.nextstep = -1
+            assert runner.gointo == self.n
+            assert runner.comefrom != self.n
+            assert self.h == 0
+            if runner.comefrom != -42:
+                assert 0 <= runner.comefrom < 10
+                task = runner.tasks[runner.comefrom]
+                assert task.h == 0
+                task.h = h
+            else:
+                assert h == rstacklet.EMPTY_STACK_HANDLE
+            runner.comefrom = -1
+            runner.gointo = -1
+        assert (res & (res-1)) == 0   # to prevent a tail-call to withdepth()
+        return res
+
+
+runner = Runner()
+
+
+def empty_callback(h, arg):
+    print 'in empty_callback:', h, arg
+    assert arg == 123
+    return h
+
+def switchbackonce_callback(h, arg):
+    print 'in switchbackonce_callback:', h, arg
+    assert arg == 321
+    runner.nextstatus(1)
+    assert h != rstacklet.EMPTY_STACK_HANDLE
+    h = rstacklet.switch(runner.thrd, h)
+    runner.nextstatus(3)
+    assert h != rstacklet.EMPTY_STACK_HANDLE
+    return h
+
+def variousstackdepths_callback(h, arg):
+    assert runner.nextstep == runner.status
+    runner.nextstep = -1
+    assert arg == runner.gointo
+    self = runner.tasks[arg]
+    assert self.n == runner.gointo
+    assert self.h == 0
+    assert 0 <= runner.comefrom < 10
+    task = runner.tasks[runner.comefrom]
+    assert task.h == 0
+    assert h != 0 and h != rstacklet.EMPTY_STACK_HANDLE
+    task.h = h
+    runner.comefrom = -1
+    runner.gointo = -1
+
+    while self.withdepth(runner.random.genrand32() % 20) == 0:
+        pass
+
+    assert self.h == 0
+    while 1:
+        n = intmask(runner.random.genrand32() % 10)
+        h = runner.tasks[n].h
+        if h != 0:
+            break
+
+    assert h != rstacklet.EMPTY_STACK_HANDLE
+    runner.tasks[n].h = 0
+    runner.comefrom = -42
+    runner.gointo = n
+    assert runner.nextstep == -1
+    runner.status += 1
+    runner.nextstep = runner.status
+    print "LEAVING %d to go to %d" % (self.n, n)
+    return h
+
+
+def entry_point(argv):
+    seed = 0
+    if len(argv) > 1:
+        seed = int(argv[1])
+    runner.init(seed)
+    for name, meth in runner.TESTS:
+        print '-----', name, '-----'
+        meth(runner)
+    print '----- all done -----'
+    runner.done()
+    return 0
+
+
+class BaseTestStacklet(StandaloneTests):
+
+    def setup_class(cls):
+        from pypy.config.pypyoption import get_pypy_config
+        config = get_pypy_config(translating=True)
+        config.translation.gc = cls.gc
+        if cls.gcrootfinder is not None:
+            config.translation.gcrootfinder = cls.gcrootfinder
+        #try:
+        #    config.translation.tealet = True
+        #except ConflictConfigError, e:
+        #    py.test.skip(str(e))
+        cls.config = config
+
+    def test_demo1(self):
+        t, cbuilder = self.compile(entry_point)
+
+        expected_data = "----- all done -----\n"
+        for i in range(20):
+            data = cbuilder.cmdexec('%d' % i, env={})
+            assert data.endswith(expected_data)
+            #data = cbuilder.cmdexec('%d' % i, env={'PYPY_GC_NURSERY': '10k'})
+            #assert data.endswith(expected_data)
+
+
+class TestStackletBoehm(BaseTestStacklet):
+    gc = 'boehm'
+    gcrootfinder = None
+
+
+def target(*args):
+    return entry_point, None
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(entry_point(sys.argv))
diff --git a/pypy/rpython/lltypesystem/ll2ctypes.py b/pypy/rpython/lltypesystem/ll2ctypes.py
--- a/pypy/rpython/lltypesystem/ll2ctypes.py
+++ b/pypy/rpython/lltypesystem/ll2ctypes.py
@@ -1092,6 +1092,8 @@
     for i in range(len(FUNCTYPE.ARGS)):
         if FUNCTYPE.ARGS[i] is lltype.Void:
             void_arguments.append(i)
+    def callme(cargs):   # an extra indirection: workaround for rlib.rstacklet
+        return cfunc(*cargs)
     def invoke_via_ctypes(*argvalues):
         global _callback_exc_info
         cargs = []
@@ -1103,7 +1105,7 @@
                 cargs.append(cvalue)
         _callback_exc_info = None
         _restore_c_errno()
-        cres = cfunc(*cargs)
+        cres = callme(cargs)
         _save_c_errno()
         if _callback_exc_info:
             etype, evalue, etb = _callback_exc_info


More information about the pypy-commit mailing list