[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