Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions cpython-unix/build-cpython.sh
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,25 @@ if [ -n "${CROSS_COMPILING}" ]; then
# TODO: There are probably more of these, see #599.
fi

# Apply weak sem_clockwait patch for runtime detection on old glibc.
# When cross-compiling against old glibc headers (< 2.30), configure cannot detect
# sem_clockwait, causing threading.Event.wait() to use CLOCK_REALTIME instead of
# CLOCK_MONOTONIC. This makes waits hang when the system clock jumps backward.
# The patch declares sem_clockwait as a weak symbol and checks at runtime.
if [[ "${PYBUILD_PLATFORM}" != macos* ]]; then
if [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_15}" ]; then
patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.15.patch
elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_13}" ]; then
patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.13.patch
elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_12}" ]; then
patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.12.patch
elif [ -n "${PYTHON_MEETS_MINIMUM_VERSION_3_11}" ]; then
patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.11.patch
else
patch -p1 -i ${ROOT}/patch-sem-clockwait-weak-3.10.patch
fi
fi

# Adjust the Python startup logic (getpath.py) to properly locate the installation, even when
# invoked through a symlink or through an incorrect argv[0]. Because this Python is relocatable, we
# don't get to rely on the fallback to the compiled-in installation prefix.
Expand Down
113 changes: 113 additions & 0 deletions cpython-unix/patch-sem-clockwait-weak-3.10.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -87,6 +87,18 @@
#endif
#endif

+/* When cross-compiling against old glibc headers (e.g., glibc < 2.30),
+ * configure cannot detect sem_clockwait. Declare it as a weak symbol so it
+ * resolves to NULL on old glibc and to the real function on glibc 2.30+.
+ * This enables monotonic clock waits at runtime when available, preventing
+ * hangs when the system clock jumps backward (e.g., NTP sync). */
+#if defined(__linux__) && !defined(HAVE_SEM_CLOCKWAIT)
+#include <time.h>
+__attribute__((weak)) extern int sem_clockwait(sem_t *, clockid_t,
+ const struct timespec *);
+#define HAVE_SEM_CLOCKWAIT 1
+#define _Py_SEM_CLOCKWAIT_WEAK 1
+#endif

