mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
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).
This commit is contained in:
parent
a143717003
commit
37b8294d62
9 changed files with 67 additions and 39 deletions
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue