Skip to content

Commit badd5d5

Browse files
authored
gh-160368: Detect sem_clockwait at runtime on Linux (#160371)
When Python is built with older Linux libc headers, timed semaphore waits use the real-time clock even if `sem_clockwait` is available at runtime. Use a weak reference on supported compilers to select `sem_clockwait` at runtime, falling back to `sem_timedwait` when the symbol is absent. ThreadSanitizer builds continue to use the real-time clock. Addresses [astral-sh/python-build-standalone#1176](astral-sh/python-build-standalone#1176).
1 parent 4af1265 commit badd5d5

2 files changed

Lines changed: 34 additions & 16 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
On Linux, allow timed semaphore waits to use the monotonic clock when
2+
``sem_clockwait`` is available at runtime, even if Python was built with
3+
older libc headers.

‎Python/parking_lot.c‎

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@
1010

1111
#include <stdbool.h>
1212

13+
#if defined(_Py_USE_SEMAPHORES) && defined(CLOCK_MONOTONIC) && !defined(_Py_THREAD_SANITIZER)
14+
# ifdef HAVE_SEM_CLOCKWAIT
15+
# define _Py_SEM_CLOCKWAIT_AVAILABLE 1
16+
# elif defined(__linux__) && (defined(__GNUC__) || defined(__clang__))
17+
/* Older glibc headers do not declare sem_clockwait. A weak reference allows
18+
such builds to use monotonic waits on newer runtimes. */
19+
extern int sem_clockwait(sem_t *, clockid_t, const struct timespec *)
20+
__attribute__((weak));
21+
# define _Py_SEM_CLOCKWAIT_AVAILABLE (sem_clockwait != NULL)
22+
# endif
23+
#endif
24+
1325

1426
typedef struct {
1527
// The mutex protects the waiter queue and the num_waiters counter.
@@ -151,24 +163,27 @@ _PySemaphore_Wait(_PySemaphore *sema, PyTime_t timeout)
151163
if (timeout >= 0) {
152164
struct timespec ts;
153165

154-
#if defined(CLOCK_MONOTONIC) && defined(HAVE_SEM_CLOCKWAIT) && !defined(_Py_THREAD_SANITIZER)
155-
PyTime_t now;
156-
// silently ignore error: cannot report error to the caller
157-
(void)PyTime_MonotonicRaw(&now);
158-
PyTime_t deadline = _PyTime_Add(now, timeout);
159-
_PyTime_AsTimespec_clamp(deadline, &ts);
160-
161-
err = sem_clockwait(&sema->platform_sem, CLOCK_MONOTONIC, &ts);
162-
#else
163-
PyTime_t now;
164-
// silently ignore error: cannot report error to the caller
165-
(void)PyTime_TimeRaw(&now);
166-
PyTime_t deadline = _PyTime_Add(now, timeout);
167-
168-
_PyTime_AsTimespec_clamp(deadline, &ts);
166+
#ifdef _Py_SEM_CLOCKWAIT_AVAILABLE
167+
if (_Py_SEM_CLOCKWAIT_AVAILABLE) {
168+
PyTime_t now;
169+
// Silently ignore error: cannot report error to the caller.
170+
(void)PyTime_MonotonicRaw(&now);
171+
PyTime_t deadline = _PyTime_Add(now, timeout);
172+
_PyTime_AsTimespec_clamp(deadline, &ts);
169173

170-
err = sem_timedwait(&sema->platform_sem, &ts);
174+
err = sem_clockwait(&sema->platform_sem, CLOCK_MONOTONIC, &ts);
175+
}
176+
else
171177
#endif
178+
{
179+
PyTime_t now;
180+
// Silently ignore error: cannot report error to the caller.
181+
(void)PyTime_TimeRaw(&now);
182+
PyTime_t deadline = _PyTime_Add(now, timeout);
183+
_PyTime_AsTimespec_clamp(deadline, &ts);
184+
185+
err = sem_timedwait(&sema->platform_sem, &ts);
186+
}
172187
}
173188
else {
174189
err = sem_wait(&sema->platform_sem);

0 commit comments

Comments
 (0)