[Python-checkins] bpo-21302: Add nanosleep() implementation for time.sleep() in Unix (GH-28545)

vstinner webhook-mailer at python.org
Sat Sep 25 08:36:35 EDT 2021


https://github.com/python/cpython/commit/7834ff26cbcd4d8394d64d80d9f51a364d38b1c6
commit: 7834ff26cbcd4d8394d64d80d9f51a364d38b1c6
branch: main
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2021-09-25T14:36:26+02:00
summary:

bpo-21302: Add nanosleep() implementation for time.sleep() in Unix (GH-28545)

Co-authored-by: Livius <egyszeregy at freemail.hu>

files:
A Misc/NEWS.d/next/Library/2021-09-22-23-56-15.bpo-21302.vvQ3Su.rst
M Doc/library/time.rst
M Doc/whatsnew/3.11.rst
M Modules/timemodule.c
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Doc/library/time.rst b/Doc/library/time.rst
index d91862cc38be8..69e5274f1cee7 100644
--- a/Doc/library/time.rst
+++ b/Doc/library/time.rst
@@ -364,16 +364,18 @@ Functions
    threads ready to run, the function returns immediately, and the thread
    continues execution.
 
-   Implementation:
+   Unix implementation:
 
-   * On Unix, ``clock_nanosleep()`` is used if available (resolution: 1 ns),
-     or ``select()`` is used otherwise (resolution: 1 us).
-   * On Windows, a waitable timer is used (resolution: 100 ns). If *secs* is
-     zero, ``Sleep(0)`` is used.
+   * Use ``clock_nanosleep()`` if available (resolution: 1 ns);
+   * Or use ``nanosleep()`` if available (resolution: 1 ns);
+   * Or use ``select()`` (resolution: 1 us).
+
+   On Windows, a waitable timer is used (resolution: 100 ns). If *secs* is
+   zero, ``Sleep(0)`` is used.
 
    .. versionchanged:: 3.11
-      On Unix, the ``clock_nanosleep()`` function is now used if available.
-      On Windows, a waitable timer is now used.
+      On Unix, the ``clock_nanosleep()`` and ``nanosleep()`` functions are now
+      used if available. On Windows, a waitable timer is now used.
 
    .. versionchanged:: 3.5
       The function now sleeps at least *secs* even if the sleep is interrupted
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 0e56b462f1231..818208edbf263 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -242,9 +242,10 @@ sqlite3
 time
 ----
 
-* On Unix, :func:`time.sleep` now uses the ``clock_nanosleep()`` function, if
-  available, which has a resolution of 1 ns (10^-6 sec), rather than using
-  ``select()`` which has a resolution of 1 us (10^-9 sec).
+* On Unix, :func:`time.sleep` now uses the ``clock_nanosleep()`` or
+  ``nanosleep()`` function, if available, which has a resolution of 1 ns (10^-6
+  sec), rather than using ``select()`` which has a resolution of 1 us (10^-9
+  sec).
   (Contributed by Livius and Victor Stinner in :issue:`21302`.)
 
 * On Windows, :func:`time.sleep` now uses a waitable timer which has a
diff --git a/Misc/NEWS.d/next/Library/2021-09-22-23-56-15.bpo-21302.vvQ3Su.rst b/Misc/NEWS.d/next/Library/2021-09-22-23-56-15.bpo-21302.vvQ3Su.rst
new file mode 100644
index 0000000000000..52ee8d7cc64f1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-09-22-23-56-15.bpo-21302.vvQ3Su.rst
@@ -0,0 +1 @@
+In Unix operating systems, :func:`time.sleep` now uses the ``nanosleep()`` function, if ``clock_nanosleep()`` is not available but ``nanosleep()`` is available. ``nanosleep()`` allows to sleep with nanosecond precision.
\ No newline at end of file
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index 53ec86eb3981e..4639afa590a4f 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -63,7 +63,7 @@
 #define SEC_TO_NS (1000 * 1000 * 1000)
 
 /* Forward declarations */
-static int pysleep(_PyTime_t);
+static int pysleep(_PyTime_t timeout);
 
 
 static PyObject*
@@ -357,17 +357,17 @@ Return the clk_id of a thread's CPU time clock.");
 #endif /* HAVE_PTHREAD_GETCPUCLOCKID */
 
 static PyObject *
-time_sleep(PyObject *self, PyObject *obj)
+time_sleep(PyObject *self, PyObject *timeout_obj)
 {
-    _PyTime_t secs;
-    if (_PyTime_FromSecondsObject(&secs, obj, _PyTime_ROUND_TIMEOUT))
+    _PyTime_t timeout;
+    if (_PyTime_FromSecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT))
         return NULL;
-    if (secs < 0) {
+    if (timeout < 0) {
         PyErr_SetString(PyExc_ValueError,
                         "sleep length must be non-negative");
         return NULL;
     }
-    if (pysleep(secs) != 0) {
+    if (pysleep(timeout) != 0) {
         return NULL;
     }
     Py_RETURN_NONE;
@@ -2050,15 +2050,17 @@ PyInit_time(void)
 // On error, raise an exception and return -1.
 // On success, return 0.
 static int
-pysleep(_PyTime_t secs)
+pysleep(_PyTime_t timeout)
 {
-    assert(secs >= 0);
+    assert(timeout >= 0);
 
 #ifndef MS_WINDOWS
 #ifdef HAVE_CLOCK_NANOSLEEP
     struct timespec timeout_abs;
+#elif defined(HAVE_NANOSLEEP)
+    struct timespec timeout_ts;
 #else
-    struct timeval timeout;
+    struct timeval timeout_tv;
 #endif
     _PyTime_t deadline, monotonic;
     int err = 0;
@@ -2066,7 +2068,7 @@ pysleep(_PyTime_t secs)
     if (get_monotonic(&monotonic) < 0) {
         return -1;
     }
-    deadline = monotonic + secs;
+    deadline = monotonic + timeout;
 #ifdef HAVE_CLOCK_NANOSLEEP
     if (_PyTime_AsTimespec(deadline, &timeout_abs) < 0) {
         return -1;
@@ -2074,24 +2076,31 @@ pysleep(_PyTime_t secs)
 #endif
 
     do {
-#ifndef HAVE_CLOCK_NANOSLEEP
-        if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_CEILING) < 0) {
+#ifdef HAVE_CLOCK_NANOSLEEP
+        // use timeout_abs
+#elif defined(HAVE_NANOSLEEP)
+        if (_PyTime_AsTimespec(timeout, &timeout_ts) < 0) {
+            return -1;
+        }
+#else
+        if (_PyTime_AsTimeval(timeout, &timeout_tv, _PyTime_ROUND_CEILING) < 0) {
             return -1;
         }
 #endif
 
         int ret;
-#ifdef HAVE_CLOCK_NANOSLEEP
         Py_BEGIN_ALLOW_THREADS
+#ifdef HAVE_CLOCK_NANOSLEEP
         ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &timeout_abs, NULL);
-        Py_END_ALLOW_THREADS
         err = ret;
+#elif defined(HAVE_NANOSLEEP)
+        ret = nanosleep(&timeout_ts, NULL);
+        err = errno;
 #else
-        Py_BEGIN_ALLOW_THREADS
-        ret = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
-        Py_END_ALLOW_THREADS
+        ret = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout_tv);
         err = errno;
 #endif
+        Py_END_ALLOW_THREADS
 
         if (ret == 0) {
             break;
@@ -2112,8 +2121,8 @@ pysleep(_PyTime_t secs)
         if (get_monotonic(&monotonic) < 0) {
             return -1;
         }
-        secs = deadline - monotonic;
-        if (secs < 0) {
+        timeout = deadline - monotonic;
+        if (timeout < 0) {
             break;
         }
         /* retry with the recomputed delay */
@@ -2122,10 +2131,11 @@ pysleep(_PyTime_t secs)
 
     return 0;
 #else  // MS_WINDOWS
-    _PyTime_t timeout = _PyTime_As100Nanoseconds(secs, _PyTime_ROUND_CEILING);
+    _PyTime_t timeout_100ns = _PyTime_As100Nanoseconds(timeout,
+                                                       _PyTime_ROUND_CEILING);
 
     // Maintain Windows Sleep() semantics for time.sleep(0)
-    if (timeout == 0) {
+    if (timeout_100ns == 0) {
         Py_BEGIN_ALLOW_THREADS
         // A value of zero causes the thread to relinquish the remainder of its
         // time slice to any other thread that is ready to run. If there are no
@@ -2138,9 +2148,9 @@ pysleep(_PyTime_t secs)
 
     LARGE_INTEGER relative_timeout;
     // No need to check for integer overflow, both types are signed
-    assert(sizeof(relative_timeout) == sizeof(timeout));
+    assert(sizeof(relative_timeout) == sizeof(timeout_100ns));
     // SetWaitableTimer(): a negative due time indicates relative time
-    relative_timeout.QuadPart = -timeout;
+    relative_timeout.QuadPart = -timeout_100ns;
 
     HANDLE timer = CreateWaitableTimerW(NULL, FALSE, NULL);
     if (timer == NULL) {
diff --git a/configure b/configure
index 2e3c9ba7baddd..4acf91f22107f 100755
--- a/configure
+++ b/configure
@@ -13310,6 +13310,64 @@ 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"
diff --git a/configure.ac b/configure.ac
index 4a0694c442f3f..48d86ef79199e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4121,6 +4121,12 @@ AC_CHECK_FUNCS(clock_nanosleep, [], [
     ])
 ])
 
+AC_CHECK_FUNCS(nanosleep, [], [
+    AC_CHECK_LIB(rt, nanosleep, [
+        AC_DEFINE(HAVE_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 d6408e9415e2d..23d7111b9f77e 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -736,6 +736,9 @@
 /* Define to 1 if you have the `mremap' function. */
 #undef HAVE_MREMAP
 
+/* Define to 1 if you have the `nanosleep' function. */
+#undef HAVE_NANOSLEEP
+
 /* Define to 1 if you have the <ncurses.h> header file. */
 #undef HAVE_NCURSES_H
 



More information about the Python-checkins mailing list