[Python-checkins] bpo-41710: PyThread_acquire_lock_timed() uses sem_clockwait() (GH-28662)

vstinner webhook-mailer at python.org
Fri Oct 1 03:55:37 EDT 2021


https://github.com/python/cpython/commit/1ee0f94d16f150356a4b9b0a39d44ba1d2d5b9fc
commit: 1ee0f94d16f150356a4b9b0a39d44ba1d2d5b9fc
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-10-01T09:55:28+02:00
summary:

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.

files:
A Misc/NEWS.d/next/Library/2021-09-30-23-00-18.bpo-41710.svuloZ.rst
M Doc/whatsnew/3.11.rst
M Python/thread_pthread.h
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index d01d2e263199a..ff376d231bafa 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 0000000000000..d8a4f9507c189
--- /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 3815ffae20c01..9b5e273f1a8ba 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 4acf91f22107f..75e2e296f10b1 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 48d86ef79199e..908dd28e7aaca 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 23d7111b9f77e..862c083604ee4 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
 



More information about the Python-checkins mailing list