[pypy-commit] pypy py3k: explicitly run the signal handlers on RPY_LOCK_INTR in acquire_timed

pjenvey noreply at buildbot.pypy.org
Wed Mar 13 03:21:27 CET 2013


Author: Philip Jenvey <pjenvey at underboss.org>
Branch: py3k
Changeset: r62327:e60b56f3fc81
Date: 2013-03-12 19:19 -0700
http://bitbucket.org/pypy/pypy/changeset/e60b56f3fc81/

Log:	explicitly run the signal handlers on RPY_LOCK_INTR in acquire_timed

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
@@ -53,12 +53,14 @@
     return microseconds
 
 
-def acquire_timed(lock, microseconds):
+def acquire_timed(space, lock, microseconds):
     """Helper to acquire an interruptible lock with a timeout."""
     endtime = (time.time() * 1e6) + microseconds
     while True:
         result = lock.acquire_timed(microseconds)
         if result == RPY_LOCK_INTR:
+            # Run signal handlers if we were interrupted
+            space.getexecutioncontext().checksignals()
             if microseconds >= 0:
                 microseconds = r_longlong(endtime - (time.time() * 1e6))
                 # Check for negative values, since those mean block
@@ -89,7 +91,7 @@
 and the return value reflects whether the lock is acquired.
 The blocking operation is interruptible."""
         microseconds = parse_acquire_args(space, blocking, timeout)
-        result = acquire_timed(self.lock, microseconds)
+        result = acquire_timed(space, self.lock, microseconds)
         return space.newbool(result == RPY_LOCK_ACQUIRED)
 
     def descr_lock_release(self, space):
@@ -201,7 +203,7 @@
         if self.rlock_count > 0 or not self.lock.acquire(False):
             if not blocking:
                 return space.w_False
-            r = acquire_timed(self.lock, microseconds)
+            r = acquire_timed(space, self.lock, microseconds)
             r = (r == RPY_LOCK_ACQUIRED)
         if r:
             assert self.rlock_count == 0
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
@@ -1,4 +1,5 @@
 from __future__ import with_statement
+import sys
 from pypy.module.thread.test.support import GenericTestThread
 from rpython.translator.c.test.test_genc import compile
 
@@ -149,6 +150,9 @@
 
 class AppTestLockSignals(GenericTestThread):
 
+    def setup_class(cls):
+        cls.w_using_pthread_cond = cls.space.wrap(sys.platform == 'freebsd6')
+
     def w_acquire_retries_on_intr(self, lock):
         import _thread, os, signal, time
         self.sig_recvd = False
@@ -186,3 +190,73 @@
     def test_rlock_acquire_retries_on_intr(self):
         import _thread
         self.acquire_retries_on_intr(_thread.RLock())
+
+    def w_alarm_interrupt(self, sig, frame):
+        raise KeyboardInterrupt
+
+    def test_lock_acquire_interruption(self):
+        if self.using_pthread_cond:
+            skip('POSIX condition variables cannot be interrupted')
+        import _thread, signal, time
+        # Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck
+        # in a deadlock.
+        # XXX this test can fail when the legacy (non-semaphore) implementation
+        # of locks is used in thread_pthread.h, see issue #11223.
+        oldalrm = signal.signal(signal.SIGALRM, self.alarm_interrupt)
+        try:
+            lock = _thread.allocate_lock()
+            lock.acquire()
+            signal.alarm(1)
+            t1 = time.time()
+            # XXX: raises doesn't work here?
+            #raises(KeyboardInterrupt, lock.acquire, timeout=5)
+            try:
+                lock.acquire(timeout=5)
+            except KeyboardInterrupt:
+                pass
+            else:
+                assert False, 'Expected KeyboardInterrupt'
+            dt = time.time() - t1
+            # Checking that KeyboardInterrupt was raised is not sufficient.
+            # We want to assert that lock.acquire() was interrupted because
+            # of the signal, not that the signal handler was called immediately
+            # after timeout return of lock.acquire() (which can fool assertRaises).
+            assert dt < 3.0
+        finally:
+            signal.signal(signal.SIGALRM, oldalrm)
+
+    def test_rlock_acquire_interruption(self):
+        if self.using_pthread_cond:
+            skip('POSIX condition variables cannot be interrupted')
+        import _thread, signal, time
+        # Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck
+        # in a deadlock.
+        # XXX this test can fail when the legacy (non-semaphore) implementation
+        # of locks is used in thread_pthread.h, see issue #11223.
+        oldalrm = signal.signal(signal.SIGALRM, self.alarm_interrupt)
+        try:
+            rlock = _thread.RLock()
+            # For reentrant locks, the initial acquisition must be in another
+            # thread.
+            def other_thread():
+                rlock.acquire()
+            _thread.start_new_thread(other_thread, ())
+            # Wait until we can't acquire it without blocking...
+            while rlock.acquire(blocking=False):
+                rlock.release()
+                time.sleep(0.01)
+            signal.alarm(1)
+            t1 = time.time()
+            #raises(KeyboardInterrupt, rlock.acquire, timeout=5)
+            try:
+                rlock.acquire(timeout=5)
+            except KeyboardInterrupt:
+                pass
+            else:
+                assert False, 'Expected KeyboardInterrupt'
+            dt = time.time() - t1
+            # See rationale above in test_lock_acquire_interruption
+            assert dt < 3.0
+        finally:
+            signal.signal(signal.SIGALRM, oldalrm)
+


More information about the pypy-commit mailing list