Skip to content

Commit 7834ff2

Browse files
vstinnerLivius90
andauthored
bpo-21302: Add nanosleep() implementation for time.sleep() in Unix (GH-28545)
Co-authored-by: Livius <[email protected]>
1 parent 71f8ff4 commit 7834ff2

File tree

7 files changed

+114
-33
lines changed

7 files changed

+114
-33
lines changed

Doc/library/time.rst

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -364,16 +364,18 @@ Functions
364364
threads ready to run, the function returns immediately, and the thread
365365
continues execution.
366366

367-
Implementation:
367+
Unix implementation:
368368

369-
* On Unix, ``clock_nanosleep()`` is used if available (resolution: 1 ns),
370-
or ``select()`` is used otherwise (resolution: 1 us).
371-
* On Windows, a waitable timer is used (resolution: 100 ns). If *secs* is
372-
zero, ``Sleep(0)`` is used.
369+
* Use ``clock_nanosleep()`` if available (resolution: 1 ns);
370+
* Or use ``nanosleep()`` if available (resolution: 1 ns);
371+
* Or use ``select()`` (resolution: 1 us).
372+
373+
On Windows, a waitable timer is used (resolution: 100 ns). If *secs* is
374+
zero, ``Sleep(0)`` is used.
373375

374376
.. versionchanged:: 3.11
375-
On Unix, the ``clock_nanosleep()`` function is now used if available.
376-
On Windows, a waitable timer is now used.
377+
On Unix, the ``clock_nanosleep()`` and ``nanosleep()`` functions are now
378+
used if available. On Windows, a waitable timer is now used.
377379

378380
.. versionchanged:: 3.5
379381
The function now sleeps at least *secs* even if the sleep is interrupted

Doc/whatsnew/3.11.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,10 @@ sqlite3
242242
time
243243
----
244244

245-
* On Unix, :func:`time.sleep` now uses the ``clock_nanosleep()`` function, if
246-
available, which has a resolution of 1 ns (10^-6 sec), rather than using
247-
``select()`` which has a resolution of 1 us (10^-9 sec).
245+
* On Unix, :func:`time.sleep` now uses the ``clock_nanosleep()`` or
246+
``nanosleep()`` function, if available, which has a resolution of 1 ns (10^-6
247+
sec), rather than using ``select()`` which has a resolution of 1 us (10^-9
248+
sec).
248249
(Contributed by Livius and Victor Stinner in :issue:`21302`.)
249250

250251
* On Windows, :func:`time.sleep` now uses a waitable timer which has a
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

Modules/timemodule.c

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
#define SEC_TO_NS (1000 * 1000 * 1000)
6464

6565
/* Forward declarations */
66-
static int pysleep(_PyTime_t);
66+
static int pysleep(_PyTime_t timeout);
6767

6868

6969
static PyObject*
@@ -357,17 +357,17 @@ Return the clk_id of a thread's CPU time clock.");
357357
#endif /* HAVE_PTHREAD_GETCPUCLOCKID */
358358

