diff options
| author | Victor Stinner <victor.stinner@gmail.com> | 2017-10-24 16:53:32 -0700 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-10-24 16:53:32 -0700 | 
| commit | 850a18e03e8f8309bc8c39adc6e7d51a4568cd9a (patch) | |
| tree | 56455b89cb152566734d1fc5de5c70029c3baa70 /Python/thread_pthread.h | |
| parent | 3557b05c5a7dfd7d97ddfd3b79aefd53d25e5132 (diff) | |
| download | cpython-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.h | 55 | 
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)) {  | 
