[3.6] bpo-30765: Avoid blocking when PyThread_acquire_lock() is asked not to (GH-2403) (#2418)

* bpo-30765: Avoid blocking when PyThread_acquire_lock() is asked not to lock

This is especially important if PyThread_acquire_lock() is called reentrantly
(for example from a signal handler).

* Update 2017-06-26-14-29-50.bpo-30765.Q5iBmf.rst

* Avoid core logic when taking the mutex failed
(cherry picked from commit f84ac420c2)
This commit is contained in:
Antoine Pitrou 2017-06-26 21:57:28 +02:00 committed by GitHub
parent 64a0c266e7
commit 55ab604e3f
2 changed files with 50 additions and 43 deletions

View file

@ -0,0 +1,2 @@
Avoid blocking in pthread_mutex_lock() when PyThread_acquire_lock() is asked
not to block.

View file

@ -466,21 +466,27 @@ PyLockStatus
PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds, PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
int intr_flag) int intr_flag)
{ {
PyLockStatus success; PyLockStatus success = PY_LOCK_FAILURE;
pthread_lock *thelock = (pthread_lock *)lock; pthread_lock *thelock = (pthread_lock *)lock;
int status, error = 0; int status, error = 0;
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n", dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
lock, microseconds, intr_flag)); lock, microseconds, intr_flag));
if (microseconds == 0) {
status = pthread_mutex_trylock( &thelock->mut );
if (status != EBUSY)
CHECK_STATUS_PTHREAD("pthread_mutex_trylock[1]");
}
else {
status = pthread_mutex_lock( &thelock->mut ); status = pthread_mutex_lock( &thelock->mut );
CHECK_STATUS_PTHREAD("pthread_mutex_lock[1]"); CHECK_STATUS_PTHREAD("pthread_mutex_lock[1]");
}
if (status == 0) {
if (thelock->locked == 0) { if (thelock->locked == 0) {
success = PY_LOCK_ACQUIRED; success = PY_LOCK_ACQUIRED;
} else if (microseconds == 0) { }
success = PY_LOCK_FAILURE; else if (microseconds != 0) {
} else {
struct timespec ts; struct timespec ts;
if (microseconds > 0) if (microseconds > 0)
MICROSECONDS_TO_TIMESPEC(microseconds, ts); MICROSECONDS_TO_TIMESPEC(microseconds, ts);
@ -488,7 +494,6 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
/* mut must be locked by me -- part of the condition /* mut must be locked by me -- part of the condition
* protocol */ * protocol */
success = PY_LOCK_FAILURE;
while (success == PY_LOCK_FAILURE) { while (success == PY_LOCK_FAILURE) {
if (microseconds > 0) { if (microseconds > 0) {
status = pthread_cond_timedwait( status = pthread_cond_timedwait(
@ -511,16 +516,16 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
* it and retry. */ * it and retry. */
success = PY_LOCK_INTR; success = PY_LOCK_INTR;
break; break;
} else if (status == 0 && !thelock->locked) { }
else if (status == 0 && !thelock->locked) {
success = PY_LOCK_ACQUIRED; success = PY_LOCK_ACQUIRED;
} else {
success = PY_LOCK_FAILURE;
} }
} }
} }
if (success == PY_LOCK_ACQUIRED) thelock->locked = 1; if (success == PY_LOCK_ACQUIRED) thelock->locked = 1;
status = pthread_mutex_unlock( &thelock->mut ); status = pthread_mutex_unlock( &thelock->mut );
CHECK_STATUS_PTHREAD("pthread_mutex_unlock[1]"); CHECK_STATUS_PTHREAD("pthread_mutex_unlock[1]");
}
if (error) success = PY_LOCK_FAILURE; if (error) success = PY_LOCK_FAILURE;
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n", dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n",