/* Whether or not to use semaphores directly rather than emulating them with
* mutexes and condition variables:
@@ -443,9 +455,7 @@
sem_t *thelock = (sem_t *)lock;
int status, error = 0;
struct timespec ts;
-#ifndef HAVE_SEM_CLOCKWAIT
_PyTime_t deadline = 0;
-#endif

(void) error; /* silence unused-but-set-variable warning */
dprintf(("PyThread_acquire_lock_timed(%p, %lld, %d) called\n",
@@ -455,29 +465,35 @@
Py_FatalError("Timeout larger than PY_TIMEOUT_MAX");
}

- if (microseconds > 0) {
-#ifdef HAVE_SEM_CLOCKWAIT
- monotonic_abs_timeout(microseconds, &ts);
+#ifdef _Py_SEM_CLOCKWAIT_WEAK
+ int use_clockwait = (sem_clockwait != NULL);
#else
- MICROSECONDS_TO_TIMESPEC(microseconds, ts);
+ int use_clockwait = 1;
+#endif

- 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;
+ if (microseconds > 0) {
+ if (use_clockwait) {
+ monotonic_abs_timeout(microseconds, &ts);
+ } else {
+ 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;
+ }
}
-#endif
}

while (1) {
if (microseconds > 0) {
-#ifdef HAVE_SEM_CLOCKWAIT
- status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC,
- &ts));
-#else
- status = fix_status(sem_timedwait(thelock, &ts));
-#endif
+ if (use_clockwait) {
+ status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC,
+ &ts));
+ } else {
+ status = fix_status(sem_timedwait(thelock, &ts));
+ }
}
else if (microseconds == 0) {
status = fix_status(sem_trywait(thelock));
@@ -494,8 +510,7 @@

// sem_clockwait() uses an absolute timeout, there is no need
// to recompute the relative timeout.
-#ifndef HAVE_SEM_CLOCKWAIT
- if (microseconds > 0) {
+ if (!use_clockwait && microseconds > 0) {
/* wait interrupted by a signal (EINTR): recompute the timeout */
_PyTime_t dt = deadline - _PyTime_GetMonotonicClock();
if (dt < 0) {
@@ -516,18 +531,13 @@
microseconds = 0;
}
}
-#endif
}

/* Don't check the status if we're stopping because of an interrupt. */
if (!(intr_flag && status == EINTR)) {
if (microseconds > 0) {
if (status != ETIMEDOUT) {
-#ifdef HAVE_SEM_CLOCKWAIT
- CHECK_STATUS("sem_clockwait");
-#else
- CHECK_STATUS("sem_timedwait");
-#endif
+ CHECK_STATUS(use_clockwait ? "sem_clockwait" : "sem_timedwait");
}
}
else if (microseconds == 0) {
103 changes: 103 additions & 0 deletions cpython-unix/patch-sem-clockwait-weak-3.11.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -89,6 +89,18 @@
#endif
#endif

+/* When cross-compiling against old glibc headers (e.g., glibc < 2.30),
+ * configure cannot detect sem_clockwait. Declare it as a weak symbol so it
+ * resolves to NULL on old glibc and to the real function on glibc 2.30+.
+ * This enables monotonic clock waits at runtime when available, preventing
+ * hangs when the system clock jumps backward (e.g., NTP sync). */
+#if defined(__linux__) && !defined(HAVE_SEM_CLOCKWAIT)
+#include <time.h>
+__attribute__((weak)) extern int sem_clockwait(sem_t *, clockid_t,
+ const struct timespec *);
+#define HAVE_SEM_CLOCKWAIT 1
+#define _Py_SEM_CLOCKWAIT_WEAK 1
+#endif

/* Whether or not to use semaphores directly rather than emulating them with
* mutexes and condition variables:
@@ -463,32 +475,32 @@
timeout = _PyTime_FromNanoseconds(-1);
}

-#ifdef HAVE_SEM_CLOCKWAIT
- struct timespec abs_timeout;
- // Local scope for deadline
- {
- _PyTime_t deadline = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout);
- _PyTime_AsTimespec_clamp(deadline, &abs_timeout);
- }
+#ifdef _Py_SEM_CLOCKWAIT_WEAK
+ int use_clockwait = (sem_clockwait != NULL);
#else
+ int use_clockwait = 1;
+#endif
+ struct timespec abs_timeout;
_PyTime_t deadline = 0;
- if (timeout > 0 && !intr_flag) {
+ if (use_clockwait) {
+ _PyTime_t dl = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout);
+ _PyTime_AsTimespec_clamp(dl, &abs_timeout);
+ } else if (timeout > 0 && !intr_flag) {
deadline = _PyDeadline_Init(timeout);
}
-#endif

while (1) {
if (timeout > 0) {
-#ifdef HAVE_SEM_CLOCKWAIT
- status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC,
- &abs_timeout));
-#else
- _PyTime_t abs_time = _PyTime_Add(_PyTime_GetSystemClock(),
- timeout);
- struct timespec ts;
- _PyTime_AsTimespec_clamp(abs_time, &ts);
- status = fix_status(sem_timedwait(thelock, &ts));
-#endif
+ if (use_clockwait) {
+ status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC,
+ &abs_timeout));
+ } else {
+ _PyTime_t abs_time = _PyTime_Add(_PyTime_GetSystemClock(),
+ timeout);
+ struct timespec ts;
+ _PyTime_AsTimespec_clamp(abs_time, &ts);
+ status = fix_status(sem_timedwait(thelock, &ts));
+ }
}
else if (timeout == 0) {
status = fix_status(sem_trywait(thelock));
@@ -505,8 +517,7 @@

// sem_clockwait() uses an absolute timeout, there is no need
// to recompute the relative timeout.
-#ifndef HAVE_SEM_CLOCKWAIT
- if (timeout > 0) {
+ if (!use_clockwait && timeout > 0) {
/* wait interrupted by a signal (EINTR): recompute the timeout */
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
@@ -514,18 +525,13 @@
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) {
-#ifdef HAVE_SEM_CLOCKWAIT
- CHECK_STATUS("sem_clockwait");
-#else
- CHECK_STATUS("sem_timedwait");
-#endif
+ CHECK_STATUS(use_clockwait ? "sem_clockwait" : "sem_timedwait");
}
}
else if (timeout == 0) {
104 changes: 104 additions & 0 deletions cpython-unix/patch-sem-clockwait-weak-3.12.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
--- a/Python/thread_pthread.h
+++ b/Python/thread_pthread.h
@@ -96,6 +96,19 @@
#undef HAVE_SEM_CLOCKWAIT
#endif

+/* When cross-compiling against old glibc headers (e.g., glibc < 2.30),
+ * configure cannot detect sem_clockwait. Declare it as a weak symbol so it
+ * resolves to NULL on old glibc and to the real function on glibc 2.30+.
+ * This enables monotonic clock waits at runtime when available, preventing
+ * hangs when the system clock jumps backward (e.g., NTP sync). */
+#if defined(__linux__) && !defined(HAVE_SEM_CLOCKWAIT)
+#include <time.h>
+__attribute__((weak)) extern int sem_clockwait(sem_t *, clockid_t,
+ const struct timespec *);
+#define HAVE_SEM_CLOCKWAIT 1
+#define _Py_SEM_CLOCKWAIT_WEAK 1
+#endif
+
/* Whether or not to use semaphores directly rather than emulating them with
* mutexes and condition variables:
*/
@@ -456,32 +469,32 @@
timeout = _PyTime_FromNanoseconds(-1);
}

-#ifdef HAVE_SEM_CLOCKWAIT
- struct timespec abs_timeout;
- // Local scope for deadline
- {
- _PyTime_t deadline = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout);
- _PyTime_AsTimespec_clamp(deadline, &abs_timeout);
- }
+#ifdef _Py_SEM_CLOCKWAIT_WEAK
+ int use_clockwait = (sem_clockwait != NULL);
#else
+ int use_clockwait = 1;
+#endif
+ struct timespec abs_timeout;
_PyTime_t deadline = 0;
- if (timeout > 0 && !intr_flag) {
+ if (use_clockwait) {
+ _PyTime_t dl = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout);
+ _PyTime_AsTimespec_clamp(dl, &abs_timeout);
+ } else if (timeout > 0 && !intr_flag) {
deadline = _PyDeadline_Init(timeout);
}
-#endif

