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).
This commit is contained in:
Victor Stinner 2015-03-31 12:08:09 +02:00
parent 45ca48b03d
commit b310173319
2 changed files with 40 additions and 3 deletions

View File

@ -159,6 +159,12 @@ below:
timeout has elapsed if the current process receives a signal: in this timeout has elapsed if the current process receives a signal: in this
case, an empty list will be returned. 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() .. method:: close()
Close the selector. Close the selector.

View File

@ -357,7 +357,35 @@ class BaseSelectorTestCase(unittest.TestCase):
@unittest.skipUnless(hasattr(signal, "alarm"), @unittest.skipUnless(hasattr(signal, "alarm"),
"signal.alarm() required for this test") "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() s = self.SELECTOR()
self.addCleanup(s.close) self.addCleanup(s.close)
@ -371,8 +399,11 @@ class BaseSelectorTestCase(unittest.TestCase):
s.register(rd, selectors.EVENT_READ) s.register(rd, selectors.EVENT_READ)
t = time() t = time()
self.assertFalse(s.select(2)) # select() is interrupted by a signal, but the signal handler doesn't
self.assertLess(time() - t, 2.5) # 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: class ScalableSelectorMixIn: