mirror of https://github.com/python/cpython
bpo-21302: Add nanosleep() implementation for time.sleep() in Unix (GH-28545)
Co-authored-by: Livius <egyszeregy@freemail.hu>
This commit is contained in:
parent
71f8ff45c6
commit
7834ff26cb
|
@ -364,16 +364,18 @@ Functions
|
||||||
threads ready to run, the function returns immediately, and the thread
|
threads ready to run, the function returns immediately, and the thread
|
||||||
continues execution.
|
continues execution.
|
||||||
|
|
||||||
Implementation:
|
Unix implementation:
|
||||||
|
|
||||||
* On Unix, ``clock_nanosleep()`` is used if available (resolution: 1 ns),
|
* Use ``clock_nanosleep()`` if available (resolution: 1 ns);
|
||||||
or ``select()`` is used otherwise (resolution: 1 us).
|
* Or use ``nanosleep()`` if available (resolution: 1 ns);
|
||||||
* On Windows, a waitable timer is used (resolution: 100 ns). If *secs* is
|
* Or use ``select()`` (resolution: 1 us).
|
||||||
zero, ``Sleep(0)`` is used.
|
|
||||||
|
On Windows, a waitable timer is used (resolution: 100 ns). If *secs* is
|
||||||
|
zero, ``Sleep(0)`` is used.
|
||||||
|
|
||||||
.. versionchanged:: 3.11
|
.. versionchanged:: 3.11
|
||||||
On Unix, the ``clock_nanosleep()`` function is now used if available.
|
On Unix, the ``clock_nanosleep()`` and ``nanosleep()`` functions are now
|
||||||
On Windows, a waitable timer is now used.
|
used if available. On Windows, a waitable timer is now used.
|
||||||
|
|
||||||
.. versionchanged:: 3.5
|
.. versionchanged:: 3.5
|
||||||
The function now sleeps at least *secs* even if the sleep is interrupted
|
The function now sleeps at least *secs* even if the sleep is interrupted
|
||||||
|
|
|
@ -242,9 +242,10 @@ sqlite3
|
||||||
time
|
time
|
||||||
----
|
----
|
||||||
|
|
||||||
* On Unix, :func:`time.sleep` now uses the ``clock_nanosleep()`` function, if
|
* On Unix, :func:`time.sleep` now uses the ``clock_nanosleep()`` or
|
||||||
available, which has a resolution of 1 ns (10^-6 sec), rather than using
|
``nanosleep()`` function, if available, which has a resolution of 1 ns (10^-6
|
||||||
``select()`` which has a resolution of 1 us (10^-9 sec).
|
sec), rather than using ``select()`` which has a resolution of 1 us (10^-9
|
||||||
|
sec).
|
||||||
(Contributed by Livius and Victor Stinner in :issue:`21302`.)
|
(Contributed by Livius and Victor Stinner in :issue:`21302`.)
|
||||||
|
|
||||||
* On Windows, :func:`time.sleep` now uses a waitable timer which has a
|
* On Windows, :func:`time.sleep` now uses a waitable timer which has a
|
||||||
|
|
|
@ -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.
|
|
@ -63,7 +63,7 @@
|
||||||
#define SEC_TO_NS (1000 * 1000 * 1000)
|
#define SEC_TO_NS (1000 * 1000 * 1000)
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
static int pysleep(_PyTime_t);
|
static int pysleep(_PyTime_t timeout);
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
|
@ -357,17 +357,17 @@ Return the clk_id of a thread's CPU time clock.");
|
||||||
#endif /* HAVE_PTHREAD_GETCPUCLOCKID */
|
#endif /* HAVE_PTHREAD_GETCPUCLOCKID */
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_sleep(PyObject *self, PyObject *obj)
|
time_sleep(PyObject *self, PyObject *timeout_obj)
|
||||||
{
|
{
|
||||||
_PyTime_t secs;
|
_PyTime_t timeout;
|
||||||
if (_PyTime_FromSecondsObject(&secs, obj, _PyTime_ROUND_TIMEOUT))
|
if (_PyTime_FromSecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (secs < 0) {
|
if (timeout < 0) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
PyErr_SetString(PyExc_ValueError,
|
||||||
"sleep length must be non-negative");
|
"sleep length must be non-negative");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (pysleep(secs) != 0) {
|
if (pysleep(timeout) != 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -2050,15 +2050,17 @@ PyInit_time(void)
|
||||||
// On error, raise an exception and return -1.
|
// On error, raise an exception and return -1.
|
||||||
// On success, return 0.
|
// On success, return 0.
|
||||||
static int
|
static int
|
||||||
pysleep(_PyTime_t secs)
|
pysleep(_PyTime_t timeout)
|
||||||
{
|
{
|
||||||
assert(secs >= 0);
|
assert(timeout >= 0);
|
||||||
|
|
||||||
#ifndef MS_WINDOWS
|
#ifndef MS_WINDOWS
|
||||||
#ifdef HAVE_CLOCK_NANOSLEEP
|
#ifdef HAVE_CLOCK_NANOSLEEP
|
||||||
struct timespec timeout_abs;
|
struct timespec timeout_abs;
|
||||||
|
#elif defined(HAVE_NANOSLEEP)
|
||||||
|
struct timespec timeout_ts;
|
||||||
#else
|
#else
|
||||||
struct timeval timeout;
|
struct timeval timeout_tv;
|
||||||
#endif
|
#endif
|
||||||
_PyTime_t deadline, monotonic;
|
_PyTime_t deadline, monotonic;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
@ -2066,7 +2068,7 @@ pysleep(_PyTime_t secs)
|
||||||
if (get_monotonic(&monotonic) < 0) {
|
if (get_monotonic(&monotonic) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
deadline = monotonic + secs;
|
deadline = monotonic + timeout;
|
||||||
#ifdef HAVE_CLOCK_NANOSLEEP
|
#ifdef HAVE_CLOCK_NANOSLEEP
|
||||||
if (_PyTime_AsTimespec(deadline, &timeout_abs) < 0) {
|
if (_PyTime_AsTimespec(deadline, &timeout_abs) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2074,24 +2076,31 @@ pysleep(_PyTime_t secs)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
do {
|
do {
|
||||||
#ifndef HAVE_CLOCK_NANOSLEEP
|
#ifdef HAVE_CLOCK_NANOSLEEP
|
||||||
if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_CEILING) < 0) {
|
// 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;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
#ifdef HAVE_CLOCK_NANOSLEEP
|
#ifdef HAVE_CLOCK_NANOSLEEP
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &timeout_abs, NULL);
|
ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &timeout_abs, NULL);
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
err = ret;
|
err = ret;
|
||||||
|
#elif defined(HAVE_NANOSLEEP)
|
||||||
|
ret = nanosleep(&timeout_ts, NULL);
|
||||||
|
err = errno;
|
||||||
#else
|
#else
|
||||||
Py_BEGIN_ALLOW_THREADS
|
ret = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout_tv);
|
||||||
ret = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
|
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
err = errno;
|
err = errno;
|
||||||
#endif
|
#endif
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
break;
|
break;
|
||||||
|
@ -2112,8 +2121,8 @@ pysleep(_PyTime_t secs)
|
||||||
if (get_monotonic(&monotonic) < 0) {
|
if (get_monotonic(&monotonic) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
secs = deadline - monotonic;
|
timeout = deadline - monotonic;
|
||||||
if (secs < 0) {
|
if (timeout < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* retry with the recomputed delay */
|
/* retry with the recomputed delay */
|
||||||
|
@ -2122,10 +2131,11 @@ pysleep(_PyTime_t secs)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
#else // MS_WINDOWS
|
#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)
|
// Maintain Windows Sleep() semantics for time.sleep(0)
|
||||||
if (timeout == 0) {
|
if (timeout_100ns == 0) {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
// A value of zero causes the thread to relinquish the remainder of its
|
// 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
|
// 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;
|
LARGE_INTEGER relative_timeout;
|
||||||
// No need to check for integer overflow, both types are signed
|
// 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
|
// SetWaitableTimer(): a negative due time indicates relative time
|
||||||
relative_timeout.QuadPart = -timeout;
|
relative_timeout.QuadPart = -timeout_100ns;
|
||||||
|
|
||||||
HANDLE timer = CreateWaitableTimerW(NULL, FALSE, NULL);
|
HANDLE timer = CreateWaitableTimerW(NULL, FALSE, NULL);
|
||||||
if (timer == NULL) {
|
if (timer == NULL) {
|
||||||
|
|
|
@ -13310,6 +13310,64 @@ fi
|
||||||
done
|
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
|
for ac_func in clock_getres
|
||||||
do :
|
do :
|
||||||
ac_fn_c_check_func "$LINENO" "clock_getres" "ac_cv_func_clock_getres"
|
ac_fn_c_check_func "$LINENO" "clock_getres" "ac_cv_func_clock_getres"
|
||||||
|
|
|
@ -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_MSG_CHECKING(for major, minor, and makedev)
|
||||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
|
||||||
#if defined(MAJOR_IN_MKDEV)
|
#if defined(MAJOR_IN_MKDEV)
|
||||||
|
|
|
@ -736,6 +736,9 @@
|
||||||
/* Define to 1 if you have the `mremap' function. */
|
/* Define to 1 if you have the `mremap' function. */
|
||||||
#undef HAVE_MREMAP
|
#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. */
|
/* Define to 1 if you have the <ncurses.h> header file. */
|
||||||
#undef HAVE_NCURSES_H
|
#undef HAVE_NCURSES_H
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue