Commits

Armin Rigo committed b2f107c

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.

  • Participants
  • Parent commits 49a1afc
  • Branches stm-thread-2

Comments (0)

Files changed (2)

File pypy/module/thread/os_lock.py

 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
         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,
         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__)
 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__,

File pypy/module/thread/test/test_lock.py

             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