[pypy-commit] pypy stm-thread-2: Start writing a second target test. Fix a subtle bug revealed by it
arigo
noreply at buildbot.pypy.org
Thu Sep 13 07:33:57 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch: stm-thread-2
Changeset: r57320:1f89a668016c
Date: 2012-09-12 23:41 +0200
http://bitbucket.org/pypy/pypy/changeset/1f89a668016c/
Log: Start writing a second target test. Fix a subtle bug revealed by it
(see extradoc/6a8e4f80ba44).
diff --git a/pypy/translator/stm/src_stm/et.c b/pypy/translator/stm/src_stm/et.c
--- a/pypy/translator/stm/src_stm/et.c
+++ b/pypy/translator/stm/src_stm/et.c
@@ -87,14 +87,13 @@
{
if (R != G && --d->readonly_updates < 0)
{
+ volatile revision_t *vp;
d->readonly_updates = 148; /* XXX tweak */
- // compress the chain
- while ((gcptr)G->h_revision != R)
- {
- gcptr G_next = (gcptr)G->h_revision;
- G->h_revision = (revision_t)R;
- G = G_next;
- }
+ // compress the chain one step (cannot compress the whole chain!)
+ // avoid emitting a pointless "write", too.
+ vp = (volatile revision_t *)&G->h_revision;
+ if (*vp != (revision_t)R)
+ *vp = (revision_t)R;
// update the original field
if (R_Container != NULL)
{
diff --git a/pypy/translator/stm/test/targetjit1.py b/pypy/translator/stm/test/targetjit1.py
new file mode 100644
--- /dev/null
+++ b/pypy/translator/stm/test/targetjit1.py
@@ -0,0 +1,170 @@
+from pypy.module.thread import ll_thread
+from pypy.rlib import jit, rstm
+from pypy.rlib.objectmodel import invoke_around_extcall
+
+
+class Global(object):
+ NUM_THREADS = 4
+ LENGTH = 5000
+ node = None
+
+glob = Global()
+
+class Node(object):
+ def __init__(self, value, next):
+ self.value = value
+ self.next = next
+
+def check_chained_list(node):
+ seen = [0] * (glob.LENGTH+1)
+ seen[-1] = glob.NUM_THREADS
+ errors = glob.LENGTH
+ while node is not None:
+ value = node.value
+ #print value
+ if not (0 <= value < glob.LENGTH):
+ print "node.value out of bounds:", value
+ raise AssertionError
+ value = glob.LENGTH - 1 - value
+ seen[value] += 1
+ if seen[value] > seen[value-1]:
+ errors = min(errors, value)
+ node = node.next
+ if errors < glob.LENGTH:
+ value = errors
+ print "seen[%d] = %d, seen[%d] = %d" % (value-1, seen[value-1],
+ value, seen[value])
+ raise AssertionError
+
+ if seen[glob.LENGTH-1] != glob.NUM_THREADS:
+ print "seen[LENGTH-1] != NUM_THREADS"
+ raise AssertionError
+ print "check ok!"
+
+
+class ThreadRunner(object):
+ arg = None
+
+ def __init__(self, i):
+ self.index = i
+ self.value = 0
+ self.finished_lock = ll_thread.allocate_lock()
+ self.finished_lock.acquire(True)
+
+ def run(self):
+ try:
+ rstm.perform_transaction(ThreadRunner.run_really,
+ ThreadRunner, self)
+ finally:
+ self.finished_lock.release()
+
+ def run_really(self, retry_counter):
+ jitdriver.jit_merge_point(self=self)
+ glob.node = Node(self.value, glob.node)
+ self.value += 1
+ return int(self.value < glob.LENGTH)
+
+jitdriver = jit.JitDriver(greens=[], reds=['self'])
+
+# ____________________________________________________________
+# bah, we are really missing an RPython interface to threads
+
+class Bootstrapper(object):
+ # The following lock is held whenever the fields
+ # 'bootstrapper.w_callable' and 'bootstrapper.args' are in use.
+ lock = None
+ args = None
+
+ @staticmethod
+ def setup():
+ if bootstrapper.lock is None:
+ bootstrapper.lock = ll_thread.allocate_lock()
+
+ @staticmethod
+ def reinit():
+ bootstrapper.lock = None
+ bootstrapper.args = None
+
+ def _freeze_(self):
+ self.reinit()
+ return False
+
+ @staticmethod
+ def bootstrap():
+ # Note that when this runs, we already hold the GIL. This is ensured
+ # by rffi's callback mecanism: we are a callback for the
+ # c_thread_start() external function.
+ ll_thread.gc_thread_start()
+ args = bootstrapper.args
+ bootstrapper.release()
+ # run!
+ try:
+ args.run()
+ finally:
+ ll_thread.gc_thread_die()
+
+ @staticmethod
+ def acquire(args):
+ # If the previous thread didn't start yet, wait until it does.
+ # Note that bootstrapper.lock must be a regular lock, not a NOAUTO
+ # lock, because the GIL must be released while we wait.
+ bootstrapper.lock.acquire(True)
+ bootstrapper.args = args
+
+ @staticmethod
+ def release():
+ # clean up 'bootstrapper' to make it ready for the next
+ # start_new_thread() and release the lock to tell that there
+ # isn't any bootstrapping thread left.
+ bootstrapper.args = None
+ bootstrapper.lock.release()
+
+bootstrapper = Bootstrapper()
+
+def setup_threads():
+ #space.threadlocals.setup_threads(space)
+ bootstrapper.setup()
+ invoke_around_extcall(rstm.before_external_call, rstm.after_external_call,
+ rstm.enter_callback_call, rstm.leave_callback_call)
+
+def start_thread(args):
+ bootstrapper.acquire(args)
+ try:
+ ll_thread.gc_thread_prepare() # (this has no effect any more)
+ ident = ll_thread.start_new_thread(bootstrapper.bootstrap, ())
+ except Exception, e:
+ bootstrapper.release() # normally called by the new thread
+ raise
+ return ident
+
+# __________ Entry point __________
+
+def entry_point(argv):
+ print "hello jit1 world"
+ if len(argv) > 1:
+ glob.NUM_THREADS = int(argv[1])
+ if len(argv) > 2:
+ glob.LENGTH = int(argv[2])
+ #
+ setup_threads()
+ #
+ locks = []
+ for i in range(glob.NUM_THREADS):
+ threadrunner = ThreadRunner(i)
+ start_thread(threadrunner)
+ locks.append(threadrunner.finished_lock)
+ for lock in locks:
+ lock.acquire(True)
+ #
+ check_chained_list(glob.node)
+ #
+ return 0
+
+# _____ Define and setup target ___
+
+def target(*args):
+ return entry_point, None
+
+if __name__ == '__main__':
+ import sys
+ entry_point(sys.argv)
More information about the pypy-commit
mailing list