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
This commit is contained in:
parent
63f54c6893
commit
f84ac420c2
|
@ -0,0 +1,2 @@
|
||||||
|
Avoid blocking in pthread_mutex_lock() when PyThread_acquire_lock() is asked
|
||||||
|
not to block.
|
|
@ -466,61 +466,66 @@ 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));
|
||||||
|
|
||||||
status = pthread_mutex_lock( &thelock->mut );
|
if (microseconds == 0) {
|
||||||
CHECK_STATUS_PTHREAD("pthread_mutex_lock[1]");
|
status = pthread_mutex_trylock( &thelock->mut );
|
||||||
|
if (status != EBUSY)
|
||||||
|
CHECK_STATUS_PTHREAD("pthread_mutex_trylock[1]");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
status = pthread_mutex_lock( &thelock->mut );
|
||||||
|
CHECK_STATUS_PTHREAD("pthread_mutex_lock[1]");
|
||||||
|
}
|
||||||
|
if (status == 0) {
|
||||||
|
if (thelock->locked == 0) {
|
||||||
|
success = PY_LOCK_ACQUIRED;
|
||||||
|
}
|
||||||
|
else if (microseconds != 0) {
|
||||||
|
struct timespec ts;
|
||||||
|
if (microseconds > 0)
|
||||||
|
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
|
||||||
|
/* continue trying until we get the lock */
|
||||||
|
|
||||||
if (thelock->locked == 0) {
|
/* mut must be locked by me -- part of the condition
|
||||||
success = PY_LOCK_ACQUIRED;
|
* protocol */
|
||||||
} else if (microseconds == 0) {
|
while (success == PY_LOCK_FAILURE) {
|
||||||
success = PY_LOCK_FAILURE;
|
if (microseconds > 0) {
|
||||||
} else {
|
status = pthread_cond_timedwait(
|
||||||
struct timespec ts;
|
&thelock->lock_released,
|
||||||
if (microseconds > 0)
|
&thelock->mut, &ts);
|
||||||
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
|
if (status == ETIMEDOUT)
|
||||||
/* continue trying until we get the lock */
|
break;
|
||||||
|
CHECK_STATUS_PTHREAD("pthread_cond_timed_wait");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
status = pthread_cond_wait(
|
||||||
|
&thelock->lock_released,
|
||||||
|
&thelock->mut);
|
||||||
|
CHECK_STATUS_PTHREAD("pthread_cond_wait");
|
||||||
|
}
|
||||||
|
|
||||||
/* mut must be locked by me -- part of the condition
|
if (intr_flag && status == 0 && thelock->locked) {
|
||||||
* protocol */
|
/* We were woken up, but didn't get the lock. We probably received
|
||||||
success = PY_LOCK_FAILURE;
|
* a signal. Return PY_LOCK_INTR to allow the caller to handle
|
||||||
while (success == PY_LOCK_FAILURE) {
|
* it and retry. */
|
||||||
if (microseconds > 0) {
|
success = PY_LOCK_INTR;
|
||||||
status = pthread_cond_timedwait(
|
|
||||||
&thelock->lock_released,
|
|
||||||
&thelock->mut, &ts);
|
|
||||||
if (status == ETIMEDOUT)
|
|
||||||
break;
|
break;
|
||||||
CHECK_STATUS_PTHREAD("pthread_cond_timed_wait");
|
}
|
||||||
}
|
else if (status == 0 && !thelock->locked) {
|
||||||
else {
|
success = PY_LOCK_ACQUIRED;
|
||||||
status = pthread_cond_wait(
|
}
|
||||||
&thelock->lock_released,
|
|
||||||
&thelock->mut);
|
|
||||||
CHECK_STATUS_PTHREAD("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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (success == PY_LOCK_ACQUIRED) thelock->locked = 1;
|
||||||
|
status = pthread_mutex_unlock( &thelock->mut );
|
||||||
|
CHECK_STATUS_PTHREAD("pthread_mutex_unlock[1]");
|
||||||
}
|
}
|
||||||
if (success == PY_LOCK_ACQUIRED) thelock->locked = 1;
|
|
||||||
status = pthread_mutex_unlock( &thelock->mut );
|
|
||||||
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",
|
||||||
|
|
Loading…
Reference in New Issue