Author: Armin Rigo <ar...@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() _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit