bpo-41710: PyThread_acquire_lock_timed() uses sem_clockwait() (GH-28662)

On Unix, if the sem_clockwait() function is available in the C
library (glibc 2.30 and newer), the threading.Lock.acquire() method
now uses the monotonic clock (time.CLOCK_MONOTONIC) for the timeout,
rather than using the system clock (time.CLOCK_REALTIME), to not be
affected by system clock changes.

configure now checks if the sem_clockwait() function is available.
This commit is contained in:
Victor Stinner 2021-10-01 09:55:28 +02:00 committed by GitHub
parent 3e1c5d989a
commit 1ee0f94d16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 216 additions and 128 deletions

View file

@ -92,7 +92,7 @@
* mutexes and condition variables:
*/
#if (defined(_POSIX_SEMAPHORES) && !defined(HAVE_BROKEN_POSIX_SEMAPHORES) && \
defined(HAVE_SEM_TIMEDWAIT))
(defined(HAVE_SEM_TIMEDWAIT) || defined(HAVE_SEM_CLOCKWAIT)))
# define USE_SEMAPHORES
#else
# undef USE_SEMAPHORES
@ -461,17 +461,34 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
timeout = _PyTime_FromNanoseconds(-1);
}
#ifdef HAVE_SEM_CLOCKWAIT
struct timespec abs_timeout;
// Local scope for deadline
{
_PyTime_t deadline = _PyTime_GetMonotonicClock() + timeout;
_PyTime_AsTimespec_clamp(deadline, &abs_timeout);
}
#else
_PyTime_t deadline = 0;
if (timeout > 0 && !intr_flag) {
if (timeout > 0
&& !intr_flag
)
{
deadline = _PyTime_GetMonotonicClock() + timeout;
}
#endif
while (1) {
if (timeout > 0) {
_PyTime_t t = _PyTime_GetSystemClock() + timeout;
#ifdef HAVE_SEM_CLOCKWAIT
status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC,
&abs_timeout));
#else
_PyTime_t abs_timeout = _PyTime_GetSystemClock() + timeout;
struct timespec ts;
_PyTime_AsTimespec_clamp(t, &ts);
_PyTime_AsTimespec_clamp(abs_timeout, &ts);
status = fix_status(sem_timedwait(thelock, &ts));
#endif
}
else if (timeout == 0) {
status = fix_status(sem_trywait(thelock));
@ -486,6 +503,9 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
break;
}
// sem_clockwait() uses an absolute timeout, there is no need
// to recompute the relative timeout.
#ifndef HAVE_SEM_CLOCKWAIT
if (timeout > 0) {
/* wait interrupted by a signal (EINTR): recompute the timeout */
_PyTime_t timeout = deadline - _PyTime_GetMonotonicClock();
@ -494,17 +514,24 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
break;
}
}
#endif
}
/* Don't check the status if we're stopping because of an interrupt. */
if (!(intr_flag && status == EINTR)) {
if (timeout > 0) {
if (status != ETIMEDOUT)
if (status != ETIMEDOUT) {
#ifdef HAVE_SEM_CLOCKWAIT
CHECK_STATUS("sem_clockwait");
#else
CHECK_STATUS("sem_timedwait");
#endif
}
}
else if (timeout == 0) {
if (status != EAGAIN)
if (status != EAGAIN) {
CHECK_STATUS("sem_trywait");
}
}
else {
CHECK_STATUS("sem_wait");