changeset: 88594:e042ea77a152 user: Victor Stinner date: Tue Jan 21 01:48:28 2014 +0100 files: Lib/selectors.py Lib/test/test_epoll.py Lib/test/test_selectors.py Misc/NEWS description: 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. Move also a test in test_epoll which was moved by my previous merge. diff -r 02f9db3e684e -r e042ea77a152 Lib/selectors.py --- a/Lib/selectors.py Tue Jan 21 01:42:11 2014 +0100 +++ b/Lib/selectors.py Tue Jan 21 01:48:28 2014 +0100 @@ -8,6 +8,7 @@ from abc import ABCMeta, abstractmethod from collections import namedtuple, Mapping import functools +import math import select import sys @@ -351,7 +352,12 @@ 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 -r 02f9db3e684e -r e042ea77a152 Lib/test/test_epoll.py --- a/Lib/test/test_epoll.py Tue Jan 21 01:42:11 2014 +0100 +++ b/Lib/test/test_epoll.py Tue Jan 21 01:48:28 2014 +0100 @@ -47,18 +47,6 @@ 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 @@ 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 -r 02f9db3e684e -r e042ea77a152 Lib/test/test_selectors.py --- a/Lib/test/test_selectors.py Tue Jan 21 01:42:11 2014 +0100 +++ b/Lib/test/test_selectors.py Tue Jan 21 01:48:28 2014 +0100 @@ -5,7 +5,7 @@ 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 @@ 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: diff -r 02f9db3e684e -r e042ea77a152 Misc/NEWS --- a/Misc/NEWS Tue Jan 21 01:42:11 2014 +0100 +++ b/Misc/NEWS Tue Jan 21 01:48:28 2014 +0100 @@ -25,6 +25,11 @@ 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.