From 1ee0f94d16f150356a4b9b0a39d44ba1d2d5b9fc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Oct 2021 09:55:28 +0200 Subject: 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. --- Doc/whatsnew/3.11.rst | 10 ++ .../2021-09-30-23-00-18.bpo-41710.svuloZ.rst | 5 + Python/thread_pthread.h | 39 ++++- configure | 167 +++++++++++++-------- configure.ac | 2 +- pyconfig.h.in | 9 +- 6 files changed, 160 insertions(+), 72 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-09-30-23-00-18.bpo-41710.svuloZ.rst diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index d01d2e2..ff376d2 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -239,6 +239,16 @@ sqlite3 (Contributed by Aviv Palivoda, Daniel Shahaf, and Erlend E. Aasland in :issue:`16379`.) +threading +--------- + +* On Unix, if the ``sem_clockwait()`` function is available in the C library + (glibc 2.30 and newer), the :meth:`threading.Lock.acquire` method now uses + the monotonic clock (:data:`time.CLOCK_MONOTONIC`) for the timeout, rather + than using the system clock (:data:`time.CLOCK_REALTIME`), to not be affected + by system clock changes. + (Contributed by Livius and Victor Stinner in :issue:`41710`.) + time ---- diff --git a/Misc/NEWS.d/next/Library/2021-09-30-23-00-18.bpo-41710.svuloZ.rst b/Misc/NEWS.d/next/Library/2021-09-30-23-00-18.bpo-41710.svuloZ.rst new file mode 100644 index 0000000..d8a4f95 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-09-30-23-00-18.bpo-41710.svuloZ.rst @@ -0,0 +1,5 @@ +On Unix, if the ``sem_clockwait()`` function is available in the C library +(glibc 2.30 and newer), the :meth:`threading.Lock.acquire` method now uses the +monotonic clock (:data:`time.CLOCK_MONOTONIC`) for the timeout, rather than +using the system clock (:data:`time.CLOCK_REALTIME`), to not be affected by +system clock changes. Patch by Victor Stinner. diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index 3815ffa..9b5e273 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -92,7 +92,7 @@ * mutexes and condition variables: */ #if (defined(_POSIX_SEMAPHORES) && !defined(HAVE_BROKEN_POSIX_SEMAPHORES) && \ - defined(HAVE_SEM_TIMEDWAIT)) + (defined(HAVE_SEM_TIMEDWAIT) || defined(HAVE_SEM_CLOCKWAIT))) # define USE_SEMAPHORES #else # undef USE_SEMAPHORES @@ -461,17 +461,34 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds, timeout = _PyTime_FromNanoseconds(-1); } +#ifdef HAVE_SEM_CLOCKWAIT + struct timespec abs_timeout; + // Local scope for deadline + { + _PyTime_t deadline = _PyTime_GetMonotonicClock() + timeout; + _PyTime_AsTimespec_clamp(deadline, &abs_timeout); + } +#else _PyTime_t deadline = 0; - if (timeout > 0 && !intr_flag) { + if (timeout > 0 + && !intr_flag + ) + { deadline = _PyTime_GetMonotonicClock() + timeout; } +#endif while (1) { if (timeout > 0) { - _PyTime_t t = _PyTime_GetSystemClock() + timeout; +#ifdef HAVE_SEM_CLOCKWAIT + status = fix_status(sem_clockwait(thelock, CLOCK_MONOTONIC, + &abs_timeout)); +#else + _PyTime_t abs_timeout = _PyTime_GetSystemClock() + timeout; struct timespec ts; - _PyTime_AsTimespec_clamp(t, &ts); + _PyTime_AsTimespec_clamp(abs_timeout, &ts); status = fix_status(sem_timedwait(thelock, &ts)); +#endif } else if (timeout == 0) { status = fix_status(sem_trywait(thelock)); @@ -486,6 +503,9 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds, break; } + // sem_clockwait() uses an absolute timeout, there is no need + // to recompute the relative timeout. +#ifndef HAVE_SEM_CLOCKWAIT if (timeout > 0) { /* wait interrupted by a signal (EINTR): recompute the timeout */ _PyTime_t timeout = deadline - _PyTime_GetMonotonicClock(); @@ -494,17 +514,24 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds, 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) + if (status != ETIMEDOUT) { +#ifdef HAVE_SEM_CLOCKWAIT + CHECK_STATUS("sem_clockwait"); +#else CHECK_STATUS("sem_timedwait"); +#endif + } } else if (timeout == 0) { - if (status != EAGAIN) + if (status != EAGAIN) { CHECK_STATUS("sem_trywait"); + } } else { CHECK_STATUS("sem_wait"); diff --git a/configure b/configure index 4acf91f..75e2e29 100755 --- a/configure +++ b/configure @@ -9764,7 +9764,7 @@ then BLDSHARED="$LDSHARED" fi ;; - Linux*|GNU*|QNX*|VxWorks*) + Linux*|GNU*|QNX*|VxWorks*|Haiku*) LDSHARED='$(CC) -shared' LDCXXSHARED='$(CXX) -shared';; FreeBSD*) @@ -9835,6 +9835,7 @@ then Linux-android*) ;; Linux*|GNU*) CCSHARED="-fPIC";; FreeBSD*|NetBSD*|OpenBSD*|DragonFly*) CCSHARED="-fPIC";; + Haiku*) CCSHARED="-fPIC";; OpenUNIX*|UnixWare*) if test "$GCC" = "yes" then CCSHARED="-fPIC" @@ -10562,6 +10563,48 @@ if test "x$ac_cv_lib_socket_socket" = xyes; then : fi # SVR4 sockets +# Haiku system library +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lnetwork" >&5 +$as_echo_n "checking for socket in -lnetwork... " >&6; } +if ${ac_cv_lib_network_socket+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnetwork $LIBS $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char socket (); +int +main () +{ +return socket (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_network_socket=yes +else + ac_cv_lib_network_socket=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_network_socket" >&5 +$as_echo "$ac_cv_lib_network_socket" >&6; } +if test "x$ac_cv_lib_network_socket" = xyes; then : + LIBS="-lnetwork $LIBS" +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for --with-libs" >&5 $as_echo_n "checking for --with-libs... " >&6; } @@ -11774,7 +11817,7 @@ for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ posix_fallocate posix_fadvise posix_spawn posix_spawnp pread preadv preadv2 \ pthread_condattr_setclock pthread_init pthread_kill pwrite pwritev pwritev2 \ readlink readlinkat readv realpath renameat \ - sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ + sem_open sem_timedwait sem_clockwait sem_getvalue sem_unlink sendfile setegid seteuid \ setgid sethostname \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \ sched_get_priority_max sched_setaffinity sched_setscheduler sched_setparam \ @@ -13252,19 +13295,19 @@ fi done -for ac_func in clock_nanosleep +for ac_func in clock_getres do : - ac_fn_c_check_func "$LINENO" "clock_nanosleep" "ac_cv_func_clock_nanosleep" -if test "x$ac_cv_func_clock_nanosleep" = xyes; then : + ac_fn_c_check_func "$LINENO" "clock_getres" "ac_cv_func_clock_getres" +if test "x$ac_cv_func_clock_getres" = xyes; then : cat >>confdefs.h <<_ACEOF -#define HAVE_CLOCK_NANOSLEEP 1 +#define HAVE_CLOCK_GETRES 1 _ACEOF else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_nanosleep in -lrt" >&5 -$as_echo_n "checking for clock_nanosleep in -lrt... " >&6; } -if ${ac_cv_lib_rt_clock_nanosleep+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_getres in -lrt" >&5 +$as_echo_n "checking for clock_getres in -lrt... " >&6; } +if ${ac_cv_lib_rt_clock_getres+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -13278,29 +13321,29 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char clock_nanosleep (); +char clock_getres (); int main () { -return clock_nanosleep (); +return clock_getres (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_rt_clock_nanosleep=yes + ac_cv_lib_rt_clock_getres=yes else - ac_cv_lib_rt_clock_nanosleep=no + ac_cv_lib_rt_clock_getres=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_nanosleep" >&5 -$as_echo "$ac_cv_lib_rt_clock_nanosleep" >&6; } -if test "x$ac_cv_lib_rt_clock_nanosleep" = xyes; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_getres" >&5 +$as_echo "$ac_cv_lib_rt_clock_getres" >&6; } +if test "x$ac_cv_lib_rt_clock_getres" = xyes; then : - $as_echo "#define HAVE_CLOCK_NANOSLEEP 1" >>confdefs.h + $as_echo "#define HAVE_CLOCK_GETRES 1" >>confdefs.h fi @@ -13310,19 +13353,19 @@ fi done -for ac_func in nanosleep +for ac_func in clock_settime do : - ac_fn_c_check_func "$LINENO" "nanosleep" "ac_cv_func_nanosleep" -if test "x$ac_cv_func_nanosleep" = xyes; then : + ac_fn_c_check_func "$LINENO" "clock_settime" "ac_cv_func_clock_settime" +if test "x$ac_cv_func_clock_settime" = xyes; then : cat >>confdefs.h <<_ACEOF -#define HAVE_NANOSLEEP 1 +#define HAVE_CLOCK_SETTIME 1 _ACEOF else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nanosleep in -lrt" >&5 -$as_echo_n "checking for nanosleep in -lrt... " >&6; } -if ${ac_cv_lib_rt_nanosleep+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_settime in -lrt" >&5 +$as_echo_n "checking for clock_settime in -lrt... " >&6; } +if ${ac_cv_lib_rt_clock_settime+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -13336,29 +13379,29 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char nanosleep (); +char clock_settime (); int main () { -return nanosleep (); +return clock_settime (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_rt_nanosleep=yes + ac_cv_lib_rt_clock_settime=yes else - ac_cv_lib_rt_nanosleep=no + ac_cv_lib_rt_clock_settime=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_nanosleep" >&5 -$as_echo "$ac_cv_lib_rt_nanosleep" >&6; } -if test "x$ac_cv_lib_rt_nanosleep" = xyes; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_settime" >&5 +$as_echo "$ac_cv_lib_rt_clock_settime" >&6; } +if test "x$ac_cv_lib_rt_clock_settime" = xyes; then : - $as_echo "#define HAVE_NANOSLEEP 1" >>confdefs.h + $as_echo "#define HAVE_CLOCK_SETTIME 1" >>confdefs.h fi @@ -13368,19 +13411,19 @@ fi done -for ac_func in clock_getres +for ac_func in clock_nanosleep do : - ac_fn_c_check_func "$LINENO" "clock_getres" "ac_cv_func_clock_getres" -if test "x$ac_cv_func_clock_getres" = xyes; then : + ac_fn_c_check_func "$LINENO" "clock_nanosleep" "ac_cv_func_clock_nanosleep" +if test "x$ac_cv_func_clock_nanosleep" = xyes; then : cat >>confdefs.h <<_ACEOF -#define HAVE_CLOCK_GETRES 1 +#define HAVE_CLOCK_NANOSLEEP 1 _ACEOF else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_getres in -lrt" >&5 -$as_echo_n "checking for clock_getres in -lrt... " >&6; } -if ${ac_cv_lib_rt_clock_getres+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_nanosleep in -lrt" >&5 +$as_echo_n "checking for clock_nanosleep in -lrt... " >&6; } +if ${ac_cv_lib_rt_clock_nanosleep+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -13394,29 +13437,29 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char clock_getres (); +char clock_nanosleep (); int main () { -return clock_getres (); +return clock_nanosleep (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_rt_clock_getres=yes + ac_cv_lib_rt_clock_nanosleep=yes else - ac_cv_lib_rt_clock_getres=no + ac_cv_lib_rt_clock_nanosleep=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_getres" >&5 -$as_echo "$ac_cv_lib_rt_clock_getres" >&6; } -if test "x$ac_cv_lib_rt_clock_getres" = xyes; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_nanosleep" >&5 +$as_echo "$ac_cv_lib_rt_clock_nanosleep" >&6; } +if test "x$ac_cv_lib_rt_clock_nanosleep" = xyes; then : - $as_echo "#define HAVE_CLOCK_GETRES 1" >>confdefs.h + $as_echo "#define HAVE_CLOCK_NANOSLEEP 1" >>confdefs.h fi @@ -13426,19 +13469,19 @@ fi done -for ac_func in clock_settime +for ac_func in nanosleep do : - ac_fn_c_check_func "$LINENO" "clock_settime" "ac_cv_func_clock_settime" -if test "x$ac_cv_func_clock_settime" = xyes; then : + ac_fn_c_check_func "$LINENO" "nanosleep" "ac_cv_func_nanosleep" +if test "x$ac_cv_func_nanosleep" = xyes; then : cat >>confdefs.h <<_ACEOF -#define HAVE_CLOCK_SETTIME 1 +#define HAVE_NANOSLEEP 1 _ACEOF else - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_settime in -lrt" >&5 -$as_echo_n "checking for clock_settime in -lrt... " >&6; } -if ${ac_cv_lib_rt_clock_settime+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for nanosleep in -lrt" >&5 +$as_echo_n "checking for nanosleep in -lrt... " >&6; } +if ${ac_cv_lib_rt_nanosleep+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -13452,29 +13495,29 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char clock_settime (); +char nanosleep (); int main () { -return clock_settime (); +return nanosleep (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_rt_clock_settime=yes + ac_cv_lib_rt_nanosleep=yes else - ac_cv_lib_rt_clock_settime=no + ac_cv_lib_rt_nanosleep=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_settime" >&5 -$as_echo "$ac_cv_lib_rt_clock_settime" >&6; } -if test "x$ac_cv_lib_rt_clock_settime" = xyes; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_nanosleep" >&5 +$as_echo "$ac_cv_lib_rt_nanosleep" >&6; } +if test "x$ac_cv_lib_rt_nanosleep" = xyes; then : - $as_echo "#define HAVE_CLOCK_SETTIME 1" >>confdefs.h + $as_echo "#define HAVE_NANOSLEEP 1" >>confdefs.h fi diff --git a/configure.ac b/configure.ac index 48d86ef..908dd28 100644 --- a/configure.ac +++ b/configure.ac @@ -3744,7 +3744,7 @@ AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ posix_fallocate posix_fadvise posix_spawn posix_spawnp pread preadv preadv2 \ pthread_condattr_setclock pthread_init pthread_kill pwrite pwritev pwritev2 \ readlink readlinkat readv realpath renameat \ - sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ + sem_open sem_timedwait sem_clockwait sem_getvalue sem_unlink sendfile setegid seteuid \ setgid sethostname \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \ sched_get_priority_max sched_setaffinity sched_setscheduler sched_setparam \ diff --git a/pyconfig.h.in b/pyconfig.h.in index 23d7111..862c083 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -136,15 +136,15 @@ /* Define to 1 if you have the `clock' function. */ #undef HAVE_CLOCK -/* Define to 1 if you have the `clock_nanosleep' function. */ -#undef HAVE_CLOCK_NANOSLEEP - /* Define to 1 if you have the `clock_getres' function. */ #undef HAVE_CLOCK_GETRES /* Define to 1 if you have the `clock_gettime' function. */ #undef HAVE_CLOCK_GETTIME +/* Define to 1 if you have the `clock_nanosleep' function. */ +#undef HAVE_CLOCK_NANOSLEEP + /* Define to 1 if you have the `clock_settime' function. */ #undef HAVE_CLOCK_SETTIME @@ -908,6 +908,9 @@ /* Define to 1 if you have the `sched_setscheduler' function. */ #undef HAVE_SCHED_SETSCHEDULER +/* Define to 1 if you have the `sem_clockwait' function. */ +#undef HAVE_SEM_CLOCKWAIT + /* Define to 1 if you have the `sem_getvalue' function. */ #undef HAVE_SEM_GETVALUE -- cgit v0.12