summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-03-31 12:08:09 +0200
committerVictor Stinner <victor.stinner@gmail.com>2015-03-31 12:08:09 +0200
commiteae8624b93258135fa4e4329fc79add27df87de4 (patch)
tree14d663ca0278017969cd26f4e9a4b1d208c65a7a
parent1f2890e7f13c4a7b231eaf09b841bb0ec56fd71a (diff)
downloadcpython-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.rst6
-rw-r--r--Lib/test/test_selectors.py37
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: