changeset: 94958:1e8eff72c19e tag: tip user: Victor Stinner date: Thu Mar 12 16:19:01 2015 +0100 files: Doc/library/time.rst Lib/test/eintrdata/eintr_tester.py Modules/timemodule.c description: test diff -r eb48295e1f8b -r 1e8eff72c19e Doc/library/time.rst --- a/Doc/library/time.rst Thu Mar 12 16:04:41 2015 +0100 +++ b/Doc/library/time.rst Thu Mar 12 16:19:01 2015 +0100 @@ -350,6 +350,10 @@ The module defines the following functio 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 (see :pep:`475` for the rationale). + .. function:: strftime(format[, t]) diff -r eb48295e1f8b -r 1e8eff72c19e Lib/test/eintrdata/eintr_tester.py --- a/Lib/test/eintrdata/eintr_tester.py Thu Mar 12 16:04:41 2015 +0100 +++ b/Lib/test/eintrdata/eintr_tester.py Thu Mar 12 16:19:01 2015 +0100 @@ -252,8 +252,23 @@ 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(2) + signal.alarm(0) + dt = time.monotonic() - t0 + self.assertGreaterEqual(dt, 1.9) + + def test_main(): - support.run_unittest(OSEINTRTest, SocketEINTRTest) + support.run_unittest( + OSEINTRTest, + SocketEINTRTest, + TimeEINTRTest) if __name__ == "__main__": diff -r eb48295e1f8b -r 1e8eff72c19e Modules/timemodule.c --- a/Modules/timemodule.c Thu Mar 12 16:04:41 2015 +0100 +++ b/Modules/timemodule.c Thu Mar 12 16:19:01 2015 +0100 @@ -1386,74 +1386,94 @@ 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; - double frac; - int err; + _PyTime_timeval deadline, monotonic; +#if defined(MS_WINDOWS) + double millisecs; + unsigned long ul_millis; + DWORD rc; + HANDLE hInterruptEvent; - 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 -#endif - { - 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; + _PyTime_monotonic(&deadline); + _PyTime_ADD_SECONDS(deadline, secs); + do { + 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); + if (ul_millis == 0 || !_PyOS_IsMainThread()) { + Py_BEGIN_ALLOW_THREADS + Sleep(0); + Py_END_ALLOW_THREADS + break; + } else { - DWORD rc; - HANDLE hInterruptEvent = _PyOS_SigintEvent(); + hInterruptEvent = _PyOS_SigintEvent(); ResetEvent(hInterruptEvent); + + Py_BEGIN_ALLOW_THREADS rc = WaitForSingleObjectEx(hInterruptEvent, ul_millis, FALSE); - if (rc == WAIT_OBJECT_0) { - Py_BLOCK_THREADS - errno = EINTR; - PyErr_SetFromErrno(PyExc_OSError); + 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); +#else + struct timeval timeout; + double frac; + int err = 0; + + _PyTime_monotonic(&deadline); + _PyTime_ADD_SECONDS(deadline, secs); + + while (1) { + 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 && errno == EINTR)) + break; + + /* select() was interrupted by a signal */ + if (PyErr_CheckSignals()) + return -1; + + _PyTime_monotonic(&monotonic); + secs = _PyTime_INTERVAL(monotonic, deadline); + if (secs <= 0.0) { + err = 0; + errno = 0; + break; + } } -#else - /* XXX Can't interrupt this sleep */ - Py_BEGIN_ALLOW_THREADS - sleep((int)secs); - Py_END_ALLOW_THREADS + + if (err != 0) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } #endif - return 0; }