359359
static PyObject *
360-
time_sleep(PyObject *self, PyObject *obj)
360+
time_sleep(PyObject *self, PyObject *timeout_obj)
361361
{
362-
_PyTime_t secs;
363-
if (_PyTime_FromSecondsObject(&secs, obj, _PyTime_ROUND_TIMEOUT))
362+
_PyTime_t timeout;
363+
if (_PyTime_FromSecondsObject(&timeout, timeout_obj, _PyTime_ROUND_TIMEOUT))
364364
return NULL;
365-
if (secs < 0) {
365+
if (timeout < 0) {
366366
PyErr_SetString(PyExc_ValueError,
367367
"sleep length must be non-negative");
368368
return NULL;
369369
}
370-
if (pysleep(secs) != 0) {
370+
if (pysleep(timeout) != 0) {
371371
return NULL;
372372
}
373373
Py_RETURN_NONE;
@@ -2050,48 +2050,57 @@ PyInit_time(void)
20502050
// On error, raise an exception and return -1.
20512051
// On success, return 0.
20522052
static int
2053-
pysleep(_PyTime_t secs)
2053+
pysleep(_PyTime_t timeout)
20542054
{
2055-
assert(secs >= 0);
2055+
assert(timeout >= 0);
20562056

20572057
#ifndef MS_WINDOWS
20582058
#ifdef HAVE_CLOCK_NANOSLEEP
20592059
struct timespec timeout_abs;
2060+
#elif defined(HAVE_NANOSLEEP)
2061+
struct timespec timeout_ts;
20602062
#else
2061-
struct timeval timeout;
2063+
struct timeval timeout_tv;
20622064
#endif
20632065
_PyTime_t deadline, monotonic;
20642066
int err = 0;
20652067

20662068
if (get_monotonic(&monotonic) < 0) {
20672069
return -1;
20682070
}
2069-
deadline = monotonic + secs;
2071+
deadline = monotonic + timeout;
20702072
#ifdef HAVE_CLOCK_NANOSLEEP
20712073
if (_PyTime_AsTimespec(deadline, &timeout_abs) < 0) {
20722074
return -1;
20732075
}
20742076
#endif
20752077

20762078
do {
2077-
#ifndef HAVE_CLOCK_NANOSLEEP
2078-
if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_CEILING) < 0) {
2079+
#ifdef HAVE_CLOCK_NANOSLEEP
2080+
// use timeout_abs
2081+
#elif defined(HAVE_NANOSLEEP)
2082+
if (_PyTime_AsTimespec(timeout, &timeout_ts) < 0) {
2083+
return -1;
2084+
}
2085+
#else
2086+
if (_PyTime_AsTimeval(timeout, &timeout_tv, _PyTime_ROUND_CEILING) < 0) {
20792087
return -1;
20802088
}
20812089
#endif
20822090

20832091
int ret;
2084-
#ifdef HAVE_CLOCK_NANOSLEEP
20852092
Py_BEGIN_ALLOW_THREADS
2093+
#ifdef HAVE_CLOCK_NANOSLEEP
20862094
ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &timeout_abs, NULL);
2087-
Py_END_ALLOW_THREADS
20882095
err = ret;
2096+
#elif defined(HAVE_NANOSLEEP)
2097+
ret = nanosleep(&timeout_ts, NULL);
2098+
err = errno;
20892099
#else
2090-
Py_BEGIN_ALLOW_THREADS
2091-
ret = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
2092-
Py_END_ALLOW_THREADS
2100+
ret = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout_tv);
20932101
err = errno;
20942102
#endif
2103+
Py_END_ALLOW_THREADS
20952104

20962105
if (ret == 0) {
20972106
break;
@@ -2112,8 +2121,8 @@ pysleep(_PyTime_t secs)
21122121
if (get_monotonic(&monotonic) < 0) {
21132122
return -1;
21142123
}
2115-
secs = deadline - monotonic;
2116-
if (secs < 0) {
2124+
timeout = deadline - monotonic;
2125+
if (timeout < 0) {
21172126
break;
21182127
}
21192128
/* retry with the recomputed delay */
@@ -2122,10 +2131,11 @@ pysleep(_PyTime_t secs)
21222131

21232132
return 0;
21242133
#else // MS_WINDOWS
2125-
_PyTime_t timeout = _PyTime_As100Nanoseconds(secs, _PyTime_ROUND_CEILING);
2134+
_PyTime_t timeout_100ns = _PyTime_As100Nanoseconds(timeout,
2135+
_PyTime_ROUND_CEILING);
21262136

21272137
// Maintain Windows Sleep() semantics for time.sleep(0)
2128-
if (timeout == 0) {
2138+
if (timeout_100ns == 0) {
21292139
Py_BEGIN_ALLOW_THREADS
21302140
// A value of zero causes the thread to relinquish the remainder of its
21312141
// time slice to any other thread that is ready to run. If there are no
@@ -2138,9 +2148,9 @@ pysleep(_PyTime_t secs)
21382148

21392149
LARGE_INTEGER relative_timeout;
21402150
// No need to check for integer overflow, both types are signed
2141-
assert(sizeof(relative_timeout) == sizeof(timeout));
2151+
assert(sizeof(relative_timeout) == sizeof(timeout_100ns));
21422152
// SetWaitableTimer(): a negative due time indicates relative time
2143-
relative_timeout.QuadPart = -timeout;
2153+
relative_timeout.QuadPart = -timeout_100ns;
21442154

21452155
HANDLE timer = CreateWaitableTimerW(NULL, FALSE, NULL);
21462156
if (timer == NULL) {

configure

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13310,6 +13310,64 @@ fi
1331013310
done
1331113311

1331213312

13313+
for ac_func in nanosleep
13314+
do :
13315+
ac_fn_c_check_func "$LINENO" "nanosleep" "ac_cv_func_nanosleep"
13316+
if test "x$ac_cv_func_nanosleep" = xyes; then :
13317+
cat >>confdefs.h <<_ACEOF
13318+
#define HAVE_NANOSLEEP 1
13319+
_ACEOF
13320+
13321+
else
13322+
13323+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for nanosleep in -lrt" >&5
13324+
$as_echo_n "checking for nanosleep in -lrt... " >&6; }
13325+
if ${ac_cv_lib_rt_nanosleep+:} false; then :
13326+
$as_echo_n "(cached) " >&6
13327+
else
13328+
ac_check_lib_save_LIBS=$LIBS
13329+
LIBS="-lrt $LIBS"
13330+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
13331+
/* end confdefs.h. */
13332+
13333+
/* Override any GCC internal prototype to avoid an error.
13334+
Use char because int might match the return type of a GCC
13335+
builtin and then its argument prototype would still apply. */
13336+
#ifdef __cplusplus
13337+
extern "C"
13338+
#endif
13339+
char nanosleep ();
13340+
int
13341+
main ()
13342+
{
13343+
return nanosleep ();
13344+
;
13345+
return 0;
13346+
}
13347+
_ACEOF
13348+
if ac_fn_c_try_link "$LINENO"; then :
13349+
ac_cv_lib_rt_nanosleep=yes
13350+
else
13351+
ac_cv_lib_rt_nanosleep=no
13352+
fi
13353+
rm -f core conftest.err conftest.$ac_objext \
13354+
conftest$ac_exeext conftest.$ac_ext
13355+
LIBS=$ac_check_lib_save_LIBS
13356+
fi
13357+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_nanosleep" >&5
13358+
$as_echo "$ac_cv_lib_rt_nanosleep" >&6; }
13359+
if test "x$ac_cv_lib_rt_nanosleep" = xyes; then :
13360+
13361+
$as_echo "#define HAVE_NANOSLEEP 1" >>confdefs.h
13362+
13363+
13364+
fi
13365+
13366+
13367+
fi
13368+
done
13369+
13370+
1331313371
for ac_func in clock_getres
1331413372
do :
1331513373
ac_fn_c_check_func "$LINENO" "clock_getres" "ac_cv_func_clock_getres"

configure.ac

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4121,6 +4121,12 @@ AC_CHECK_FUNCS(clock_nanosleep, [], [
41214121
])
41224122
])
41234123

4124+
AC_CHECK_FUNCS(nanosleep, [], [
4125+
AC_CHECK_LIB(rt, nanosleep, [
4126+
AC_DEFINE(HAVE_NANOSLEEP, 1)
4127+
])
4128+
])
4129+
41244130
AC_MSG_CHECKING(for major, minor, and makedev)
41254131
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
41264132
#if defined(MAJOR_IN_MKDEV)

pyconfig.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,9 @@
736736
/* Define to 1 if you have the `mremap' function. */
737737
#undef HAVE_MREMAP
738738

739+
/* Define to 1 if you have the `nanosleep' function. */
740+
#undef HAVE_NANOSLEEP
741+
739742
/* Define to 1 if you have the <ncurses.h> header file. */
740743
#undef HAVE_NCURSES_H
741744

0 commit comments

Comments
 (0)