diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2015-03-31 12:08:09 +0200 |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2015-03-31 12:08:09 +0200 |
commit | eae8624b93258135fa4e4329fc79add27df87de4 (patch) | |
tree | 14d663ca0278017969cd26f4e9a4b1d208c65a7a | |
parent | 1f2890e7f13c4a7b231eaf09b841bb0ec56fd71a (diff) | |
download | cpython-eae8624b93258135fa4e4329fc79add27df87de4.tar.gz |
Issue #23485: Enhance and update selectors doc and test_selectors
Selector.select() is now retried with the recomputed timeout when interrupted
by a signal.
Write an unit test with a signal handler raising an exception, and a unit with
a signal handler which does not raise an exception (it does nothing).
-rw-r--r-- | Doc/library/selectors.rst | 6 | ||||
-rw-r--r-- | Lib/test/test_selectors.py | 37 |
2 files changed, 40 insertions, 3 deletions
diff --git a/Doc/library/selectors.rst b/Doc/library/selectors.rst index 8bd9e1ce2e..f6ef24b6ae 100644 --- a/Doc/library/selectors.rst +++ b/Doc/library/selectors.rst @@ -159,6 +159,12 @@ below: timeout has elapsed if the current process receives a signal: in this case, an empty list will be returned. + .. versionchanged:: 3.5 + The selector is now retried with a recomputed timeout when interrupted + by a signal if the signal handler did not raise an exception (see + :pep:`475` for the rationale), instead of returning an empty list + of events before the timeout. + .. method:: close() Close the selector. diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py index 9521481901..454c17be99 100644 --- a/Lib/test/test_selectors.py +++ b/Lib/test/test_selectors.py @@ -357,7 +357,35 @@ class BaseSelectorTestCase(unittest.TestCase): @unittest.skipUnless(hasattr(signal, "alarm"), "signal.alarm() required for this test") - def test_select_interrupt(self): + def test_select_interrupt_exc(self): + s = self.SELECTOR() + self.addCleanup(s.close) + + rd, wr = self.make_socketpair() + + class InterruptSelect(Exception): + pass + + def handler(*args): + raise InterruptSelect + + orig_alrm_handler = signal.signal(signal.SIGALRM, handler) + self.addCleanup(signal.signal, signal.SIGALRM, orig_alrm_handler) + self.addCleanup(signal.alarm, 0) + + signal.alarm(1) + + s.register(rd, selectors.EVENT_READ) + t = time() + # select() is interrupted by a signal which raises an exception + with self.assertRaises(InterruptSelect): + s.select(30) + # select() was interrupted before the timeout of 30 seconds + self.assertLess(time() - t, 5.0) + + @unittest.skipUnless(hasattr(signal, "alarm"), + "signal.alarm() required for this test") + def test_select_interrupt_noraise(self): s = self.SELECTOR() self.addCleanup(s.close) @@ -371,8 +399,11 @@ class BaseSelectorTestCase(unittest.TestCase): s.register(rd, selectors.EVENT_READ) t = time() - self.assertFalse(s.select(2)) - self.assertLess(time() - t, 2.5) + # select() is interrupted by a signal, but the signal handler doesn't + # raise an exception, so select() should by retries with a recomputed + # timeout + self.assertFalse(s.select(1.5)) + self.assertGreaterEqual(time() - t, 1.0) class ScalableSelectorMixIn: |