[pypy-commit] pypy stm-thread: Support for thread.atomic also for pypy without stm.

arigo noreply at buildbot.pypy.org
Sat May 12 20:30:20 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-thread
Changeset: r55064:149d8955d1c8
Date: 2012-05-12 20:29 +0200
http://bitbucket.org/pypy/pypy/changeset/149d8955d1c8/

Log:	Support for thread.atomic also for pypy without stm.

diff --git a/pypy/module/thread/atomic.py b/pypy/module/thread/atomic.py
--- a/pypy/module/thread/atomic.py
+++ b/pypy/module/thread/atomic.py
@@ -1,18 +1,26 @@
 from pypy.interpreter.error import OperationError
-from pypy.rlib.rstm import increment_atomic, decrement_atomic, is_atomic
 from pypy.module.thread.error import wrap_thread_error
 
 def atomic_enter(space):
-    if not space.config.translation.stm:
-        raise wrap_thread_error(space,
-            "atomic.__enter__(): STM not available")
-    increment_atomic()
+    if space.config.translation.stm:
+        from pypy.rlib.rstm import increment_atomic
+        increment_atomic()
+    else:
+        giltl = space.threadlocals
+        giltl.is_atomic += 1
+        space.threadlocals.set_gil_releasing_calls()
 
 def atomic_exit(space, w_ignored1=None, w_ignored2=None, w_ignored3=None):
-    if not space.config.translation.stm:
-        raise wrap_thread_error(space,
-            "atomic.__exit__(): STM not available")
-    if not is_atomic():
-        raise wrap_thread_error(space,
-            "atomic.__exit__(): more exits than enters")
-    decrement_atomic()
+    if space.config.translation.stm:
+        from pypy.rlib.rstm import decrement_atomic, is_atomic
+        if is_atomic():
+            decrement_atomic()
+            return
+    else:
+        giltl = space.threadlocals
+        if giltl.is_atomic > 0:
+            giltl.is_atomic -= 1
+            space.threadlocals.set_gil_releasing_calls()
+            return
+    raise wrap_thread_error(space,
+        "atomic.__exit__(): more exits than enters")
diff --git a/pypy/module/thread/gil.py b/pypy/module/thread/gil.py
--- a/pypy/module/thread/gil.py
+++ b/pypy/module/thread/gil.py
@@ -11,12 +11,13 @@
 from pypy.module.thread.error import wrap_thread_error
 from pypy.interpreter.executioncontext import PeriodicAsyncAction
 from pypy.module.thread.threadlocals import OSThreadLocals
-from pypy.rlib.objectmodel import invoke_around_extcall
+from pypy.rlib.objectmodel import invoke_around_extcall, has_around_extcall
 from pypy.rlib.rposix import get_errno, set_errno
 
 class GILThreadLocals(OSThreadLocals):
     """A version of OSThreadLocals that enforces a GIL."""
     gil_ready = False
+    is_atomic = 0
     _immutable_fields_ = ['gil_ready?']
 
     def initialize(self, space):
@@ -42,9 +43,20 @@
         # test_lock_again after the global state was cleared by
         # test_compile_lock.  As a workaround, we repatch these global
         # fields systematically.
-        invoke_around_extcall(before_external_call, after_external_call)
+        self.set_gil_releasing_calls()
         return result
 
+    def set_gil_releasing_calls(self):
+        if self.is_atomic == 0:
+            # not running atomically so far, so we register the
+            # functions that will be called around external calls
+            invoke_around_extcall(before_external_call, after_external_call)
+        else:
+            # running atomically: we have the GIL here, so if we
+            # just un-register the functions, we won't release the GIL
+            # any more.
+            invoke_around_extcall(None, None)
+
     def reinit_threads(self, space):
         if self.gil_ready:
             self.gil_ready = False
@@ -109,9 +121,10 @@
     # explicitly release the gil, in a way that tries to give more
     # priority to other threads (as opposed to continuing to run in
     # the same thread).
-    if thread.gil_yield_thread():
-        thread.gc_thread_run()
-        spacestate.after_thread_switch()
+    if has_around_extcall():
+        if thread.gil_yield_thread():
+            thread.gc_thread_run()
+            spacestate.after_thread_switch()
 do_yield_thread._gctransformer_hint_close_stack_ = True
 do_yield_thread._dont_reach_me_in_del_ = True
 do_yield_thread._dont_inline_ = True
diff --git a/pypy/module/thread/test/test_atomic.py b/pypy/module/thread/test/test_atomic.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/thread/test/test_atomic.py
@@ -0,0 +1,15 @@
+from __future__ import with_statement
+from pypy.module.thread.test.support import GenericTestThread
+
+
+class AppTestAtomic(GenericTestThread):
+
+    def test_simple(self):
+        import thread
+        with thread.atomic:
+            pass
+        try:
+            with thread.atomic:
+                raise ValueError
+        except ValueError:
+            pass
diff --git a/pypy/rlib/objectmodel.py b/pypy/rlib/objectmodel.py
--- a/pypy/rlib/objectmodel.py
+++ b/pypy/rlib/objectmodel.py
@@ -489,8 +489,8 @@
     # the 'aroundstate' contains regular function and not ll pointers to them,
     # but let's call llhelper() anyway to force their annotation
     from pypy.rpython.annlowlevel import llhelper
-    llhelper(rffi.AroundFnPtr, before)
-    llhelper(rffi.AroundFnPtr, after)
+    if before is not None: llhelper(rffi.AroundFnPtr, before)
+    if after  is not None: llhelper(rffi.AroundFnPtr, after)
     # do the same thing about enter/leave_callback
     if enter_callback is not None:
         rffi.aroundstate.enter_callback = enter_callback
@@ -503,6 +503,10 @@
     from pypy.rpython.lltypesystem import rffi
     return rffi.stackcounter.stacks_counter > 1
 
+def has_around_extcall():
+    from pypy.rpython.lltypesystem import rffi
+    return rffi.aroundstate.before is not None
+
 
 class UnboxedValue(object):
     """A mixin class to use for classes that have exactly one field which


More information about the pypy-commit mailing list