diff --git a/SOURCES/00257-threading-condition-wait.patch b/SOURCES/00257-threading-condition-wait.patch new file mode 100644 index 0000000..7172133 --- /dev/null +++ b/SOURCES/00257-threading-condition-wait.patch @@ -0,0 +1,40 @@ +diff --git a/Lib/threading.py b/Lib/threading.py +index 7ab9ad8..dcedd3b 100644 +--- a/Lib/threading.py ++++ b/Lib/threading.py +@@ -3,7 +3,7 @@ + import sys as _sys + import _thread + +-from time import monotonic as _time ++from time import monotonic as _time, sleep as _sleep + from traceback import format_exc as _format_exc + from _weakrefset import WeakSet + from itertools import islice as _islice, count as _count +@@ -296,7 +296,25 @@ class Condition: + gotit = True + else: + if timeout > 0: +- gotit = waiter.acquire(True, timeout) ++ # rhbz#2003758: Avoid waiter.acquire(True, timeout) since ++ # it uses the system clock internally. ++ # ++ # Balancing act: We can't afford a pure busy loop, so we ++ # have to sleep; but if we sleep the whole timeout time, ++ # we'll be unresponsive. The scheme here sleeps very ++ # little at first, longer as time goes on, but never longer ++ # than 20 times per second (or the timeout time remaining). ++ endtime = _time() + timeout ++ delay = 0.0005 # 500 us -> initial delay of 1 ms ++ while True: ++ gotit = waiter.acquire(0) ++ if gotit: ++ break ++ remaining = min(endtime - _time(), timeout) ++ if remaining <= 0: ++ break ++ delay = min(delay * 2, remaining, .05) ++ _sleep(delay) + else: + gotit = waiter.acquire(False) + return gotit diff --git a/SOURCES/00370-GIL-monotonic-clock.patch b/SOURCES/00370-GIL-monotonic-clock.patch new file mode 100644 index 0000000..124f198 --- /dev/null +++ b/SOURCES/00370-GIL-monotonic-clock.patch @@ -0,0 +1,267 @@ +diff --git a/Makefile.pre.in b/Makefile.pre.in +index 8da1965..9864fe2 100644 +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -884,7 +884,8 @@ regen-opcode-targets: + $(srcdir)/Python/opcode_targets.h.new + $(UPDATE_FILE) $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/opcode_targets.h.new + +-Python/ceval.o: $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/ceval_gil.h ++Python/ceval.o: $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/ceval_gil.h \ ++ $(srcdir)/Python/condvar.h + + Python/frozen.o: $(srcdir)/Python/importlib.h $(srcdir)/Python/importlib_external.h + +@@ -1706,7 +1707,7 @@ patchcheck: @DEF_MAKE_RULE@ + + # Dependencies + +-Python/thread.o: @THREADHEADERS@ ++Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h + + # Declare targets that aren't real files + .PHONY: all build_all sharedmods check-clean-src oldsharedmods test quicktest +diff --git a/Python/ceval.c b/Python/ceval.c +index 0b30cc1..3f1300c 100644 +--- a/Python/ceval.c ++++ b/Python/ceval.c +@@ -232,6 +232,7 @@ PyEval_InitThreads(void) + { + if (gil_created()) + return; ++ PyThread_init_thread(); + create_gil(); + take_gil(PyThreadState_GET()); + main_thread = PyThread_get_thread_ident(); +diff --git a/Python/condvar.h b/Python/condvar.h +index 9a71b17..39a420f 100644 +--- a/Python/condvar.h ++++ b/Python/condvar.h +@@ -59,20 +59,6 @@ + + #include + +-#define PyCOND_ADD_MICROSECONDS(tv, interval) \ +-do { /* TODO: add overflow and truncation checks */ \ +- tv.tv_usec += (long) interval; \ +- tv.tv_sec += tv.tv_usec / 1000000; \ +- tv.tv_usec %= 1000000; \ +-} while (0) +- +-/* We assume all modern POSIX systems have gettimeofday() */ +-#ifdef GETTIMEOFDAY_NO_TZ +-#define PyCOND_GETTIMEOFDAY(ptv) gettimeofday(ptv) +-#else +-#define PyCOND_GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL) +-#endif +- + /* The following functions return 0 on success, nonzero on error */ + #define PyMUTEX_T pthread_mutex_t + #define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL) +@@ -81,32 +67,30 @@ do { /* TODO: add overflow and truncation checks */ \ + #define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(mut) + + #define PyCOND_T pthread_cond_t +-#define PyCOND_INIT(cond) pthread_cond_init((cond), NULL) ++#define PyCOND_INIT(cond) _PyThread_cond_init(cond) + #define PyCOND_FINI(cond) pthread_cond_destroy(cond) + #define PyCOND_SIGNAL(cond) pthread_cond_signal(cond) + #define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond) + #define PyCOND_WAIT(cond, mut) pthread_cond_wait((cond), (mut)) + ++/* These private functions are implemented in Python/thread_pthread.h */ ++int _PyThread_cond_init(PyCOND_T *cond); ++void _PyThread_cond_after(long long us, struct timespec *abs); ++ + /* return 0 for success, 1 on timeout, -1 on error */ + Py_LOCAL_INLINE(int) + PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us) + { +- int r; +- struct timespec ts; +- struct timeval deadline; +- +- PyCOND_GETTIMEOFDAY(&deadline); +- PyCOND_ADD_MICROSECONDS(deadline, us); +- ts.tv_sec = deadline.tv_sec; +- ts.tv_nsec = deadline.tv_usec * 1000; +- +- r = pthread_cond_timedwait((cond), (mut), &ts); +- if (r == ETIMEDOUT) ++ struct timespec abs; ++ _PyThread_cond_after(us, &abs); ++ int ret = pthread_cond_timedwait(cond, mut, &abs); ++ if (ret == ETIMEDOUT) { + return 1; +- else if (r) ++ } ++ if (ret) { + return -1; +- else +- return 0; ++ } ++ return 0; + } + + #elif defined(NT_THREADS) +diff --git a/Python/thread.c b/Python/thread.c +index 63eeb1e..c5d0e59 100644 +--- a/Python/thread.c ++++ b/Python/thread.c +@@ -6,6 +6,7 @@ + Stuff shared by all thread_*.h files is collected here. */ + + #include "Python.h" ++#include "condvar.h" + + #ifndef _POSIX_THREADS + /* This means pthreads are not implemented in libc headers, hence the macro +diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h +index baea71f..7dc295e 100644 +--- a/Python/thread_pthread.h ++++ b/Python/thread_pthread.h +@@ -66,16 +66,6 @@ + #endif + #endif + +-#if !defined(pthread_attr_default) +-# define pthread_attr_default ((pthread_attr_t *)NULL) +-#endif +-#if !defined(pthread_mutexattr_default) +-# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL) +-#endif +-#if !defined(pthread_condattr_default) +-# define pthread_condattr_default ((pthread_condattr_t *)NULL) +-#endif +- + + /* Whether or not to use semaphores directly rather than emulating them with + * mutexes and condition variables: +@@ -120,6 +110,56 @@ do { \ + } while(0) + + ++/* ++ * pthread_cond support ++ */ ++ ++#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) ++// monotonic is supported statically. It doesn't mean it works on runtime. ++#define CONDATTR_MONOTONIC ++#endif ++ ++// NULL when pthread_condattr_setclock(CLOCK_MONOTONIC) is not supported. ++static pthread_condattr_t *condattr_monotonic = NULL; ++ ++static void ++init_condattr() ++{ ++#ifdef CONDATTR_MONOTONIC ++ static pthread_condattr_t ca; ++ pthread_condattr_init(&ca); ++ if (pthread_condattr_setclock(&ca, CLOCK_MONOTONIC) == 0) { ++ condattr_monotonic = &ca; // Use monotonic clock ++ } ++#endif ++} ++ ++int ++_PyThread_cond_init(PyCOND_T *cond) ++{ ++ return pthread_cond_init(cond, condattr_monotonic); ++} ++ ++void ++_PyThread_cond_after(long long us, struct timespec *abs) ++{ ++#ifdef CONDATTR_MONOTONIC ++ if (condattr_monotonic) { ++ clock_gettime(CLOCK_MONOTONIC, abs); ++ abs->tv_sec += us / 1000000; ++ abs->tv_nsec += (us % 1000000) * 1000; ++ abs->tv_sec += abs->tv_nsec / 1000000000; ++ abs->tv_nsec %= 1000000000; ++ return; ++ } ++#endif ++ ++ struct timespec ts; ++ MICROSECONDS_TO_TIMESPEC(us, ts); ++ *abs = ts; ++} ++ ++ + /* A pthread mutex isn't sufficient to model the Python lock type + * because, according to Draft 5 of the docs (P1003.4a/D5), both of the + * following are undefined: +@@ -175,6 +215,7 @@ PyThread__init_thread(void) + extern void pthread_init(void); + pthread_init(); + #endif ++ init_condattr(); + } + + #endif /* !_HAVE_BSDI */ +@@ -449,8 +490,7 @@ PyThread_allocate_lock(void) + memset((void *)lock, '\0', sizeof(pthread_lock)); + lock->locked = 0; + +- status = pthread_mutex_init(&lock->mut, +- pthread_mutexattr_default); ++ status = pthread_mutex_init(&lock->mut, NULL); + CHECK_STATUS_PTHREAD("pthread_mutex_init"); + /* Mark the pthread mutex underlying a Python mutex as + pure happens-before. We can't simply mark the +@@ -459,8 +499,7 @@ PyThread_allocate_lock(void) + will cause errors. */ + _Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(&lock->mut); + +- status = pthread_cond_init(&lock->lock_released, +- pthread_condattr_default); ++ status = _PyThread_cond_init(&lock->lock_released); + CHECK_STATUS_PTHREAD("pthread_cond_init"); + + if (error) { +@@ -519,9 +558,10 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds, + success = PY_LOCK_ACQUIRED; + } + else if (microseconds != 0) { +- struct timespec ts; +- if (microseconds > 0) +- MICROSECONDS_TO_TIMESPEC(microseconds, ts); ++ struct timespec abs; ++ if (microseconds > 0) { ++ _PyThread_cond_after(microseconds, &abs); ++ } + /* continue trying until we get the lock */ + + /* mut must be locked by me -- part of the condition +@@ -530,10 +570,13 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds, + if (microseconds > 0) { + status = pthread_cond_timedwait( + &thelock->lock_released, +- &thelock->mut, &ts); ++ &thelock->mut, &abs); ++ if (status == 1) { ++ break; ++ } + if (status == ETIMEDOUT) + break; +- CHECK_STATUS_PTHREAD("pthread_cond_timed_wait"); ++ CHECK_STATUS_PTHREAD("pthread_cond_timedwait"); + } + else { + status = pthread_cond_wait( +diff --git a/configure.ac b/configure.ac +index a0e3613..8a17559 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -3582,7 +3582,7 @@ AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ + memrchr mbrtowc mkdirat mkfifo \ + mkfifoat mknod mknodat mktime mremap nice openat pathconf pause pipe2 plock poll \ + posix_fallocate posix_fadvise pread \ +- pthread_init pthread_kill putenv pwrite readlink readlinkat readv realpath renameat \ ++ pthread_condattr_setclock pthread_init pthread_kill putenv pwrite readlink readlinkat readv realpath renameat \ + select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ + setgid sethostname \ + setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \ diff --git a/SPECS/python3.spec b/SPECS/python3.spec index e04db64..18812e3 100644 --- a/SPECS/python3.spec +++ b/SPECS/python3.spec @@ -14,7 +14,7 @@ URL: https://www.python.org/ # WARNING When rebasing to a new Python version, # remember to update the python3-docs package as well Version: %{pybasever}.8 -Release: 43%{?dist} +Release: 44%{?dist} License: Python @@ -344,6 +344,16 @@ Patch189: 00189-use-rpm-wheels.patch # Fedora Change: https://fedoraproject.org/wiki/Changes/Making_sudo_pip_safe Patch251: 00251-change-user-install-location.patch +# 00257 # +# Use the monotonic clock for threading.Condition.wait() as to not be affected by +# the system clock changes. +# This patch works around the issue. +# Implemented by backporting the respective python2 code: +# https://github.com/python/cpython/blob/v2.7.18/Lib/threading.py#L331 +# along with our downstream patch for python2 fixing the same issue. +# Downstream only. +Patch257: 00257-threading-condition-wait.patch + # 00262 # # Backport of PEP 538: Coercing the legacy C locale to a UTF-8 based locale # https://www.python.org/dev/peps/pep-0538/ @@ -628,6 +638,13 @@ Patch368: 00368-CVE-2021-3737.patch # Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2009200 Patch369: 00369-rollover-only-regular-files-in-logging-handlers.patch +# 00370 # +# Utilize the monotonic clock for the global interpreter lock instead of the real-time clock +# to avoid issues when the system time changes +# Upstream: https://bugs.python.org/issue12822 +# Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2003758 +Patch370: 00370-GIL-monotonic-clock.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -928,6 +945,7 @@ rm Lib/ensurepip/_bundled/*.whl %endif %patch251 -p1 +%patch257 -p1 %patch262 -p1 %patch294 -p1 %patch316 -p1 @@ -964,6 +982,7 @@ git apply %{PATCH351} %patch366 -p1 %patch368 -p1 %patch369 -p1 +%patch370 -p1 # Remove files that should be generated by the build # (This is after patching, so that we can use patches directly from upstream) @@ -1889,6 +1908,11 @@ fi # ====================================================== %changelog +* Tue Oct 12 2021 Charalampos Stratakis - 3.6.8-44 +- Use the monotonic clock for theading.Condition +- Use the monotonic clock for the global interpreter lock +Resolves: rhbz#2003758 + * Mon Oct 11 2021 Charalampos Stratakis - 3.6.8-43 - Change shouldRollover() methods of logging.handlers to only rollover regular files Resolves: rhbz#2009200