From 85a4748118c3793be7047ecbcbfc79dd07cb2a75 Mon Sep 17 00:00:00 2001 From: Livius Date: Mon, 13 Sep 2021 14:37:38 +0200 Subject: [PATCH] bpo-21302: Add clock_nanosleep() implementation for time.sleep() (GH-28111) In Unix operating systems, time.sleep() now uses the clock_nanosleep() function, if available, which allows to sleep for an interval specified with nanosecond precision. Co-authored-by: Victor Stinner --- .../2021-09-11-18-44-40.bpo-21302.QxHRpR.rst | 2 + Modules/timemodule.c | 36 ++++++++++-- configure | 58 +++++++++++++++++++ configure.ac | 6 ++ pyconfig.h.in | 3 + 5 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-09-11-18-44-40.bpo-21302.QxHRpR.rst diff --git a/Misc/NEWS.d/next/Library/2021-09-11-18-44-40.bpo-21302.QxHRpR.rst b/Misc/NEWS.d/next/Library/2021-09-11-18-44-40.bpo-21302.QxHRpR.rst new file mode 100644 index 00000000000..86f0a5ac4c2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-09-11-18-44-40.bpo-21302.QxHRpR.rst @@ -0,0 +1,2 @@ +In Unix operating systems, :func:`time.sleep` now uses the ``clock_nanosleep()`` function, +if available, which allows to sleep for an interval specified with nanosecond precision. \ No newline at end of file diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 4caacc3b64d..cf58a18caf1 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -2053,8 +2053,13 @@ pysleep(_PyTime_t secs) { _PyTime_t deadline, monotonic; #ifndef MS_WINDOWS +#ifdef HAVE_CLOCK_NANOSLEEP + struct timespec timeout_abs; +#else struct timeval timeout; +#endif int err = 0; + int ret = 0; #else _PyTime_t millisecs; unsigned long ul_millis; @@ -2066,20 +2071,38 @@ pysleep(_PyTime_t secs) return -1; } deadline = monotonic + secs; +#if defined(HAVE_CLOCK_NANOSLEEP) && !defined(MS_WINDOWS) + if (_PyTime_AsTimespec(deadline, &timeout_abs) < 0) { + return -1; + } +#endif do { #ifndef MS_WINDOWS - if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_CEILING) < 0) +#ifndef HAVE_CLOCK_NANOSLEEP + if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_CEILING) < 0) { return -1; + } +#endif +#ifdef HAVE_CLOCK_NANOSLEEP Py_BEGIN_ALLOW_THREADS - err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout); + ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &timeout_abs, NULL); Py_END_ALLOW_THREADS + err = ret; +#else + Py_BEGIN_ALLOW_THREADS + ret = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout); + Py_END_ALLOW_THREADS + err = errno; +#endif - if (err == 0) + if (ret == 0) { break; + } - if (errno != EINTR) { + if (err != EINTR) { + errno = err; PyErr_SetFromErrno(PyExc_OSError); return -1; } @@ -2114,9 +2137,11 @@ pysleep(_PyTime_t secs) #endif /* sleep was interrupted by SIGINT */ - if (PyErr_CheckSignals()) + if (PyErr_CheckSignals()) { return -1; + } +#ifndef HAVE_CLOCK_NANOSLEEP if (get_monotonic(&monotonic) < 0) { return -1; } @@ -2125,6 +2150,7 @@ pysleep(_PyTime_t secs) break; } /* retry with the recomputed delay */ +#endif } while (1); return 0; diff --git a/configure b/configure index 4f12972540d..2e3c9ba7bad 100755 --- a/configure +++ b/configure @@ -13252,6 +13252,64 @@ 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 clock_getres do : ac_fn_c_check_func "$LINENO" "clock_getres" "ac_cv_func_clock_getres" diff --git a/configure.ac b/configure.ac index bcb205497c1..4a0694c442f 100644 --- a/configure.ac +++ b/configure.ac @@ -4115,6 +4115,12 @@ AC_CHECK_FUNCS(clock_settime, [], [ ]) ]) +AC_CHECK_FUNCS(clock_nanosleep, [], [ + AC_CHECK_LIB(rt, clock_nanosleep, [ + AC_DEFINE(HAVE_CLOCK_NANOSLEEP, 1) + ]) +]) + AC_MSG_CHECKING(for major, minor, and makedev) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #if defined(MAJOR_IN_MKDEV) diff --git a/pyconfig.h.in b/pyconfig.h.in index 49407ab62b4..d6408e9415e 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -136,6 +136,9 @@ /* 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