Skip to content

Commit f84ac42

Browse files
authored
bpo-30765: Avoid blocking when PyThread_acquire_lock() is asked not to (#2403)
* 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
1 parent 63f54c6 commit f84ac42

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
@@ -466,61 +466,66 @@ PyLockStatus
466466
PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
467467
int intr_flag)
468468
{
469-
PyLockStatus success;
469+
PyLockStatus success = PY_LOCK_FAILURE;
470470
pthread_lock *thelock = (pthread_lock *)lock;
471471
int status, error = 0;
472472

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

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

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

0 commit comments

Comments
 (0)