diff options
author | Antoine Pitrou <pitrou@free.fr> | 2017-06-26 20:41:07 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-26 20:41:07 +0200 |
commit | f84ac420c2af98339678744953869cad3c253281 (patch) | |
tree | c43cedbdbae29574ee72815ffe348c33bbfedf68 /Python/thread_pthread.h | |
parent | 63f54c68936d648c70ca411661e4208329edcf26 (diff) | |
download | cpython-git-f84ac420c2af98339678744953869cad3c253281.tar.gz |
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
Diffstat (limited to 'Python/thread_pthread.h')
-rw-r--r-- | Python/thread_pthread.h | 95 |
1 files changed, 50 insertions, 45 deletions
diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index b95840ce2d..399d28c751 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -466,61 +466,66 @@ PyLockStatus PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds, int intr_flag) { - PyLockStatus success; + PyLockStatus success = PY_LOCK_FAILURE; pthread_lock *thelock = (pthread_lock *)lock; int status, error = 0; dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n", lock, microseconds, intr_flag)); - status = pthread_mutex_lock( &thelock->mut ); - CHECK_STATUS_PTHREAD("pthread_mutex_lock[1]"); - - 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); - /* continue trying until we get the lock */ - - /* mut must be locked by me -- part of the condition - * protocol */ - success = PY_LOCK_FAILURE; - while (success == PY_LOCK_FAILURE) { - if (microseconds > 0) { - status = pthread_cond_timedwait( - &thelock->lock_released, - &thelock->mut, &ts); - if (status == ETIMEDOUT) + 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 ); + 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 */ + + /* mut must be locked by me -- part of the condition + * protocol */ + while (success == PY_LOCK_FAILURE) { + if (microseconds > 0) { + status = pthread_cond_timedwait( + &thelock->lock_released, + &thelock->mut, &ts); + if (status == ETIMEDOUT) + break; + CHECK_STATUS_PTHREAD("pthread_cond_timed_wait"); + } + else { + 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; - CHECK_STATUS_PTHREAD("pthread_cond_timed_wait"); - } - else { - 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; + } + else if (status == 0 && !thelock->locked) { + success = PY_LOCK_ACQUIRED; + } } } + 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; dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) -> %d\n", |