[pypy-commit] pypy stm-thread-2: A PyPy extension: add to thread locks a method acquire_interruptible().

arigo noreply at buildbot.pypy.org
Thu Jan 31 18:19:49 CET 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-thread-2
Changeset: r60784:b2f107c5c3c5
Date: 2013-01-31 18:14 +0100
http://bitbucket.org/pypy/pypy/changeset/b2f107c5c3c5/

Log:	A PyPy extension: add to thread locks a method
	acquire_interruptible(). This is like acquire() but handles signals,
	similarly to Python 3's acquire() method.

diff --git a/pypy/module/thread/os_lock.py b/pypy/module/thread/os_lock.py
--- a/pypy/module/thread/os_lock.py
+++ b/pypy/module/thread/os_lock.py
@@ -2,6 +2,7 @@
 Python locks, based on true threading locks provided by the OS.
 """
 
+import sys
 from rpython.rlib import rthread as thread
 from pypy.module.thread.error import wrap_thread_error
 from pypy.interpreter.baseobjspace import Wrappable
@@ -51,6 +52,23 @@
         result = mylock.acquire(bool(waitflag))
         return space.newbool(result)
 
+    def descr_lock_acquire_interruptible(self, space):
+        """Lock the lock.  Unlike acquire(), this is always blocking
+but may be interrupted: signal handlers are still called, and may
+raise (e.g. a Ctrl-C will correctly raise KeyboardInterrupt).
+
+This is an extension only available on PyPy."""
+        mylock = self.lock
+        while True:
+            result = mylock.acquire_timed(-1)
+            if result == 1:      # RPY_LOCK_ACQUIRED
+                return
+            assert result == 2   # RPY_LOCK_INTR
+            space.getexecutioncontext().checksignals()
+        # then retry, if the signal handler did not raise
+    assert sys.platform != 'win32', (
+        "acquire_interruptible: fix acquire_timed() on Windows")
+
     def descr_lock_release(self, space):
         """Release the lock, allowing another thread that is blocked waiting for
 the lock to acquire the lock.  The lock must be in the locked state,
@@ -83,6 +101,7 @@
         self.descr_lock_release(self.space)
 
 descr_acquire = interp2app(Lock.descr_lock_acquire)
+descr_acquire_interruptible = interp2app(Lock.descr_lock_acquire_interruptible)
 descr_release = interp2app(Lock.descr_lock_release)
 descr_locked  = interp2app(Lock.descr_lock_locked)
 descr__enter__ = interp2app(Lock.descr__enter__)
@@ -102,6 +121,7 @@
 unlock it.  A thread attempting to lock a lock that it has already locked
 will block until another thread unlocks it.  Deadlocks may ensue.""",
     acquire = descr_acquire,
+    acquire_interruptible = descr_acquire_interruptible,
     release = descr_release,
     locked  = descr_locked,
     __enter__ = descr__enter__,
diff --git a/pypy/module/thread/test/test_lock.py b/pypy/module/thread/test/test_lock.py
--- a/pypy/module/thread/test/test_lock.py
+++ b/pypy/module/thread/test/test_lock.py
@@ -46,6 +46,34 @@
             assert feedback == [42]
         assert lock.locked() is False
 
+    def test_acquire_interruptible(self):
+        import thread, signal, posix
+        ticks = []
+        def tick(*args):
+            ticks.append(1)
+            if len(ticks) == 3:
+                raise OverflowError
+        prev_handler = signal.signal(signal.SIGUSR1, tick)
+        #
+        lock = thread.allocate_lock()
+        lock.acquire()
+        def f():
+            self.busywait(0.25)
+            posix.kill(posix.getpid(), signal.SIGUSR1)
+            self.busywait(0.25)
+            posix.kill(posix.getpid(), signal.SIGUSR1)
+            self.busywait(0.25)
+            posix.kill(posix.getpid(), signal.SIGUSR1)
+        thread.start_new_thread(f, ())
+        try:
+            lock.acquire_interruptible()
+            raise AssertionError("should not reach here")
+        except OverflowError:
+            pass
+        assert ticks == [1, 1, 1]
+        signal.signal(signal.SIGUSR1, prev_handler)
+
+
 def test_compile_lock():
     from rpython.rlib import rgc
     from rpython.rlib.rthread import allocate_lock


More information about the pypy-commit mailing list