This commit is contained in:
Victor Stinner 2015-03-12 16:19:01 +01:00
parent f7cc3fccad
commit 945c82eea3
4 changed files with 102 additions and 60 deletions

View file

@ -350,6 +350,10 @@ 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 (see :pep:`475` for the rationale).
.. function:: strftime(format[, t]) .. function:: strftime(format[, t])

View file

@ -252,8 +252,23 @@ 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(2)
signal.alarm(0)
dt = time.monotonic() - t0
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__":

View file

@ -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.time()
select.select([read], [], [], TIMEOUT_FULL) select.select([read], [], [], TIMEOUT_FULL)
after_time = time.time() after_time = time.time()
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)

View file

@ -1386,74 +1386,94 @@ 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;
frac = fmod(secs, 1.0); _PyTime_monotonic(&deadline);
secs = floor(secs); _PyTime_ADD_SECONDS(deadline, secs);
t.tv_sec = (long)secs;
t.tv_usec = (long)(frac*1000000.0); while (1) {
Py_BEGIN_ALLOW_THREADS frac = fmod(secs, 1.0);
err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t); secs = floor(secs);
Py_END_ALLOW_THREADS timeout.tv_sec = (long)secs;
if (err != 0) { timeout.tv_usec = (long)(frac*1000000.0);
#ifdef EINTR
if (errno == EINTR) { Py_BEGIN_ALLOW_THREADS
if (PyErr_CheckSignals()) err = select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
return -1; Py_END_ALLOW_THREADS
}
else if (!(err != 0 && errno == EINTR))
#endif break;
{
PyErr_SetFromErrno(PyExc_OSError); /* select() was interrupted by a signal */
if (PyErr_CheckSignals())
return -1; return -1;
_PyTime_monotonic(&monotonic);
secs = _PyTime_INTERVAL(monotonic, deadline);
if (secs <= 0.0) {
err = 0;
errno = 0;
break;
} }
} }
#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;
if (err != 0) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
#else
double millisecs;
unsigned long ul_millis;
DWORD rc;
HANDLE hInterruptEvent;
_PyTime_monotonic(&deadline);
_PyTime_ADD_SECONDS(deadline, secs);
do {
millisecs = secs * 1000.0;
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;
}
} }
Py_END_ALLOW_THREADS else {
} hInterruptEvent = _PyOS_SigintEvent();
#else ResetEvent(hInterruptEvent);
/* XXX Can't interrupt this sleep */
Py_BEGIN_ALLOW_THREADS
sleep((int)secs);
Py_END_ALLOW_THREADS
#endif
Py_BEGIN_ALLOW_THREADS
rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE);
Py_END_ALLOW_THREADS
if (rc != WAIT_OBJECT_0)
break;
/* WaitForSingleObjectEx() was interrupted by SIGINT */
if (PyErr_CheckSignals())
return -1;
_PyTime_monotonic(&monotonic);
secs = _PyTime_INTERVAL(monotonic, deadline);
if (secs <= 0.0)
break;
}
} while (1);
#endif
return 0; return 0;
} }