mirror of https://github.com/python/cpython
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
|
||||
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])
|
||||
|
||||
|
|
|
@ -252,8 +252,26 @@ class SocketEINTRTest(EINTRBaseTest):
|
|||
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():
|
||||
support.run_unittest(OSEINTRTest, SocketEINTRTest)
|
||||
support.run_unittest(
|
||||
OSEINTRTest,
|
||||
SocketEINTRTest,
|
||||
TimeEINTRTest)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -419,17 +419,20 @@ class WakeupSignalTests(unittest.TestCase):
|
|||
TIMEOUT_HALF = 5
|
||||
|
||||
signal.alarm(1)
|
||||
before_time = time.time()
|
||||
|
||||
# We attempt to get a signal during the sleep,
|
||||
# before select is called
|
||||
time.sleep(TIMEOUT_FULL)
|
||||
mid_time = time.time()
|
||||
dt = mid_time - before_time
|
||||
if dt >= TIMEOUT_HALF:
|
||||
raise Exception("%s >= %s" % (dt, TIMEOUT_HALF))
|
||||
try:
|
||||
select.select([], [], [], TIMEOUT_FULL)
|
||||
except InterruptedError:
|
||||
pass
|
||||
else:
|
||||
raise Exception("select() was not interrupted")
|
||||
|
||||
before_time = time.monotonic()
|
||||
select.select([read], [], [], TIMEOUT_FULL)
|
||||
after_time = time.time()
|
||||
dt = after_time - mid_time
|
||||
after_time = time.monotonic()
|
||||
dt = after_time - before_time
|
||||
if dt >= TIMEOUT_HALF:
|
||||
raise Exception("%s >= %s" % (dt, TIMEOUT_HALF))
|
||||
""", signal.SIGALRM)
|
||||
|
@ -443,7 +446,7 @@ class WakeupSignalTests(unittest.TestCase):
|
|||
TIMEOUT_HALF = 5
|
||||
|
||||
signal.alarm(1)
|
||||
before_time = time.time()
|
||||
before_time = time.monotonic()
|
||||
# We attempt to get a signal during the select call
|
||||
try:
|
||||
select.select([read], [], [], TIMEOUT_FULL)
|
||||
|
@ -451,7 +454,7 @@ class WakeupSignalTests(unittest.TestCase):
|
|||
pass
|
||||
else:
|
||||
raise Exception("OSError not raised")
|
||||
after_time = time.time()
|
||||
after_time = time.monotonic()
|
||||
dt = after_time - before_time
|
||||
if 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.setitimer(self.itimer, 0.3, 0.2)
|
||||
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 60.0:
|
||||
start_time = time.monotonic()
|
||||
while time.monotonic() - start_time < 60.0:
|
||||
# use up some virtual time by doing real work
|
||||
_ = pow(12345, 67890, 10000019)
|
||||
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.setitimer(self.itimer, 0.2, 0.2)
|
||||
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 60.0:
|
||||
start_time = time.monotonic()
|
||||
while time.monotonic() - start_time < 60.0:
|
||||
# do some work
|
||||
_ = pow(12345, 67890, 10000019)
|
||||
if signal.getitimer(self.itimer) == (0.0, 0.0):
|
||||
|
|
|
@ -18,6 +18,10 @@ Core and Builtins
|
|||
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
|
||||
Dec 30 of previous year. Based on patch by Jim Carroll.
|
||||
|
||||
|
|
|
@ -1386,74 +1386,79 @@ floattime(_Py_clock_info_t *info)
|
|||
static int
|
||||
floatsleep(double secs)
|
||||
{
|
||||
/* XXX Should test for MS_WINDOWS first! */
|
||||
#if defined(HAVE_SELECT) && !defined(__EMX__)
|
||||
struct timeval t;
|
||||
_PyTime_timeval deadline, monotonic;
|
||||
#ifndef MS_WINDOWS
|
||||
struct timeval timeout;
|
||||
double frac;
|
||||
int err;
|
||||
|
||||
frac = fmod(secs, 1.0);
|
||||
secs = floor(secs);
|
||||
t.tv_sec = (long)secs;
|
||||
t.tv_usec = (long)(frac*1000000.0);
|
||||
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
|
||||
int err = 0;
|
||||
#else
|
||||
double millisecs;
|
||||
unsigned long ul_millis;
|
||||
DWORD rc;
|
||||
HANDLE hInterruptEvent;
|
||||
#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);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#elif defined(__WATCOMC__) && !defined(__QNX__)
|
||||
/* 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;
|
||||
|
||||
#else
|
||||
millisecs = secs * 1000.0;
|
||||
if (millisecs > (double)ULONG_MAX) {
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
"sleep length is too large");
|
||||
return -1;
|
||||
}
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
|
||||
/* Allow sleep(0) to maintain win32 semantics, and as decreed
|
||||
* by Guido, only the main thread can be interrupted.
|
||||
*/
|
||||
ul_millis = (unsigned long)millisecs;
|
||||
if (ul_millis == 0 || !_PyOS_IsMainThread())
|
||||
Sleep(ul_millis);
|
||||
else {
|
||||
DWORD rc;
|
||||
HANDLE hInterruptEvent = _PyOS_SigintEvent();
|
||||
ResetEvent(hInterruptEvent);
|
||||
rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
|
||||
if (rc == WAIT_OBJECT_0) {
|
||||
Py_BLOCK_THREADS
|
||||
errno = EINTR;
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
return -1;
|
||||
}
|
||||
if (ul_millis == 0 || !_PyOS_IsMainThread()) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
Sleep(0);
|
||||
Py_END_ALLOW_THREADS
|
||||
break;
|
||||
}
|
||||
|
||||
hInterruptEvent = _PyOS_SigintEvent();
|
||||
ResetEvent(hInterruptEvent);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
|
||||
Py_END_ALLOW_THREADS
|
||||
}
|
||||
#else
|
||||
/* XXX Can't interrupt this sleep */
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
sleep((int)secs);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
break;
|
||||
#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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue