Commit 85310a50 authored by Victor Stinner's avatar Victor Stinner

Issue #20505: Remove resolution and _granularity from selectors and asyncio

* Remove selectors.BaseSelector.resolution attribute
* Remove asyncio.BaseEventLoop._granularity attribute
parent c489e834
...@@ -118,19 +118,6 @@ Which clock is used depends on the (platform-specific) event loop ...@@ -118,19 +118,6 @@ Which clock is used depends on the (platform-specific) event loop
implementation; ideally it is a monotonic clock. This will generally be implementation; ideally it is a monotonic clock. This will generally be
a different clock than :func:`time.time`. a different clock than :func:`time.time`.
The granularity of the event loop depends on the resolution of the
:meth:`~BaseEventLoop.time` method and the resolution of the selector. It is
usually between 1 ms and 16 ms. For example, a granularity of 1 ms means that
in the best case, the difference between the expected delay and the real
elapsed time is between -1 ms and +1 ms: a call scheduled in 1 nanosecond may
be called in 1 ms, and a call scheduled in 100 ms may be called in 99 ms.
The granularity is the best difference in theory. In practice, it depends on
the system load and the the time taken by tasks executed by the event loop.
For example, if a task blocks the event loop for 1 second, all tasks scheduled
in this second will be delayed. The :ref:`Handle correctly blocking functions
<asyncio-handle-blocking>` section explains how to avoid such issue.
.. method:: BaseEventLoop.call_later(delay, callback, *args) .. method:: BaseEventLoop.call_later(delay, callback, *args)
......
...@@ -98,10 +98,6 @@ below: ...@@ -98,10 +98,6 @@ below:
:class:`BaseSelector` and its concrete implementations support the :class:`BaseSelector` and its concrete implementations support the
:term:`context manager` protocol. :term:`context manager` protocol.
.. attribute:: resolution
Resolution of the selector in seconds.
.. method:: register(fileobj, events, data=None) .. method:: register(fileobj, events, data=None)
Register a file object for selection, monitoring it for I/O events. Register a file object for selection, monitoring it for I/O events.
......
...@@ -96,7 +96,6 @@ class BaseEventLoop(events.AbstractEventLoop): ...@@ -96,7 +96,6 @@ class BaseEventLoop(events.AbstractEventLoop):
self._default_executor = None self._default_executor = None
self._internal_fds = 0 self._internal_fds = 0
self._running = False self._running = False
self._granularity = time.get_clock_info('monotonic').resolution
def _make_socket_transport(self, sock, protocol, waiter=None, *, def _make_socket_transport(self, sock, protocol, waiter=None, *,
extra=None, server=None): extra=None, server=None):
...@@ -634,21 +633,11 @@ class BaseEventLoop(events.AbstractEventLoop): ...@@ -634,21 +633,11 @@ class BaseEventLoop(events.AbstractEventLoop):
else: else:
logger.log(level, 'poll took %.3f seconds', t1-t0) logger.log(level, 'poll took %.3f seconds', t1-t0)
else: else:
t0 = self.time()
event_list = self._selector.select(timeout) event_list = self._selector.select(timeout)
dt = self.time() - t0
if timeout and not event_list and dt < timeout:
print("%s.select(%.3f ms) took %.3f ms (granularity=%.3f ms, resolution=%.3f ms)"
% (self._selector.__class__.__name__,
timeout * 1e3,
dt * 1e3,
self._granularity * 1e3,
self._selector.resolution * 1e3),
file=sys.__stderr__)
self._process_events(event_list) self._process_events(event_list)
# Handle 'later' callbacks that are ready. # Handle 'later' callbacks that are ready.
now = self.time() + self._granularity now = self.time()
while self._scheduled: while self._scheduled:
handle = self._scheduled[0] handle = self._scheduled[0]
if handle._when > now: if handle._when > now:
......
...@@ -365,7 +365,6 @@ class BaseProactorEventLoop(base_events.BaseEventLoop): ...@@ -365,7 +365,6 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
self._selector = proactor # convenient alias self._selector = proactor # convenient alias
self._self_reading_future = None self._self_reading_future = None
self._accept_futures = {} # socket file descriptor => Future self._accept_futures = {} # socket file descriptor => Future
self._granularity = max(proactor.resolution, self._granularity)
proactor.set_loop(self) proactor.set_loop(self)
self._make_self_pipe() self._make_self_pipe()
......
...@@ -36,7 +36,6 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop): ...@@ -36,7 +36,6 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
selector = selectors.DefaultSelector() selector = selectors.DefaultSelector()
logger.debug('Using selector: %s', selector.__class__.__name__) logger.debug('Using selector: %s', selector.__class__.__name__)
self._selector = selector self._selector = selector
self._granularity = max(selector.resolution, self._granularity)
self._make_self_pipe() self._make_self_pipe()
def _make_socket_transport(self, sock, protocol, waiter=None, *, def _make_socket_transport(self, sock, protocol, waiter=None, *,
......
...@@ -83,11 +83,6 @@ class BaseSelector(metaclass=ABCMeta): ...@@ -83,11 +83,6 @@ class BaseSelector(metaclass=ABCMeta):
performant implementation on the current platform. performant implementation on the current platform.
""" """
@abstractproperty
def resolution(self):
"""Resolution of the selector in seconds"""
return None
@abstractmethod @abstractmethod
def register(self, fileobj, events, data=None): def register(self, fileobj, events, data=None):
"""Register a file object. """Register a file object.
...@@ -289,10 +284,6 @@ class SelectSelector(_BaseSelectorImpl): ...@@ -289,10 +284,6 @@ class SelectSelector(_BaseSelectorImpl):
self._readers = set() self._readers = set()
self._writers = set() self._writers = set()
@property
def resolution(self):
return 1e-6
def register(self, fileobj, events, data=None): def register(self, fileobj, events, data=None):
key = super().register(fileobj, events, data) key = super().register(fileobj, events, data)
if events & EVENT_READ: if events & EVENT_READ:
...@@ -345,10 +336,6 @@ if hasattr(select, 'poll'): ...@@ -345,10 +336,6 @@ if hasattr(select, 'poll'):
super().__init__() super().__init__()
self._poll = select.poll() self._poll = select.poll()
@property
def resolution(self):
return 1e-3
def register(self, fileobj, events, data=None): def register(self, fileobj, events, data=None):
key = super().register(fileobj, events, data) key = super().register(fileobj, events, data)
poll_events = 0 poll_events = 0
...@@ -400,10 +387,6 @@ if hasattr(select, 'epoll'): ...@@ -400,10 +387,6 @@ if hasattr(select, 'epoll'):
super().__init__() super().__init__()
self._epoll = select.epoll() self._epoll = select.epoll()
@property
def resolution(self):
return 1e-3
def fileno(self): def fileno(self):
return self._epoll.fileno() return self._epoll.fileno()
...@@ -468,10 +451,6 @@ if hasattr(select, 'kqueue'): ...@@ -468,10 +451,6 @@ if hasattr(select, 'kqueue'):
super().__init__() super().__init__()
self._kqueue = select.kqueue() self._kqueue = select.kqueue()
@property
def resolution(self):
return 1e-9
def fileno(self): def fileno(self):
return self._kqueue.fileno() return self._kqueue.fileno()
......
...@@ -124,7 +124,8 @@ class BaseEventLoopTests(unittest.TestCase): ...@@ -124,7 +124,8 @@ class BaseEventLoopTests(unittest.TestCase):
self.loop.run_forever() self.loop.run_forever()
dt = self.loop.time() - t0 dt = self.loop.time() - t0
self.assertGreaterEqual(dt, delay - self.loop._granularity, dt) # 50 ms: maximum granularity of the event loop
self.assertGreaterEqual(dt, delay - 0.050, dt)
# tolerate a difference of +800 ms because some Python buildbots # tolerate a difference of +800 ms because some Python buildbots
# are really slow # are really slow
self.assertLessEqual(dt, 0.9, dt) self.assertLessEqual(dt, 0.9, dt)
......
...@@ -1170,28 +1170,19 @@ class EventLoopTestsMixin: ...@@ -1170,28 +1170,19 @@ class EventLoopTestsMixin:
orig_run_once = self.loop._run_once orig_run_once = self.loop._run_once
self.loop._run_once_counter = 0 self.loop._run_once_counter = 0
self.loop._run_once = _run_once self.loop._run_once = _run_once
calls = []
@asyncio.coroutine @asyncio.coroutine
def wait(): def wait():
loop = self.loop loop = self.loop
calls.append(loop._run_once_counter) yield from asyncio.sleep(1e-2, loop=loop)
yield from asyncio.sleep(loop._granularity * 10, loop=loop) yield from asyncio.sleep(1e-4, loop=loop)
calls.append(loop._run_once_counter)
yield from asyncio.sleep(loop._granularity / 10, loop=loop)
calls.append(loop._run_once_counter)
self.loop.run_until_complete(wait()) self.loop.run_until_complete(wait())
calls.append(self.loop._run_once_counter) # The ideal number of call is 6, but on some platforms, the selector
self.assertEqual(calls, [1, 3, 5, 6]) # may sleep at little bit less than timeout depending on the resolution
# of the clock used by the kernel. Tolerate 2 useless calls on these
def test_granularity(self): # platforms.
granularity = self.loop._granularity self.assertLessEqual(self.loop._run_once_counter, 8)
self.assertGreater(granularity, 0.0)
# Worst expected granularity: 1 ms on Linux (limited by poll/epoll
# resolution), 15.6 ms on Windows (limited by time.monotonic
# resolution)
self.assertLess(granularity, 0.050)
class SubprocessTestsMixin: class SubprocessTestsMixin:
......
...@@ -363,11 +363,6 @@ class BaseSelectorTestCase(unittest.TestCase): ...@@ -363,11 +363,6 @@ class BaseSelectorTestCase(unittest.TestCase):
self.assertFalse(s.select(2)) self.assertFalse(s.select(2))
self.assertLess(time() - t, 2.5) self.assertLess(time() - t, 2.5)
def test_resolution(self):
s = self.SELECTOR()
self.assertIsInstance(s.resolution, (int, float))
self.assertGreater(s.resolution, 0.0)
class ScalableSelectorMixIn: class ScalableSelectorMixIn:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment