Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions Doc/library/time.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
56 changes: 33 additions & 23 deletions Modules/timemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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*
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2050,48 +2050,57 @@ 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;

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;
}
#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;
Expand All @@ -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 */
Expand All @@ -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
Expand All @@ -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) {
Expand Down
58 changes: 58 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 6 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions pyconfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down