Issue #8844: Regular and recursive lock acquisitions can now be interrupted
by signals on platforms using pthreads.  Patch by Reid Kleckner.
diff --git a/Python/thread_nt.h b/Python/thread_nt.h
index 9de9e0d..684b545 100644
--- a/Python/thread_nt.h
+++ b/Python/thread_nt.h
@@ -238,10 +238,13 @@
  * and 0 if the lock was not acquired. This means a 0 is returned
  * if the lock has already been acquired by this thread!
  */
-int
-PyThread_acquire_lock_timed(PyThread_type_lock aLock, PY_TIMEOUT_T microseconds)
+PyLockStatus
+PyThread_acquire_lock_timed(PyThread_type_lock aLock,
+                            PY_TIMEOUT_T microseconds, int intr_flag)
 {
-    int success ;
+    /* Fow now, intr_flag does nothing on Windows, and lock acquires are
+     * uninterruptible.  */
+    PyLockStatus success;
     PY_TIMEOUT_T milliseconds;
 
     if (microseconds >= 0) {
@@ -258,7 +261,13 @@
     dprintf(("%ld: PyThread_acquire_lock_timed(%p, %lld) called\n",
              PyThread_get_thread_ident(), aLock, microseconds));
 
-    success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (DWORD) milliseconds) == WAIT_OBJECT_0 ;
+    if (aLock && EnterNonRecursiveMutex((PNRMUTEX)aLock,
+                                        (DWORD)milliseconds) == WAIT_OBJECT_0) {
+        success = PY_LOCK_ACQUIRED;
+    }
+    else {
+        success = PY_LOCK_FAILURE;
+    }
 
     dprintf(("%ld: PyThread_acquire_lock(%p, %lld) -> %d\n",
              PyThread_get_thread_ident(), aLock, microseconds, success));
@@ -268,7 +277,7 @@
 int
 PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
 {
-    return PyThread_acquire_lock_timed(aLock, waitflag ? -1 : 0);
+    return PyThread_acquire_lock_timed(aLock, waitflag ? -1 : 0, 0);
 }
 
 void
diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h
index c007c8b..ffc791c 100644
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -316,16 +316,17 @@
     return (status == -1) ? errno : status;
 }
 
-int
-PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
+PyLockStatus
+PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
+                            int intr_flag)
 {
-    int success;
+    PyLockStatus success;
     sem_t *thelock = (sem_t *)lock;
     int status, error = 0;
     struct timespec ts;
 
-    dprintf(("PyThread_acquire_lock_timed(%p, %lld) called\n",
-             lock, microseconds));
+    dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
+             lock, microseconds, intr_flag));
 
     if (microseconds > 0)
         MICROSECONDS_TO_TIMESPEC(microseconds, ts);
@@ -336,33 +337,38 @@
             status = fix_status(sem_trywait(thelock));
         else
             status = fix_status(sem_wait(thelock));
-    } while (status == EINTR); /* Retry if interrupted by a signal */
+        /* Retry if interrupted by a signal, unless the caller wants to be
+           notified.  */
+    } while (!intr_flag && status == EINTR);
 
-    if (microseconds > 0) {
-        if (status != ETIMEDOUT)
-            CHECK_STATUS("sem_timedwait");
-    }
-    else if (microseconds == 0) {
-        if (status != EAGAIN)
-            CHECK_STATUS("sem_trywait");
-    }
-    else {
-        CHECK_STATUS("sem_wait");
+    /* Don't check the status if we're stopping because of an interrupt.  */
+    if (!(intr_flag && status == EINTR)) {
+        if (microseconds > 0) {
+            if (status != ETIMEDOUT)
+                CHECK_STATUS("sem_timedwait");
+        }
+        else if (microseconds == 0) {
+            if (status != EAGAIN)
+                CHECK_STATUS("sem_trywait");
+        }
+        else {
+            CHECK_STATUS("sem_wait");
+        }
     }
 
-    success = (status == 0) ? 1 : 0;
+    if (status == 0) {
+        success = PY_LOCK_ACQUIRED;
+    } else if (intr_flag && status == EINTR) {
+        success = PY_LOCK_INTR;
+    } else {
+        success = PY_LOCK_FAILURE;
+    }
 
-    dprintf(("PyThread_acquire_lock_timed(%p, %lld) -> %d\n",
-             lock, microseconds, success));
+    dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n",
+             lock, microseconds, intr_flag, success));
     return success;
 }
 
-int
-PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
-{
-    return PyThread_acquire_lock_timed(lock, waitflag ? -1 : 0);
-}
-
 void
 PyThread_release_lock(PyThread_type_lock lock)
 {
@@ -436,21 +442,25 @@
     free((void *)thelock);
 }
 
-int
-PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
+PyLockStatus
+PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
+                            int intr_flag)
 {
-    int success;
+    PyLockStatus success;
     pthread_lock *thelock = (pthread_lock *)lock;
     int status, error = 0;
 
-    dprintf(("PyThread_acquire_lock_timed(%p, %lld) called\n",
-             lock, microseconds));
+    dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
+             lock, microseconds, intr_flag));
 
     status = pthread_mutex_lock( &thelock->mut );
     CHECK_STATUS("pthread_mutex_lock[1]");
-    success = thelock->locked == 0;
 
-    if (!success && microseconds != 0) {
+    if (thelock->locked == 0) {
+        success = PY_LOCK_ACQUIRED;
+    } else if (microseconds == 0) {
+        success = PY_LOCK_FAILURE;
+    } else {
         struct timespec ts;
         if (microseconds > 0)
             MICROSECONDS_TO_TIMESPEC(microseconds, ts);
@@ -458,7 +468,8 @@
 
         /* mut must be locked by me -- part of the condition
          * protocol */
-        while (thelock->locked) {
+        success = PY_LOCK_FAILURE;
+        while (success == PY_LOCK_FAILURE) {
             if (microseconds > 0) {
                 status = pthread_cond_timedwait(
                     &thelock->lock_released,
@@ -473,25 +484,30 @@
                     &thelock->mut);
                 CHECK_STATUS("pthread_cond_wait");
             }
+
+            if (intr_flag && status == 0 && thelock->locked) {
+                /* We were woken up, but didn't get the lock.  We probably received
+                 * a signal.  Return PY_LOCK_INTR to allow the caller to handle
+                 * it and retry.  */
+                success = PY_LOCK_INTR;
+                break;
+            } else if (status == 0 && !thelock->locked) {
+                success = PY_LOCK_ACQUIRED;
+            } else {
+                success = PY_LOCK_FAILURE;
+            }
         }
-        success = (status == 0);
     }
-    if (success) thelock->locked = 1;
+    if (success == PY_LOCK_ACQUIRED) thelock->locked = 1;
     status = pthread_mutex_unlock( &thelock->mut );
     CHECK_STATUS("pthread_mutex_unlock[1]");
 
-    if (error) success = 0;
-    dprintf(("PyThread_acquire_lock_timed(%p, %lld) -> %d\n",
-             lock, microseconds, success));
+    if (error) success = PY_LOCK_FAILURE;
+    dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n",
+             lock, microseconds, intr_flag, success));
     return success;
 }
 
-int
-PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
-{
-    return PyThread_acquire_lock_timed(lock, waitflag ? -1 : 0);
-}
-
 void
 PyThread_release_lock(PyThread_type_lock lock)
 {
@@ -515,6 +531,12 @@
 
 #endif /* USE_SEMAPHORES */
 
+int
+PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
+{
+    return PyThread_acquire_lock_timed(lock, waitflag ? -1 : 0, /*intr_flag=*/0);
+}
+
 /* set the thread stack size.
  * Return 0 if size is valid, -1 if size is invalid,
  * -2 if setting stack size is not supported.