Skip to content

Commit 1ee0f94

Browse files
authored
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.
1 parent 3e1c5d9 commit 1ee0f94

File tree

6 files changed

+160
-72
lines changed

6 files changed

+160
-72
lines changed

Doc/whatsnew/3.11.rst

+10
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,16 @@ sqlite3
239239
(Contributed by Aviv Palivoda, Daniel Shahaf, and Erlend E. Aasland in
240240
:issue:`16379`.)
241241

242+
threading
243+
---------
244+
245+
* On Unix, if the ``sem_clockwait()`` function is available in the C library
246+
(glibc 2.30 and newer), the :meth:`threading.Lock.acquire` method now uses
247+
the monotonic clock (:data:`time.CLOCK_MONOTONIC`) for the timeout, rather
248+
than using the system clock (:data:`time.CLOCK_REALTIME`), to not be affected
249+
by system clock changes.
250+
(Contributed by Livius and Victor Stinner in :issue:`41710`.)
251+
242252
time
243253
----
244254

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
On Unix, if the ``sem_clockwait()`` function is available in the C library
2+
(glibc 2.30 and newer), the :meth:`threading.Lock.acquire` method now uses the
3+
monotonic clock (:data:`time.CLOCK_MONOTONIC`) for the timeout, rather than
4+
using the system clock (:data:`time.CLOCK_REALTIME`), to not be affected by
5+
system clock changes. Patch by Victor Stinner.

Python/thread_pthread.h

+33-6
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
* mutexes and condition variables:
9393
*/
9494
#if (defined(_POSIX_SEMAPHORES) && !defined(HAVE_BROKEN_POSIX_SEMAPHORES) && \
95-
defined(HAVE_SEM_TIMEDWAIT))
95+
(defined(HAVE_SEM_TIMEDWAIT) || defined(HAVE_SEM_CLOCKWAIT)))
9696
# define USE_SEMAPHORES
9797
#else
9898
# undef USE_SEMAPHORES
@@ -461,17 +461,34 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
461461
timeout = _PyTime_FromNanoseconds(-1);
462462
}
463463

464+
#ifdef HAVE_SEM_CLOCKWAIT
465+
struct timespec abs_timeout;
466+
// Local scope for deadline
467+
{
468+
_PyTime_t deadline = _PyTime_GetMonotonicClock() + timeout;
469+
_PyTime_AsTimespec_clamp(deadline, &abs_timeout);
470+
}
471+
#else
464472
_PyTime_t deadline = 0;
465-
if (timeout > 0 && !intr_flag) {
473+
if (timeout > 0
474+
&& !intr_flag
475+
)
476+
{
466477
deadline = _PyTime_GetMonotonicClock() + timeout;
467478
}
479+
#endif
468480

469481
while (1) {
470482
if (timeout > 0) {
471-
_PyTime_t t = _PyTime_GetSystemClock() + timeout;
483+
#ifdef HAVE_SEM_CLOCKWAIT
484+
status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC,
485+
&abs_timeout));
486+
#else
487+
_PyTime_t abs_timeout = _PyTime_GetSystemClock() + timeout;
472488
struct timespec ts;
473-
_PyTime_AsTimespec_clamp(t, &ts);
489+
_PyTime_AsTimespec_clamp(abs_timeout, &ts);
474490
status = fix_status(sem_timedwait(thelock, &ts));
491+
#endif
475492
}
476493
else if (timeout == 0) {
477494
status = fix_status(sem_trywait(thelock));
@@ -486,6 +503,9 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
486503
break;
487504
}
488505

506+
// sem_clockwait() uses an absolute timeout, there is no need
507+
// to recompute the relative timeout.
508+
#ifndef HAVE_SEM_CLOCKWAIT
489509
if (timeout > 0) {
490510
/* wait interrupted by a signal (EINTR): recompute the timeout */
491511
_PyTime_t timeout = deadline - _PyTime_GetMonotonicClock();
@@ -494,17 +514,24 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
494514
break;
495515
}
496516
}
517+
#endif
497518
}
498519

499520
/* Don't check the status if we're stopping because of an interrupt. */
500521
if (!(intr_flag && status == EINTR)) {
501522
if (timeout > 0) {
502-
if (status != ETIMEDOUT)
523+
if (status != ETIMEDOUT) {
524+
#ifdef HAVE_SEM_CLOCKWAIT
525+
CHECK_STATUS("sem_clockwait");
526+
#else
503527
CHECK_STATUS("sem_timedwait");
528+
#endif
529+
}
504530
}
505531
else if (timeout == 0) {
506-
if (status != EAGAIN)
532+
if (status != EAGAIN) {
507533
CHECK_STATUS("sem_trywait");
534+
}
508535
}
509536
else {
510537
CHECK_STATUS("sem_wait");

0 commit comments

Comments
 (0)