aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python/thread_pthread.h
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2021-09-30 10:16:51 +0200
committerGitHub <noreply@github.com>2021-09-30 10:16:51 +0200
commit37b8294d6295ca12553fd7c98778be71d24f4b24 (patch)
tree8d9d17fb410224326246dbb25d762feb9aae6053 /Python/thread_pthread.h
parenta1437170039dc2c07e6040d3a8ba8d91434b730d (diff)
downloadcpython-37b8294d6295ca12553fd7c98778be71d24f4b24.tar.gz
cpython-37b8294d6295ca12553fd7c98778be71d24f4b24.zip
bpo-41710: PyThread_acquire_lock_timed() clamps the timout (GH-28643)
PyThread_acquire_lock_timed() now clamps the timeout into the [_PyTime_MIN; _PyTime_MAX] range (_PyTime_t type) if it is too large, rather than calling Py_FatalError() which aborts the process. PyThread_acquire_lock_timed() no longer uses MICROSECONDS_TO_TIMESPEC() to compute sem_timedwait() argument, but _PyTime_GetSystemClock() and _PyTime_AsTimespec_truncate(). Fix _thread.TIMEOUT_MAX value on Windows: the maximum timeout is 0x7FFFFFFF milliseconds (around 24.9 days), not 0xFFFFFFFF milliseconds (around 49.7 days). Set PY_TIMEOUT_MAX to 0x7FFFFFFF milliseconds, rather than 0xFFFFFFFF milliseconds. Fix PY_TIMEOUT_MAX overflow test: replace (us >= PY_TIMEOUT_MAX) with (us > PY_TIMEOUT_MAX).
Diffstat (limited to 'Python/thread_pthread.h')
-rw-r--r--Python/thread_pthread.h63
1 files changed, 34 insertions, 29 deletions
diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h
index 7f04151ca91..3815ffae20c 100644
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -433,33 +433,47 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
PyLockStatus success;
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 > PY_TIMEOUT_MAX) {
- Py_FatalError("Timeout larger than PY_TIMEOUT_MAX");
+ _PyTime_t timeout;
+ if (microseconds >= 0) {
+ _PyTime_t ns;
+ if (microseconds <= _PyTime_MAX / 1000) {
+ ns = microseconds * 1000;
+ }
+ else {
+ // bpo-41710: PyThread_acquire_lock_timed() cannot report timeout
+ // overflow to the caller, so clamp the timeout to
+ // [_PyTime_MIN, _PyTime_MAX].
+ //
+ // _PyTime_MAX nanoseconds is around 292.3 years.
+ //
+ // _thread.Lock.acquire() and _thread.RLock.acquire() raise an
+ // OverflowError if microseconds is greater than PY_TIMEOUT_MAX.
+ ns = _PyTime_MAX;
+ }
+ timeout = _PyTime_FromNanoseconds(ns);
+ }
+ else {
+ timeout = _PyTime_FromNanoseconds(-1);
}
- if (microseconds > 0) {
- MICROSECONDS_TO_TIMESPEC(microseconds, ts);
-
- 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;
- }
+ _PyTime_t deadline = 0;
+ if (timeout > 0 && !intr_flag) {
+ deadline = _PyTime_GetMonotonicClock() + timeout;
}
while (1) {
- if (microseconds > 0) {
+ if (timeout > 0) {
+ _PyTime_t t = _PyTime_GetSystemClock() + timeout;
+ struct timespec ts;
+ _PyTime_AsTimespec_clamp(t, &ts);
status = fix_status(sem_timedwait(thelock, &ts));
}
- else if (microseconds == 0) {
+ else if (timeout == 0) {
status = fix_status(sem_trywait(thelock));
}
else {
@@ -472,32 +486,23 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
break;
}
- if (microseconds > 0) {
+ if (timeout > 0) {
/* wait interrupted by a signal (EINTR): recompute the timeout */
- _PyTime_t dt = deadline - _PyTime_GetMonotonicClock();
- if (dt < 0) {
+ _PyTime_t timeout = deadline - _PyTime_GetMonotonicClock();
+ if (timeout < 0) {
status = ETIMEDOUT;
break;
}
- else if (dt > 0) {
- _PyTime_t realtime_deadline = _PyTime_GetSystemClock() + dt;
- _PyTime_AsTimespec_clamp(realtime_deadline, &ts);
- /* 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)) {
- if (microseconds > 0) {
+ if (timeout > 0) {
if (status != ETIMEDOUT)
CHECK_STATUS("sem_timedwait");
}
- else if (microseconds == 0) {
+ else if (timeout == 0) {
if (status != EAGAIN)
CHECK_STATUS("sem_trywait");
}