Skip to content

Commit b310173

Browse files
committed
Issue python#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).
1 parent 45ca48b commit b310173

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

Doc/library/selectors.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ below:
159159
timeout has elapsed if the current process receives a signal: in this
160160
case, an empty list will be returned.
161161

162+
.. versionchanged:: 3.5
163+
The selector is now retried with a recomputed timeout when interrupted
164+
by a signal if the signal handler did not raise an exception (see
165+
:pep:`475` for the rationale), instead of returning an empty list
166+
of events before the timeout.
167+
162168
.. method:: close()
163169

164170
Close the selector.

Lib/test/test_selectors.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,35 @@ def test_timeout(self):
357357

358358
@unittest.skipUnless(hasattr(signal, "alarm"),
359359
"signal.alarm() required for this test")
360-
def test_select_interrupt(self):
360+
def test_select_interrupt_exc(self):
361+
s = self.SELECTOR()
362+
self.addCleanup(s.close)
363+
364+
rd, wr = self.make_socketpair()
365+
366+
class InterruptSelect(Exception):
367+
pass
368+
369+
def handler(*args):
370+
raise InterruptSelect
371+
372+
orig_alrm_handler = signal.signal(signal.SIGALRM, handler)
373+
self.addCleanup(signal.signal, signal.SIGALRM, orig_alrm_handler)
374+
self.addCleanup(signal.alarm, 0)
375+
376+
signal.alarm(1)
377+
378+
s.register(rd, selectors.EVENT_READ)
379+
t = time()
380+
# select() is interrupted by a signal which raises an exception
381+
with self.assertRaises(InterruptSelect):
382+
s.select(30)
383+
# select() was interrupted before the timeout of 30 seconds
384+
self.assertLess(time() - t, 5.0)
385+
386+
@unittest.skipUnless(hasattr(signal, "alarm"),
387+
"signal.alarm() required for this test")
388+
def test_select_interrupt_noraise(self):
361389
s = self.SELECTOR()
362390
self.addCleanup(s.close)
363391

@@ -371,8 +399,11 @@ def test_select_interrupt(self):
371399

372400
s.register(rd, selectors.EVENT_READ)
373401
t = time()
374-
self.assertFalse(s.select(2))
375-
self.assertLess(time() - t, 2.5)
402+
# select() is interrupted by a signal, but the signal handler doesn't
403+
# raise an exception, so select() should by retries with a recomputed
404+
# timeout
405+
self.assertFalse(s.select(1.5))
406+
self.assertGreaterEqual(time() - t, 1.0)
376407

377408

378409
class ScalableSelectorMixIn:

0 commit comments

Comments
 (0)