while (1) {
if (timeout > 0) {
-#ifdef HAVE_SEM_CLOCKWAIT
- status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC,
- &abs_timeout));
-#else
- _PyTime_t abs_time = _PyTime_Add(_PyTime_GetSystemClock(),
- timeout);
- struct timespec ts;
- _PyTime_AsTimespec_clamp(abs_time, &ts);
- status = fix_status(sem_timedwait(thelock, &ts));
-#endif
+ if (use_clockwait) {
+ status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC,
+ &abs_timeout));
+ } else {
+ _PyTime_t abs_time = _PyTime_Add(_PyTime_GetSystemClock(),
+ timeout);
+ struct timespec ts;
+ _PyTime_AsTimespec_clamp(abs_time, &ts);
+ status = fix_status(sem_timedwait(thelock, &ts));
+ }
}
else if (timeout == 0) {
status = fix_status(sem_trywait(thelock));
@@ -498,8 +511,7 @@

// sem_clockwait() uses an absolute timeout, there is no need
// to recompute the relative timeout.
-#ifndef HAVE_SEM_CLOCKWAIT
- if (timeout > 0) {
+ if (!use_clockwait && timeout > 0) {
/* wait interrupted by a signal (EINTR): recompute the timeout */
timeout = _PyDeadline_Get(deadline);
if (timeout < 0) {
@@ -507,18 +519,13 @@
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) {
-#ifdef HAVE_SEM_CLOCKWAIT
- CHECK_STATUS("sem_clockwait");
-#else
- CHECK_STATUS("sem_timedwait");
-#endif
+ CHECK_STATUS(use_clockwait ? "sem_clockwait" : "sem_timedwait");
}
}
else if (timeout == 0) {
Loading