Issue #23646: If time.sleep() is interrupted by a signal, the sleep is now
retried with the recomputed delay, except if the signal handler raises an exception (PEP 475). Modify also test_signal to use a monotonic clock instead of the system clock.
This commit is contained in:
parent
0ed56a0b42
commit
79d68f929d
|
@ -350,6 +350,11 @@ The module defines the following functions and data items:
|
||||||
requested by an arbitrary amount because of the scheduling of other activity
|
requested by an arbitrary amount because of the scheduling of other activity
|
||||||
in the system.
|
in the system.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.5
|
||||||
|
The function now sleeps at least *secs* even if the sleep is interrupted
|
||||||
|
by a signal, except if the signal handler raises an exception (see
|
||||||
|
:pep:`475` for the rationale).
|
||||||
|
|
||||||
|
|
||||||
.. function:: strftime(format[, t])
|
.. function:: strftime(format[, t])
|
||||||
|
|
||||||
|
|
|
@ -252,8 +252,26 @@ class SocketEINTRTest(EINTRBaseTest):
|
||||||
lambda path: os.close(os.open(path, os.O_WRONLY)))
|
lambda path: os.close(os.open(path, os.O_WRONLY)))
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(signal, "setitimer"), "requires setitimer()")
|
||||||
|
class TimeEINTRTest(EINTRBaseTest):
|
||||||
|
""" EINTR tests for the time module. """
|
||||||
|
|
||||||
|
def test_sleep(self):
|
||||||
|
t0 = time.monotonic()
|
||||||
|
# time.sleep() may retry when interrupted by a signal
|
||||||
|
time.sleep(2)
|
||||||
|
signal.alarm(0)
|
||||||
|
dt = time.monotonic() - t0
|
||||||
|
# Tolerate a difference 100 ms: on Windows, time.monotonic() has
|
||||||
|
# a resolution of 15.6 ms or greater
|
||||||
|
self.assertGreaterEqual(dt, 1.9)
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
support.run_unittest(OSEINTRTest, SocketEINTRTest)
|
support.run_unittest(
|
||||||
|
OSEINTRTest,
|
||||||
|
SocketEINTRTest,
|
||||||
|
TimeEINTRTest)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -419,17 +419,20 @@ class WakeupSignalTests(unittest.TestCase):
|
||||||
TIMEOUT_HALF = 5
|
TIMEOUT_HALF = 5
|
||||||
|
|
||||||
signal.alarm(1)
|
signal.alarm(1)
|
||||||
before_time = time.time()
|
|
||||||
# We attempt to get a signal during the sleep,
|
# We attempt to get a signal during the sleep,
|
||||||
# before select is called
|
# before select is called
|
||||||
time.sleep(TIMEOUT_FULL)
|
try:
|
||||||
mid_time = time.time()
|
select.select([], [], [], TIMEOUT_FULL)
|
||||||
dt = mid_time - before_time
|
except InterruptedError:
|
||||||
if dt >= TIMEOUT_HALF:
|
pass
|
||||||
raise Exception("%s >= %s" % (dt, TIMEOUT_HALF))
|
else:
|
||||||
|
raise Exception("select() was not interrupted")
|
||||||
|
|
||||||
|
before_time = time.monotonic()
|
||||||
select.select([read], [], [], TIMEOUT_FULL)
|
select.select([read], [], [], TIMEOUT_FULL)
|
||||||
after_time = time.time()
|
after_time = time.monotonic()
|
||||||
dt = after_time - mid_time
|
dt = after_time - before_time
|
||||||
if dt >= TIMEOUT_HALF:
|
if dt >= TIMEOUT_HALF:
|
||||||
raise Exception("%s >= %s" % (dt, TIMEOUT_HALF))
|
raise Exception("%s >= %s" % (dt, TIMEOUT_HALF))
|
||||||
""", signal.SIGALRM)
|
""", signal.SIGALRM)
|
||||||
|
@ -443,7 +446,7 @@ class WakeupSignalTests(unittest.TestCase):
|
||||||
TIMEOUT_HALF = 5
|
TIMEOUT_HALF = 5
|
||||||
|
|
||||||
signal.alarm(1)
|
signal.alarm(1)
|
||||||
before_time = time.time()
|
before_time = time.monotonic()
|
||||||
# We attempt to get a signal during the select call
|
# We attempt to get a signal during the select call
|
||||||
try:
|
try:
|
||||||
select.select([read], [], [], TIMEOUT_FULL)
|
select.select([read], [], [], TIMEOUT_FULL)
|
||||||
|
@ -451,7 +454,7 @@ class WakeupSignalTests(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise Exception("OSError not raised")
|
raise Exception("OSError not raised")
|
||||||
after_time = time.time()
|
after_time = time.monotonic()
|
||||||
dt = after_time - before_time
|
dt = after_time - before_time
|
||||||
if dt >= TIMEOUT_HALF:
|
if dt >= TIMEOUT_HALF:
|
||||||
raise Exception("%s >= %s" % (dt, TIMEOUT_HALF))
|
raise Exception("%s >= %s" % (dt, TIMEOUT_HALF))
|
||||||
|
@ -709,8 +712,8 @@ class ItimerTest(unittest.TestCase):
|
||||||
signal.signal(signal.SIGVTALRM, self.sig_vtalrm)
|
signal.signal(signal.SIGVTALRM, self.sig_vtalrm)
|
||||||
signal.setitimer(self.itimer, 0.3, 0.2)
|
signal.setitimer(self.itimer, 0.3, 0.2)
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.monotonic()
|
||||||
while time.time() - start_time < 60.0:
|
while time.monotonic() - start_time < 60.0:
|
||||||
# use up some virtual time by doing real work
|
# use up some virtual time by doing real work
|
||||||
_ = pow(12345, 67890, 10000019)
|
_ = pow(12345, 67890, 10000019)
|
||||||
if signal.getitimer(self.itimer) == (0.0, 0.0):
|
if signal.getitimer(self.itimer) == (0.0, 0.0):
|
||||||
|
@ -732,8 +735,8 @@ class ItimerTest(unittest.TestCase):
|
||||||
signal.signal(signal.SIGPROF, self.sig_prof)
|
signal.signal(signal.SIGPROF, self.sig_prof)
|
||||||
signal.setitimer(self.itimer, 0.2, 0.2)
|
signal.setitimer(self.itimer, 0.2, 0.2)
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.monotonic()
|
||||||
while time.time() - start_time < 60.0:
|
while time.monotonic() - start_time < 60.0:
|
||||||
# do some work
|
# do some work
|
||||||
_ = pow(12345, 67890, 10000019)
|
_ = pow(12345, 67890, 10000019)
|
||||||
if signal.getitimer(self.itimer) == (0.0, 0.0):
|
if signal.getitimer(self.itimer) == (0.0, 0.0):
|
||||||
|
|
|
@ -18,6 +18,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #23646: If time.sleep() is interrupted by a signal, the sleep is now
|
||||||
|
retried with the recomputed delay, except if the signal handler raises an
|
||||||
|
exception (PEP 475).
|
||||||
|
|
||||||
- Issue #23136: _strptime now uniformly handles all days in week 0, including
|
- Issue #23136: _strptime now uniformly handles all days in week 0, including
|
||||||
Dec 30 of previous year. Based on patch by Jim Carroll.
|
Dec 30 of previous year. Based on patch by Jim Carroll.
|
||||||
|
|
||||||
|
|
|
@ -1386,74 +1386,79 @@ floattime(_Py_clock_info_t *info)
|
||||||
static int
|
static int
|
||||||
floatsleep(double secs)
|
floatsleep(double secs)
|
||||||
{
|
{
|
||||||
/* XXX Should test for MS_WINDOWS first! */
|
_PyTime_timeval deadline, monotonic;
|
||||||
#if defined(HAVE_SELECT) && !defined(__EMX__)
|
#ifndef MS_WINDOWS
|
||||||
struct timeval t;
|
struct timeval timeout;
|
||||||
double frac;
|
double frac;
|
||||||
int err;
|
int err = 0;
|
||||||
|
#else
|
||||||
frac = fmod(secs, 1.0);
|
double millisecs;
|
||||||
secs = floor(secs);
|
unsigned long ul_millis;
|
||||||
t.tv_sec = (long)secs;
|
DWORD rc;
|
||||||
t.tv_usec = (long)(frac*1000000.0);
|
HANDLE hInterruptEvent;
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t);
|
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
if (err != 0) {
|
|
||||||
#ifdef EINTR
|
|
||||||
if (errno == EINTR) {
|
|
||||||
if (PyErr_CheckSignals())
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
#endif
|
||||||
{
|
|
||||||
|
_PyTime_monotonic(&deadline);
|
||||||
|
_PyTime_ADD_SECONDS(deadline, secs);
|
||||||
|
|
||||||
|
do {
|
||||||
|
#ifndef MS_WINDOWS
|
||||||
|
frac = fmod(secs, 1.0);
|
||||||
|
secs = floor(secs);
|
||||||
|
timeout.tv_sec = (long)secs;
|
||||||
|
timeout.tv_usec = (long)(frac*1000000.0);
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
if (err == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (errno != EINTR) {
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
#else
|
||||||
#elif defined(__WATCOMC__) && !defined(__QNX__)
|
millisecs = secs * 1000.0;
|
||||||
/* XXX Can't interrupt this sleep */
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
delay((int)(secs * 1000 + 0.5)); /* delay() uses milliseconds */
|
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
#elif defined(MS_WINDOWS)
|
|
||||||
{
|
|
||||||
double millisecs = secs * 1000.0;
|
|
||||||
unsigned long ul_millis;
|
|
||||||
|
|
||||||
if (millisecs > (double)ULONG_MAX) {
|
if (millisecs > (double)ULONG_MAX) {
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
"sleep length is too large");
|
"sleep length is too large");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
/* Allow sleep(0) to maintain win32 semantics, and as decreed
|
/* Allow sleep(0) to maintain win32 semantics, and as decreed
|
||||||
* by Guido, only the main thread can be interrupted.
|
* by Guido, only the main thread can be interrupted.
|
||||||
*/
|
*/
|
||||||
ul_millis = (unsigned long)millisecs;
|
ul_millis = (unsigned long)millisecs;
|
||||||
if (ul_millis == 0 || !_PyOS_IsMainThread())
|
if (ul_millis == 0 || !_PyOS_IsMainThread()) {
|
||||||
Sleep(ul_millis);
|
Py_BEGIN_ALLOW_THREADS
|
||||||
else {
|
Sleep(0);
|
||||||
DWORD rc;
|
Py_END_ALLOW_THREADS
|
||||||
HANDLE hInterruptEvent = _PyOS_SigintEvent();
|
break;
|
||||||
ResetEvent(hInterruptEvent);
|
|
||||||
rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
|
|
||||||
if (rc == WAIT_OBJECT_0) {
|
|
||||||
Py_BLOCK_THREADS
|
|
||||||
errno = EINTR;
|
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hInterruptEvent = _PyOS_SigintEvent();
|
||||||
|
ResetEvent(hInterruptEvent);
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
}
|
|
||||||
#else
|
if (rc != WAIT_OBJECT_0)
|
||||||
/* XXX Can't interrupt this sleep */
|
break;
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
sleep((int)secs);
|
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* sleep was interrupted by SIGINT */
|
||||||
|
if (PyErr_CheckSignals())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
_PyTime_monotonic(&monotonic);
|
||||||
|
secs = _PyTime_INTERVAL(monotonic, deadline);
|
||||||
|
if (secs <= 0.0)
|
||||||
|
break;
|
||||||
|
/* retry with the recomputed delay */
|
||||||
|
} while (1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue