From 3497cc848ee1cb995f048e9f8b69071d6cfb459a Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 7 Dec 2017 13:42:24 +0000 Subject: [PATCH 1/4] Attempt at adapting Victor's patch from https://bugs.python.org/issue23428 --- Makefile.pre.in | 4 ++-- Python/condvar.h | 51 +++++++++++++++++++++++++---------------- Python/thread_pthread.h | 31 ++++++++++++------------- 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index f8216971958a4e6..e3e7a0641c0aa20 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -939,7 +939,7 @@ 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 \ $(srcdir)/Python/importlib_zipimport.h @@ -1838,7 +1838,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/condvar.h b/Python/condvar.h index 13517409aeab03f..a4ccc6d225a02b1 100644 --- a/Python/condvar.h +++ b/Python/condvar.h @@ -48,19 +48,7 @@ * POSIX support */ -#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 +#include /* The following functions return 0 on success, nonzero on error */ #define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL) @@ -68,24 +56,47 @@ do { /* TODO: add overflow and truncation checks */ \ #define PyMUTEX_LOCK(mut) pthread_mutex_lock(mut) #define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(mut) -#define PyCOND_INIT(cond) pthread_cond_init((cond), NULL) +#define PyCOND_T pthread_cond_t #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)) +#define MONOTONIC + +Py_LOCAL_INLINE(int) +PyCOND_INIT(PyCOND_T *cond) +{ + pthread_condattr_t attr; + int err; + + (void)pthread_condattr_init(&attr); +#ifdef MONOTONIC + err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + if (err) + return err; +#endif + err = pthread_cond_init(cond, &attr); + (void)pthread_condattr_destroy(&attr); + return err; +} + /* 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) +PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, PY_TIMEOUT_T us) { int r; struct timespec ts; - struct timeval deadline; + _PyTime_t deadline; + +#ifdef MONOTONIC + deadline = _PyTime_GetMonotonicClock(); +#else + deadline = _PyTime_GetSystemClock(); +#endif - PyCOND_GETTIMEOFDAY(&deadline); - PyCOND_ADD_MICROSECONDS(deadline, us); - ts.tv_sec = deadline.tv_sec; - ts.tv_nsec = deadline.tv_usec * 1000; + deadline += _PyTime_FromNanoseconds(us * 1000); + _PyTime_AsTimespec(deadline, &ts); r = pthread_cond_timedwait((cond), (mut), &ts); if (r == ETIMEDOUT) diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index 09e53a14aa473ba..7dd631231a55d9a 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -74,6 +74,7 @@ defined(HAVE_SEM_TIMEDWAIT)) # define USE_SEMAPHORES #else +# define MONOTONIC # undef USE_SEMAPHORES #endif @@ -89,24 +90,16 @@ # define SET_THREAD_SIGMASK sigprocmask #endif - -/* We assume all modern POSIX systems have gettimeofday() */ -#ifdef GETTIMEOFDAY_NO_TZ -#define GETTIMEOFDAY(ptv) gettimeofday(ptv) +#ifdef MONOTONIC +#define GETTIME _PyTime_GetMonotonicClock #else -#define GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL) +#define GETTIME _PyTime_GetSystemClock #endif -#define MICROSECONDS_TO_TIMESPEC(microseconds, ts) \ +#define MICROSECONDS_TO_TIMESPEC(us, ts) \ do { \ - struct timeval tv; \ - GETTIMEOFDAY(&tv); \ - tv.tv_usec += microseconds % 1000000; \ - tv.tv_sec += microseconds / 1000000; \ - tv.tv_sec += tv.tv_usec / 1000000; \ - tv.tv_usec %= 1000000; \ - ts.tv_sec = tv.tv_sec; \ - ts.tv_nsec = tv.tv_usec * 1000; \ + _PyTime_t t = GETTIME() + _PyTime_FromNanoseconds(us * 1000); \ + _PyTime_AsTimespec(t, &ts); \ } while(0) @@ -452,6 +445,7 @@ PyThread_allocate_lock(void) { pthread_lock *lock; int status, error = 0; + pthread_condattr_t attr; dprintf(("PyThread_allocate_lock called\n")); if (!initialized) @@ -472,9 +466,14 @@ 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); + pthread_condattr_init(&attr); +#ifdef MONOTONIC + status = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + CHECK_STATUS_PTHREAD("pthread_condattr_setclock"); +#endif + status = pthread_cond_init(&lock->lock_released, &attr); CHECK_STATUS_PTHREAD("pthread_cond_init"); + pthread_condattr_destroy(&attr); if (error) { PyMem_RawFree((void *)lock); From 523e89d1c29ba6c807270be42270031312c22472 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 21 Dec 2017 15:47:49 +0000 Subject: [PATCH 2/4] In PyCOND_TIMEDWAIT dynamically increase the timeout from the requested amount in case it is so short (especially on slow systems) that the deadline is always in the past --- Python/condvar.h | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Python/condvar.h b/Python/condvar.h index a4ccc6d225a02b1..636103ffbd889b6 100644 --- a/Python/condvar.h +++ b/Python/condvar.h @@ -81,6 +81,14 @@ PyCOND_INIT(PyCOND_T *cond) return err; } +# ifdef MONOTONIC +# define GETTIME _PyTime_GetMonotonicClock +# else +# define GETTIME _PyTime_GetSystemClock +# endif + +# define MAXIMUM_TIMEOUT 100 + /* return 0 for success, 1 on timeout, -1 on error */ Py_LOCAL_INLINE(int) PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, PY_TIMEOUT_T us) @@ -89,14 +97,16 @@ PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, PY_TIMEOUT_T us) struct timespec ts; _PyTime_t deadline; -#ifdef MONOTONIC - deadline = _PyTime_GetMonotonicClock(); -#else - deadline = _PyTime_GetSystemClock(); -#endif - - deadline += _PyTime_FromNanoseconds(us * 1000); - _PyTime_AsTimespec(deadline, &ts); + while (1) { + _PyTime_t delta; + deadline = GETTIME() + _PyTime_FromNanoseconds(us * 1000); + if (us < MAXIMUM_TIMEOUT && ((delta=(GETTIME() - deadline)) > 0)) { + us += _PyTime_AsMicroseconds(delta, _PyTime_ROUND_CEILING); + continue; + } + _PyTime_AsTimespec(deadline, &ts); + break; + } r = pthread_cond_timedwait((cond), (mut), &ts); if (r == ETIMEDOUT) From 4432ff8b73adf49ead659132ea4179a1f9456158 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 31 Jan 2019 16:12:40 +0000 Subject: [PATCH 3/4] don't define MONOTONIC unless we have pthread_condattr_setclock --- Python/condvar.h | 4 ++++ Python/thread_pthread.h | 6 +++++- configure.ac | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Python/condvar.h b/Python/condvar.h index 636103ffbd889b6..8c9c9488d0893df 100644 --- a/Python/condvar.h +++ b/Python/condvar.h @@ -62,7 +62,11 @@ #define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond) #define PyCOND_WAIT(cond, mut) pthread_cond_wait((cond), (mut)) +#if HAVE_PTHREAD_CONDATTR_SETCLOCK #define MONOTONIC +#else +#undef MONOTONIC +#endif Py_LOCAL_INLINE(int) PyCOND_INIT(PyCOND_T *cond) diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index 7dd631231a55d9a..092ef5462a22c43 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -74,8 +74,12 @@ defined(HAVE_SEM_TIMEDWAIT)) # define USE_SEMAPHORES #else -# define MONOTONIC # undef USE_SEMAPHORES +# if HAVE_PTHREAD_CONDATTR_SETCLOCK +# define MONOTONIC +# else +# undef MONOTONIC +# endif #endif diff --git a/configure.ac b/configure.ac index 721edb015ea36fb..51e0709fb63e0fa 100644 --- a/configure.ac +++ b/configure.ac @@ -3157,7 +3157,7 @@ if test "$posix_threads" = "yes"; then [Define if pthread_sigmask() does not work on your system.]) ;; esac]) - AC_CHECK_FUNCS(pthread_getcpuclockid) + AC_CHECK_FUNCS([pthread_getcpuclockid pthread_condattr_setclock]) fi From 5b248ad7ef42c02c87899b8c3f6097498da1d667 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 31 Jan 2019 17:12:34 +0000 Subject: [PATCH 4/4] update configure --- configure | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/configure b/configure index ebd9f904b09afb6..b2c307044e94470 100755 --- a/configure +++ b/configure @@ -10878,12 +10878,13 @@ $as_echo "#define HAVE_BROKEN_PTHREAD_SIGMASK 1" >>confdefs.h fi done - for ac_func in pthread_getcpuclockid + for ac_func in pthread_getcpuclockid pthread_condattr_setclock do : - ac_fn_c_check_func "$LINENO" "pthread_getcpuclockid" "ac_cv_func_pthread_getcpuclockid" -if test "x$ac_cv_func_pthread_getcpuclockid" = xyes; then : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF -#define HAVE_PTHREAD_GETCPUCLOCKID 1 +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi