summaryrefslogtreecommitdiff
path: root/Python/thread_pthread.h
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2017-10-24 16:53:32 -0700
committerGitHub <noreply@github.com>2017-10-24 16:53:32 -0700
commit850a18e03e8f8309bc8c39adc6e7d51a4568cd9a (patch)
tree56455b89cb152566734d1fc5de5c70029c3baa70 /Python/thread_pthread.h
parent3557b05c5a7dfd7d97ddfd3b79aefd53d25e5132 (diff)
downloadcpython-git-850a18e03e8f8309bc8c39adc6e7d51a4568cd9a.tar.gz
bpo-30768: Recompute timeout on interrupted lock (GH-4103)
Fix the pthread+semaphore implementation of PyThread_acquire_lock_timed() when called with timeout > 0 and intr_flag=0: recompute the timeout if sem_timedwait() is interrupted by a signal (EINTR). See also the PEP 475. The pthread implementation of PyThread_acquire_lock() now fails with a fatal error if the timeout is larger than PY_TIMEOUT_MAX, as done in the Windows implementation. The check prevents any risk of overflow in PyThread_acquire_lock(). Add also PY_DWORD_MAX constant.
Diffstat (limited to 'Python/thread_pthread.h')
-rw-r--r--Python/thread_pthread.h55
1 files changed, 49 insertions, 6 deletions
diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h
index c5b7f3256f..13cffa3bf3 100644
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -318,23 +318,66 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
sem_t *thelock = (sem_t *)lock;
int status, error = 0;
struct timespec ts;
+ _PyTime_t deadline = 0;
(void) error; /* silence unused-but-set-variable warning */
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
lock, microseconds, intr_flag));
- if (microseconds > 0)
+ if (microseconds > PY_TIMEOUT_MAX) {
+ Py_FatalError("Timeout larger than PY_TIMEOUT_MAX");
+ }
+
+ if (microseconds > 0) {
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
- do {
- if (microseconds > 0)
+
+ if (!intr_flag) {
+ /* cannot overflow thanks to (microseconds > PY_TIMEOUT_MAX)
+ check done above */
+ _PyTime_t timeout = _PyTime_FromNanoseconds(microseconds * 1000);
+ deadline = _PyTime_GetMonotonicClock() + timeout;
+ }
+ }
+
+ while (1) {
+ if (microseconds > 0) {
status = fix_status(sem_timedwait(thelock, &ts));
- else if (microseconds == 0)
+ }
+ else if (microseconds == 0) {
status = fix_status(sem_trywait(thelock));
- else
+ }
+ else {
status = fix_status(sem_wait(thelock));
+ }
+
/* Retry if interrupted by a signal, unless the caller wants to be
notified. */
- } while (!intr_flag && status == EINTR);
+ if (intr_flag || status != EINTR) {
+ break;
+ }
+
+ if (microseconds > 0) {
+ /* wait interrupted by a signal (EINTR): recompute the timeout */
+ _PyTime_t dt = deadline - _PyTime_GetMonotonicClock();
+ if (dt < 0) {
+ status = ETIMEDOUT;
+ break;
+ }
+ else if (dt > 0) {
+ _PyTime_t realtime_deadline = _PyTime_GetSystemClock() + dt;
+ if (_PyTime_AsTimespec(realtime_deadline, &ts) < 0) {
+ /* Cannot occur thanks to (microseconds > PY_TIMEOUT_MAX)
+ check done above */
+ Py_UNREACHABLE();
+ }
+ /* no need to update microseconds value, the code only care
+ if (microseconds > 0 or (microseconds == 0). */
+ }
+ else {
+ microseconds = 0;
+ }
+ }
+ }
/* Don't check the status if we're stopping because of an interrupt. */
if (!(intr_flag && status == EINTR)) {