Skip to content

bpo-21302: Add Waitable timer implementation for time.sleep() in Windows #28341

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

Closed
wants to merge 59 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
55faeb6
clock_nanosleep() implementation for time.sleep()
Livius90 Sep 1, 2021
c7bae3b
Follow PEP 7 rules in time.sleep()
Livius90 Sep 1, 2021
b3a5c5f
Adjust braces to PEP 7
Livius90 Sep 1, 2021
3e07f39
clock_nanosleep check placed after clock_settime
Livius90 Sep 1, 2021
e06ba84
Fix EINTR issue for clock_nanosleep()
Livius90 Sep 1, 2021
6e6d4a3
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 1, 2021
238f073
nanosleep() is available for Unix systems like OSX, FreeBSD
Livius90 Sep 1, 2021
b5a5c6e
Update news rst file
Livius90 Sep 1, 2021
d064255
replace select() with nanosleep()
Livius90 Sep 2, 2021
b70f187
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 2, 2021
6ad7c14
Fix test_eintr issue for macOS
Livius90 Sep 2, 2021
74c45e1
Remove news rst and regenerate
Livius90 Sep 2, 2021
5988222
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 2, 2021
129b0a0
Use waitable timer from Win32 for time.sleep() in Windows
Livius90 Sep 3, 2021
d89e13b
Finalized waitable timer for time.sleep() in Windows
Livius90 Sep 3, 2021
47249f5
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 3, 2021
0bf8330
Fix macOS build issue
Livius90 Sep 3, 2021
085cbed
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 3, 2021
2242638
Final optimization in Waitable timer object implementation
Livius90 Sep 4, 2021
db7851b
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 4, 2021
f7387e8
revert to original
Livius90 Sep 9, 2021
a35b0b4
remove news
Livius90 Sep 9, 2021
3abf812
add clock_nanosleep() only
Livius90 Sep 9, 2021
4cbfde2
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 10, 2021
d99c06e
Update Misc/NEWS.d/next/Library/2021-09-10-00-24-08.bpo-21302.XS_frc.rst
Livius90 Sep 10, 2021
117dc62
timeout_abs renaming
Livius90 Sep 10, 2021
92061f3
Simplified HAVE_CLOCK_NANOSLEEP macro blocks
Livius90 Sep 10, 2021
f045047
remake news
Livius90 Sep 10, 2021
24f9975
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 10, 2021
34e3883
Fix err checking
Livius90 Sep 10, 2021
fec634f
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 10, 2021
8022062
revert changes
Livius90 Sep 10, 2021
4685ec7
get_monotonic() and secs re-calculation are useless for clock_nanosle…
Livius90 Sep 10, 2021
0ad19e0
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 10, 2021
0839d0d
merge errno in HAVE_CLOCK_NANOSLEEP
Livius90 Sep 11, 2021
39cff8f
remake news rst
Livius90 Sep 11, 2021
87d5712
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 11, 2021
56cb8e1
Try to fix Address sanitizer test issue
Livius90 Sep 11, 2021
8ec03be
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 11, 2021
b1ccdba
#2 Try to fix Address sanitizer test issue
Livius90 Sep 11, 2021
c8372c8
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 11, 2021
1cf2205
remake news rst
Livius90 Sep 11, 2021
1e725da
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 11, 2021
3566c59
re-make news rst
Livius90 Sep 11, 2021
278aaa2
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 11, 2021
beaecab
Merge branch 'python:main' into bpo-21302
Livius90 Sep 14, 2021
df7e4c9
Add Waitable timer implmentation in time.sleep() for Windows
Livius90 Sep 14, 2021
79a06c4
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 14, 2021
6fb2c1d
Revert "📜🤖 Added by blurb_it."
Livius90 Sep 14, 2021
41fe5d8
Fix timer handle closing, simplify ifdef blocks
Livius90 Sep 14, 2021
6fc19ad
Merge branch 'python:main' into bpo-21302
Livius90 Sep 15, 2021
ac8ab07
Add _PyTime_As100Nanoseconds()
Livius90 Sep 15, 2021
b79ce48
Fix test_eintr failed issue in macOS
Livius90 Sep 15, 2021
7a3fbc2
📜🤖 Added by blurb_it.
blurb-it[bot] Sep 15, 2021
4e7c4eb
time.sleep() sleep again if SIGINT does not raise an exception
Livius90 Sep 16, 2021
ab17401
use WaitForMultipleObjectsEx()
Livius90 Sep 16, 2021
d2eb0fc
Some code refactoring and finalizing
Livius90 Sep 18, 2021
aad2c31
Fulfilling requests of last review
Livius90 Sep 20, 2021
0daf080
Update news rst
Livius90 Sep 20, 2021
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
5 changes: 5 additions & 0 deletions Include/cpython/pytime.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ PyAPI_FUNC(_PyTime_t) _PyTime_AsMicroseconds(_PyTime_t t,
/* Convert timestamp to a number of nanoseconds (10^-9 seconds). */
PyAPI_FUNC(_PyTime_t) _PyTime_AsNanoseconds(_PyTime_t t);

/* Convert timestamp to a number of 100 nanoseconds (10^-7 seconds).
Can be used for Waitable timer object in Windows. */
PyAPI_FUNC(_PyTime_t) _PyTime_As100Nanoseconds(_PyTime_t t,
_PyTime_round_t round);

/* Convert timestamp to a number of nanoseconds (10^-9 seconds) as a Python int
object. */
PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In Windows operating system, :func:`time.sleep` now uses the ``waitable timer`` object, which allows to sleep for an interval absolute time. Desired sleep time can be set in 100 nsecs resolution.
119 changes: 89 additions & 30 deletions Modules/timemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2044,41 +2044,42 @@ PyInit_time(void)
return PyModuleDef_Init(&timemodule);
}

#ifdef MS_WINDOWS
enum event_indexes {
EVENT_TIMER,
EVENT_INTERRUPT,
EVENT_SIZE
};
#endif

/* Implement pysleep() for various platforms.
When interrupted (or when another error occurs), return -1 and
set an exception; else return 0. */

static int
pysleep(_PyTime_t secs)
{
_PyTime_t deadline, monotonic;
int ret = -1;
#ifndef MS_WINDOWS
#ifdef HAVE_CLOCK_NANOSLEEP
struct timespec timeout_abs;
#else
struct timeval timeout;
#endif
_PyTime_t deadline, monotonic;
int err = 0;
int ret = 0;
#else
_PyTime_t millisecs;
unsigned long ul_millis;
DWORD rc;
HANDLE hInterruptEvent;
#endif

if (get_monotonic(&monotonic) < 0) {
return -1;
}
deadline = monotonic + secs;
#if defined(HAVE_CLOCK_NANOSLEEP) && !defined(MS_WINDOWS)
#ifdef HAVE_CLOCK_NANOSLEEP
if (_PyTime_AsTimespec(deadline, &timeout_abs) < 0) {
return -1;
}
#endif

do {
#ifndef MS_WINDOWS
#ifndef HAVE_CLOCK_NANOSLEEP
if (_PyTime_AsTimeval(secs, &timeout, _PyTime_ROUND_CEILING) < 0) {
return -1;
Expand Down Expand Up @@ -2106,12 +2107,56 @@ pysleep(_PyTime_t secs)
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}

/* sleep was interrupted by SIGINT */
if (PyErr_CheckSignals()) {
return -1;
}

#ifndef HAVE_CLOCK_NANOSLEEP
if (get_monotonic(&monotonic) < 0) {
return -1;
}
secs = deadline - monotonic;
if (secs < 0) {
ret = 0;
break;
}
/* retry with the recomputed delay */
#endif
} while (1);
#else
_PyTime_t millisecs;
DWORD ul_millis;
DWORD rc;
FILETIME FileTime;
LARGE_INTEGER deadline;
/* hTimer, hInterruptEvent */
HANDLE hEvents[EVENT_SIZE] = {NULL, NULL};

GetSystemTimePreciseAsFileTime(&FileTime);
CopyMemory(&deadline, &FileTime, sizeof(FILETIME));

/* convert to 100 nsec unit and add */
deadline.QuadPart += _PyTime_As100Nanoseconds(secs, _PyTime_ROUND_CEILING);

hEvents[EVENT_TIMER] = CreateWaitableTimerW(NULL, FALSE, NULL);
if (NULL == hEvents[EVENT_TIMER]) {
return -1;
}

if (!SetWaitableTimer(hEvents[EVENT_TIMER], &deadline, 0, NULL, NULL, FALSE)) {
ret = -1;
goto exit;
}

do {
millisecs = _PyTime_AsMilliseconds(secs, _PyTime_ROUND_CEILING);
if (millisecs > (double)ULONG_MAX) {
if (millisecs > ((_PyTime_t)ULONG_MAX)) {
PyErr_SetString(PyExc_OverflowError,
"sleep length is too large");
return -1;
"sleep length is too large");
ret = -1;
break;
}

/* Allow sleep(0) to maintain win32 semantics, and as decreed
Expand All @@ -2122,36 +2167,50 @@ pysleep(_PyTime_t secs)
Py_BEGIN_ALLOW_THREADS
Sleep(ul_millis);
Py_END_ALLOW_THREADS
ret = 0;
break;
}

hInterruptEvent = _PyOS_SigintEvent();
ResetEvent(hInterruptEvent);
hEvents[EVENT_INTERRUPT] = _PyOS_SigintEvent();
ResetEvent(hEvents[EVENT_INTERRUPT]);

Py_BEGIN_ALLOW_THREADS
rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
rc = WaitForMultipleObjectsEx(EVENT_SIZE, hEvents, FALSE, INFINITE, FALSE);
Py_END_ALLOW_THREADS

if (rc != WAIT_OBJECT_0)
/* Something went wrong */
if (rc == WAIT_FAILED) {
ret = -1;
break;
#endif

/* sleep was interrupted by SIGINT */
if (PyErr_CheckSignals()) {
return -1;
}

#ifndef HAVE_CLOCK_NANOSLEEP
if (get_monotonic(&monotonic) < 0) {
return -1;
/* Timer signaled: we are done */
if (rc == (WAIT_OBJECT_0 + EVENT_TIMER)) {
ret = 0;
break;
}
secs = deadline - monotonic;
if (secs < 0) {

/* Does it make sense to check? */
//if (rc != (WAIT_OBJECT_0 + EVENT_INTERRUPT)) {
// ret = 0;
// break;
//}

/* sleep was interrupted by SIGINT */
if (PyErr_CheckSignals()) {
ret = -1;
break;
}
/* retry with the recomputed delay */
#endif

/* The SIGINT signal handler was called and did not raise a Python exception.
* Restart sleeping. The timer deadline doesn't need to be recomputed:
* SetWaitableTimer() uses absolute time.
*/
} while (1);

return 0;
exit:
CloseHandle(hEvents[EVENT_TIMER]);
#endif

return ret;
}
9 changes: 9 additions & 0 deletions Python/pytime.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
/* Conversion from nanoseconds */
#define NS_TO_MS (1000 * 1000)
#define NS_TO_US (1000)
#define NS_TO_100NS (100)


static void
Expand Down Expand Up @@ -568,6 +569,14 @@ _PyTime_AsNanoseconds(_PyTime_t t)
}


_PyTime_t
_PyTime_As100Nanoseconds(_PyTime_t t, _PyTime_round_t round)
{
_PyTime_t ns = pytime_as_nanoseconds(t);
return pytime_divide(ns, NS_TO_100NS, round);
}


_PyTime_t
_PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round)
{
Expand Down