Skip to content

Commit 85b34ed

Browse files
authored
[3.5] bpo-30765: Avoid blocking when PyThread_acquire_lock() is asked not to (GH-2403) (#2419)
* 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 f84ac42)
1 parent 849b062 commit 85b34ed

File tree

2 files changed

+52
-45
lines changed

2 files changed

+52
-45
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Avoid blocking in pthread_mutex_lock() when PyThread_acquire_lock() is asked
2+
not to block.

Python/thread_pthread.h

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -464,61 +464,66 @@ PyLockStatus
464464
PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
465465
int intr_flag)
466466
{
467-
PyLockStatus success;
467+
PyLockStatus success = PY_LOCK_FAILURE;
468468
pthread_lock *thelock = (pthread_lock *)lock;
469469
int status, error = 0;
470470

471471
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
472472
lock, microseconds, intr_flag));
473473

474-
status = pthread_mutex_lock( &thelock->mut );
475-
CHECK_STATUS("pthread_mutex_lock[1]");
476-
477-
if (thelock->locked == 0) {
478-
success = PY_LOCK_ACQUIRED;
479-
} else if (microseconds == 0) {
480-
success = PY_LOCK_FAILURE;
481-
} else {
482-
struct timespec ts;
483-
if (microseconds > 0)
484-
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
485-
/* continue trying until we get the lock */
486-
487-
/* mut must be locked by me -- part of the condition
488-
* protocol */
489-
success = PY_LOCK_FAILURE;
490-
while (success == PY_LOCK_FAILURE) {
491-
if (microseconds > 0) {
492-
status = pthread_cond_timedwait(
493-
&thelock->lock_released,
494-
&thelock->mut, &ts);
495-
if (status == ETIMEDOUT)
474+
if (microseconds == 0) {
475+
status = pthread_mutex_trylock( &thelock->mut );
476+
if (status != EBUSY)
477+
CHECK_STATUS("pthread_mutex_trylock[1]");
478+
}
479+
else {
480+
status = pthread_mutex_lock( &thelock->mut );
481+
CHECK_STATUS("pthread_mutex_lock[1]");
482+
}
483+
if (status == 0) {
484+
if (thelock->locked == 0) {
485+
success = PY_LOCK_ACQUIRED;
486+
}
487+
else if (microseconds != 0) {
488+
struct timespec ts;
489+
if (microseconds > 0)
490+
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
491+
/* continue trying until we get the lock */
492+
493+
/* mut must be locked by me -- part of the condition
494+
* protocol */
495+
while (success == PY_LOCK_FAILURE) {
496+
if (microseconds > 0) {
497+
status = pthread_cond_timedwait(
498+
&thelock->lock_released,
499+
&thelock->mut, &ts);
500+
if (status == ETIMEDOUT)
501+
break;
502+
CHECK_STATUS("pthread_cond_timed_wait");
503+
}
504+
else {
505+
status = pthread_cond_wait(
506+
&thelock->lock_released,
507+
&thelock->mut);
508+
CHECK_STATUS("pthread_cond_wait");
509+
}
510+
511+
if (intr_flag && status == 0 && thelock->locked) {
512+
/* We were woken up, but didn't get the lock. We probably received
513+
* a signal. Return PY_LOCK_INTR to allow the caller to handle
514+
* it and retry. */
515+
success = PY_LOCK_INTR;
496516
break;
497-
CHECK_STATUS("pthread_cond_timed_wait");
498-
}
499-
else {
500-
status = pthread_cond_wait(
501-
&thelock->lock_released,
502-
&thelock->mut);
503-
CHECK_STATUS("pthread_cond_wait");
504-
}
505-
506-
if (intr_flag && status == 0 && thelock->locked) {
507-
/* We were woken up, but didn't get the lock. We probably received
508-
* a signal. Return PY_LOCK_INTR to allow the caller to handle
509-
* it and retry. */
510-
success = PY_LOCK_INTR;
511-
break;
512-
} else if (status == 0 && !thelock->locked) {
513-
success = PY_LOCK_ACQUIRED;
514-
} else {
515-
success = PY_LOCK_FAILURE;
517+
}
518+
else if (status == 0 && !thelock->locked) {
519+
success = PY_LOCK_ACQUIRED;
520+
}
516521
}
517522
}
523+
if (success == PY_LOCK_ACQUIRED) thelock->locked = 1;
524+
status = pthread_mutex_unlock( &thelock->mut );
525+
CHECK_STATUS("pthread_mutex_unlock[1]");
518526
}
519-
if (success == PY_LOCK_ACQUIRED) thelock->locked = 1;
520-
status = pthread_mutex_unlock( &thelock->mut );
521-
CHECK_STATUS("pthread_mutex_unlock[1]");
522527

523528
if (error) success = PY_LOCK_FAILURE;
524529
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n",

0 commit comments

Comments
 (0)