diff options
-rw-r--r-- | Lib/selectors.py | 8 | ||||
-rw-r--r-- | Lib/test/test_epoll.py | 23 | ||||
-rw-r--r-- | Lib/test/test_selectors.py | 18 | ||||
-rw-r--r-- | Misc/NEWS | 5 |
4 files changed, 40 insertions, 14 deletions
diff --git a/Lib/selectors.py b/Lib/selectors.py index a44d5e9..63392f5 100644 --- a/Lib/selectors.py +++ b/Lib/selectors.py @@ -8,6 +8,7 @@ This module allows high-level and efficient I/O multiplexing, built upon the from abc import ABCMeta, abstractmethod from collections import namedtuple, Mapping import functools +import math import select import sys @@ -351,7 +352,12 @@ if hasattr(select, 'poll'): return key def select(self, timeout=None): - timeout = None if timeout is None else max(int(1000 * timeout), 0) + if timeout is None: + timeout = None + elif timeout < 0: + timeout = 0 + else: + timeout = int(math.ceil(timeout * 1000.0)) ready = [] try: fd_event_list = self._poll.poll(timeout) diff --git a/Lib/test/test_epoll.py b/Lib/test/test_epoll.py index 22e9896..094b0f0 100644 --- a/Lib/test/test_epoll.py +++ b/Lib/test/test_epoll.py @@ -47,18 +47,6 @@ class TestEPoll(unittest.TestCase): self.serverSocket.listen(1) self.connections = [self.serverSocket] - def test_timeout_rounding(self): - # epoll_wait() has a resolution of 1 millisecond, check if the timeout - # is correctly rounded to the upper bound - epoll = select.epoll() - self.addCleanup(epoll.close) - for timeout in (1e-2, 1e-3, 1e-4): - t0 = time.perf_counter() - epoll.poll(timeout) - dt = time.perf_counter() - t0 - self.assertGreaterEqual(dt, timeout) - - def tearDown(self): for skt in self.connections: skt.close() @@ -266,6 +254,17 @@ class TestEPoll(unittest.TestCase): self.addCleanup(epoll.close) self.assertEqual(os.get_inheritable(epoll.fileno()), False) + def test_timeout_rounding(self): + # epoll_wait() has a resolution of 1 millisecond, check if the timeout + # is correctly rounded to the upper bound + epoll = select.epoll() + self.addCleanup(epoll.close) + for timeout in (1e-2, 1e-3, 1e-4): + t0 = time.perf_counter() + epoll.poll(timeout) + dt = time.perf_counter() - t0 + self.assertGreaterEqual(dt, timeout) + def test_main(): support.run_unittest(TestEPoll) diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py index 34edd76..d306aef 100644 --- a/Lib/test/test_selectors.py +++ b/Lib/test/test_selectors.py @@ -5,7 +5,7 @@ import selectors import signal import socket from test import support -from time import sleep +from time import sleep, perf_counter import unittest import unittest.mock try: @@ -363,6 +363,22 @@ class BaseSelectorTestCase(unittest.TestCase): self.assertFalse(s.select(2)) self.assertLess(time() - t, 2.5) + def test_timeout_rounding(self): + # Issue #20311: Timeout must be rounded away from zero to wait *at + # least* timeout seconds. For example, epoll_wait() has a resolution of + # 1 ms (10^-3), epoll.select(0.0001) must wait 1 ms, not 0 ms. + s = self.SELECTOR() + self.addCleanup(s.close) + + rd, wr = self.make_socketpair() + s.register(rd, selectors.EVENT_READ) + + for timeout in (1e-2, 1e-3, 1e-4): + t0 = perf_counter() + s.select(timeout) + dt = perf_counter() - t0 + self.assertGreaterEqual(dt, timeout) + class ScalableSelectorMixIn: @@ -25,6 +25,11 @@ Core and Builtins Library ------- +- Issue #20311: selector.PollSelector.select() now rounds the timeout away from + zero, instead of rounding towards zero. For example, a timeout of one + microsecond is now rounded to one millisecond, instead of being rounded to + zero. + - Issue #20311: select.epoll.poll() now rounds the timeout away from zero, instead of rounding towards zero. For example, a timeout of one microsecond is now rounded to one millisecond, instead of being rounded to zero. |