[pypy-commit] pypy stacklet: Rewrite this test file for continuations. Also add a multithreaded test.

arigo noreply at buildbot.pypy.org
Sat Aug 27 09:59:52 CEST 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: stacklet
Changeset: r46813:68b38c3ff9e0
Date: 2011-08-27 10:04 +0200
http://bitbucket.org/pypy/pypy/changeset/68b38c3ff9e0/

Log:	Rewrite this test file for continuations. Also add a multithreaded
	test.

diff --git a/pypy/module/_continuation/test/test_translated.py b/pypy/module/_continuation/test/test_translated.py
--- a/pypy/module/_continuation/test/test_translated.py
+++ b/pypy/module/_continuation/test/test_translated.py
@@ -1,125 +1,132 @@
+import py
 try:
     import _continuation
 except ImportError:
-    import py; py.test.skip("to run on top of a translated pypy-c")
+    py.test.skip("to run on top of a translated pypy-c")
 
-import random
+import sys, random
 
 # ____________________________________________________________
 
 STATUS_MAX = 50000
-MAIN_RECURSION_MAX = 50
-SUB_RECURSION_MAX = 20
+CONTINULETS = 50
 
 def set_fast_mode():
-    global STATUS_MAX, MAIN_RECURSION_MAX, SUB_RECURSION_MAX
+    global STATUS_MAX, CONTINULETS
     STATUS_MAX = 100
-    MAIN_RECURSION_MAX = 7
-    SUB_RECURSION_MAX = 3
+    CONTINULETS = 5
 
 # ____________________________________________________________
 
-class Task:
-    def __init__(self, n):
-        self.n = n
-        self.h = None
+class Done(Exception):
+    pass
 
-    def withdepth(self, d):
-        if d > 0:
-            res = self.withdepth(d-1)
-        else:
-            res = 0
-            n = random.randrange(10)
-            if n == self.n or (Task.status >= STATUS_MAX and
-                               not Task.tasks[n].h):
-                return 1
 
-            print "status == %d, self.n = %d" % (Task.status, self.n)
-            assert not self.h
-            assert Task.nextstep == -1
-            Task.status += 1
-            Task.nextstep = Task.status
-            Task.comefrom = self.n
-            Task.gointo = n
-            task = Task.tasks[n]
-            if not task.h:
-                # start a new stacklet
-                print "NEW", n
-                h = _continuation.new(variousstackdepths_callback, n)
-            else:
-                # switch to this stacklet
-                print "switch to", n
-                h = task.h
-                task.h = None
-                h = h.switch()
+class Runner(object):
 
-            print "back in self.n = %d, coming from %d" % (self.n,
-                                                           Task.comefrom)
-            assert Task.nextstep == Task.status
-            Task.nextstep = -1
-            assert Task.gointo == self.n
-            assert Task.comefrom != self.n
-            assert self.h is None
-            if Task.comefrom != -42:
-                assert 0 <= Task.comefrom < 10
-                task = Task.tasks[Task.comefrom]
-                print "setting %r.h = %r" % (task, h)
-                assert task.h is None
-                task.h = h
-            else:
-                assert h is None
-            Task.comefrom = -1
-            Task.gointo = -1
-        assert (res & (res-1)) == 0
-        return res
+    def __init__(self):
+        self.foobar = 12345
+        self.conts = {}     # {continulet: parent-or-None}
+        self.contlist = []
 
-def variousstackdepths_callback(h, arg):
-    assert Task.nextstep == Task.status
-    Task.nextstep = -1
-    assert arg == Task.gointo
-    self = Task.tasks[arg]
-    assert self.n == Task.gointo
-    assert self.h is None
-    assert 0 <= Task.comefrom < 10
-    task = Task.tasks[Task.comefrom]
-    print "initializing %r.h = %r" % (task, h)
-    assert task.h is None
-    assert type(h) is _continuation.Continuation
-    task.h = h
-    Task.comefrom = -1
-    Task.gointo = -1
+    def run_test(self):
+        self.start_continulets()
+        self.n = 0
+        try:
+            while True:
+                self.do_switch(src=None)
+                assert self.target is None
+        except Done:
+            self.check_traceback(sys.exc_info()[2])
 
-    while self.withdepth(random.randrange(SUB_RECURSION_MAX)) == 0:
-        pass
+    def do_switch(self, src):
+        assert src not in self.conts.values()
+        c = random.choice(self.contlist)
+        self.target = self.conts[c]
+        self.conts[c] = src
+        c.switch()
+        assert self.target is src
 
-    assert self.h is None
-    while 1:
-        n = random.randrange(10)
-        h = Task.tasks[n].h
-        if h is not None:
-            break
+    def run_continulet(self, c, i):
+        while True:
+            assert self.target is c
+            assert self.contlist[i] is c
+            self.do_switch(c)
+            assert self.foobar == 12345
+            self.n += 1
+            if self.n >= STATUS_MAX:
+                raise Done
 
-    Task.tasks[n].h = None
-    Task.comefrom = -42
-    Task.gointo = n
-    assert Task.nextstep == -1
-    Task.status += 1
-    Task.nextstep = Task.status
-    print "LEAVING %d to go to %d" % (self.n, n)
-    return h
+    def start_continulets(self, i=0):
+        c = _continuation.continulet(self.run_continulet, i)
+        self.contlist.append(c)
+        if i < CONTINULETS:
+            self.start_continulets(i + 1)
+            # ^^^ start each continulet with a different base stack
+        self.conts[c] = c   # initially (i.e. not started) there are all loops
 
-def any_alive():
-    return any([task.h is not None for task in Task.tasks])
+    def check_traceback(self, tb):
+        found = []
+        tb = tb.tb_next
+        while tb:
+            if tb.tb_frame.f_code.co_name != 'do_switch':
+                assert tb.tb_frame.f_code.co_name == 'run_continulet', (
+                    "got %r" % (tb.tb_frame.f_code.co_name,))
+                found.append(tb.tb_frame.f_locals['c'])
+            tb = tb.tb_next
+        found.reverse()
+        #
+        expected = []
+        c = self.target
+        while c is not None:
+            expected.append(c)
+            c = self.conts[c]
+        #
+        assert found == expected, "%r == %r" % (found, expected)
 
-def test_various_depths():
-    Task.tasks = [Task(i) for i in range(10)]
-    Task.nextstep = -1
-    Task.comefrom = -1
-    Task.status = 0
-    while Task.status < STATUS_MAX or any_alive():
-        Task.tasks[0].withdepth(random.randrange(MAIN_RECURSION_MAX))
+# ____________________________________________________________
+
+class AppTestWrapper:
+    def setup_class(cls):
+        "Run test_various_depths() when we are run with 'pypy py.test -A'."
+        from pypy.conftest import option
+        if not option.runappdirect:
+            py.test.skip("meant only for -A run")
+
+    def test_single_threaded(self):
+        for i in range(20):
+            yield Runner().run_test,
+
+    def test_multi_threaded(self):
+        for i in range(5):
+            yield multithreaded_test,
+
+class ThreadTest(object):
+    def __init__(self, lock):
+        self.lock = lock
+        self.ok = False
+        lock.acquire()
+    def run(self):
+        try:
+            Runner().run_test()
+            self.ok = True
+        finally:
+            self.lock.release()
+
+def multithreaded_test():
+    try:
+        import thread
+    except ImportError:
+        py.test.skip("no threads")
+    ts = [ThreadTest(thread.allocate_lock()) for i in range(5)]
+    for t in ts:
+        thread.start_new_thread(t.run, ())
+    for t in ts:
+        t.lock.acquire()
+    for t in ts:
+        assert t.ok
 
 # ____________________________________________________________
 
 if __name__ == '__main__':
-    test_various_depths()
+    Runner().run_test()


More information about the pypy-commit mailing list