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.
This commit is contained in:
Victor Stinner 2021-10-01 09:55:28 +02:00 committed by GitHub
parent 3e1c5d989a
commit 1ee0f94d16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 216 additions and 128 deletions

View File

@ -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
----

View File

@ -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.

View File

@ -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,18 +514,25 @@ 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");
}

279
configure vendored
View File

@ -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,122 +13295,6 @@ fi
done
for ac_func in clock_nanosleep
do :
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_NANOSLEEP 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_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lrt $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 clock_nanosleep ();
int
main ()
{
return clock_nanosleep ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_rt_clock_nanosleep=yes
else
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_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_NANOSLEEP 1" >>confdefs.h
fi
fi
done
for ac_func in nanosleep
do :
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_NANOSLEEP 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_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lrt $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 nanosleep ();
int
main ()
{
return nanosleep ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_rt_nanosleep=yes
else
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_nanosleep" >&5
$as_echo "$ac_cv_lib_rt_nanosleep" >&6; }
if test "x$ac_cv_lib_rt_nanosleep" = xyes; then :
$as_echo "#define HAVE_NANOSLEEP 1" >>confdefs.h
fi
fi
done
for ac_func in clock_getres
do :
ac_fn_c_check_func "$LINENO" "clock_getres" "ac_cv_func_clock_getres"
@ -13484,6 +13411,122 @@ fi
done
for ac_func in clock_nanosleep
do :
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_NANOSLEEP 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_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lrt $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 clock_nanosleep ();
int
main ()
{
return clock_nanosleep ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_rt_clock_nanosleep=yes
else
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_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_NANOSLEEP 1" >>confdefs.h
fi
fi
done
for ac_func in nanosleep
do :
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_NANOSLEEP 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_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lrt $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 nanosleep ();
int
main ()
{
return nanosleep ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_rt_nanosleep=yes
else
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_nanosleep" >&5
$as_echo "$ac_cv_lib_rt_nanosleep" >&6; }
if test "x$ac_cv_lib_rt_nanosleep" = xyes; then :
$as_echo "#define HAVE_NANOSLEEP 1" >>confdefs.h
fi
fi
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for major" >&5
$as_echo_n "checking for major... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext

View File

@ -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 \

View File

@ -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