mirror of https://github.com/python/cpython
Issue #20505: Remove resolution and _granularity from selectors and asyncio
* Remove selectors.BaseSelector.resolution attribute * Remove asyncio.BaseEventLoop._granularity attribute
This commit is contained in:
parent
c489e83432
commit
85310a50a9
|
@ -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
|
||||
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)
|
||||
|
||||
|
|
|
@ -98,10 +98,6 @@ below:
|
|||
:class:`BaseSelector` and its concrete implementations support the
|
||||
:term:`context manager` protocol.
|
||||
|
||||
.. attribute:: resolution
|
||||
|
||||
Resolution of the selector in seconds.
|
||||
|
||||
.. method:: register(fileobj, events, data=None)
|
||||
|
||||
Register a file object for selection, monitoring it for I/O events.
|
||||
|
|
|
@ -96,7 +96,6 @@ class BaseEventLoop(events.AbstractEventLoop):
|
|||
self._default_executor = None
|
||||
self._internal_fds = 0
|
||||
self._running = False
|
||||
self._granularity = time.get_clock_info('monotonic').resolution
|
||||
|
||||
def _make_socket_transport(self, sock, protocol, waiter=None, *,
|
||||
extra=None, server=None):
|
||||
|
@ -634,21 +633,11 @@ class BaseEventLoop(events.AbstractEventLoop):
|
|||
else:
|
||||
logger.log(level, 'poll took %.3f seconds', t1-t0)
|
||||
else:
|
||||
t0 = self.time()
|
||||
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)
|
||||
|
||||
# Handle 'later' callbacks that are ready.
|
||||
now = self.time() + self._granularity
|
||||
now = self.time()
|
||||
while self._scheduled:
|
||||
handle = self._scheduled[0]
|
||||
if handle._when > now:
|
||||
|
|
|
@ -365,7 +365,6 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
|
|||
self._selector = proactor # convenient alias
|
||||
self._self_reading_future = None
|
||||
self._accept_futures = {} # socket file descriptor => Future
|
||||
self._granularity = max(proactor.resolution, self._granularity)
|
||||
proactor.set_loop(self)
|
||||
self._make_self_pipe()
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
|
|||
selector = selectors.DefaultSelector()
|
||||
logger.debug('Using selector: %s', selector.__class__.__name__)
|
||||
self._selector = selector
|
||||
self._granularity = max(selector.resolution, self._granularity)
|
||||
self._make_self_pipe()
|
||||
|
||||
def _make_socket_transport(self, sock, protocol, waiter=None, *,
|
||||
|
|
|
@ -83,11 +83,6 @@ class BaseSelector(metaclass=ABCMeta):
|
|||
performant implementation on the current platform.
|
||||
"""
|
||||
|
||||
@abstractproperty
|
||||
def resolution(self):
|
||||
"""Resolution of the selector in seconds"""
|
||||
return None
|
||||
|
||||
@abstractmethod
|
||||
def register(self, fileobj, events, data=None):
|
||||
"""Register a file object.
|
||||
|
@ -289,10 +284,6 @@ class SelectSelector(_BaseSelectorImpl):
|
|||
self._readers = set()
|
||||
self._writers = set()
|
||||
|
||||
@property
|
||||
def resolution(self):
|
||||
return 1e-6
|
||||
|
||||
def register(self, fileobj, events, data=None):
|
||||
key = super().register(fileobj, events, data)
|
||||
if events & EVENT_READ:
|
||||
|
@ -345,10 +336,6 @@ if hasattr(select, 'poll'):
|
|||
super().__init__()
|
||||
self._poll = select.poll()
|
||||
|
||||
@property
|
||||
def resolution(self):
|
||||
return 1e-3
|
||||
|
||||
def register(self, fileobj, events, data=None):
|
||||
key = super().register(fileobj, events, data)
|
||||
poll_events = 0
|
||||
|
@ -400,10 +387,6 @@ if hasattr(select, 'epoll'):
|
|||
super().__init__()
|
||||
self._epoll = select.epoll()
|
||||
|
||||
@property
|
||||
def resolution(self):
|
||||
return 1e-3
|
||||
|
||||
def fileno(self):
|
||||
return self._epoll.fileno()
|
||||
|
||||
|
@ -468,10 +451,6 @@ if hasattr(select, 'kqueue'):
|
|||
super().__init__()
|
||||
self._kqueue = select.kqueue()
|
||||
|
||||
@property
|
||||
def resolution(self):
|
||||
return 1e-9
|
||||
|
||||
def fileno(self):
|
||||
return self._kqueue.fileno()
|
||||
|
||||
|
|
|
@ -124,7 +124,8 @@ class BaseEventLoopTests(unittest.TestCase):
|
|||
self.loop.run_forever()
|
||||
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
|
||||
# are really slow
|
||||
self.assertLessEqual(dt, 0.9, dt)
|
||||
|
|
|
@ -1170,28 +1170,19 @@ class EventLoopTestsMixin:
|
|||
orig_run_once = self.loop._run_once
|
||||
self.loop._run_once_counter = 0
|
||||
self.loop._run_once = _run_once
|
||||
calls = []
|
||||
|
||||
@asyncio.coroutine
|
||||
def wait():
|
||||
loop = self.loop
|
||||
calls.append(loop._run_once_counter)
|
||||
yield from asyncio.sleep(loop._granularity * 10, loop=loop)
|
||||
calls.append(loop._run_once_counter)
|
||||
yield from asyncio.sleep(loop._granularity / 10, loop=loop)
|
||||
calls.append(loop._run_once_counter)
|
||||
yield from asyncio.sleep(1e-2, loop=loop)
|
||||
yield from asyncio.sleep(1e-4, loop=loop)
|
||||
|
||||
self.loop.run_until_complete(wait())
|
||||
calls.append(self.loop._run_once_counter)
|
||||
self.assertEqual(calls, [1, 3, 5, 6])
|
||||
|
||||
def test_granularity(self):
|
||||
granularity = self.loop._granularity
|
||||
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)
|
||||
# The ideal number of call is 6, but on some platforms, the selector
|
||||
# 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
|
||||
# platforms.
|
||||
self.assertLessEqual(self.loop._run_once_counter, 8)
|
||||
|
||||
|
||||
class SubprocessTestsMixin:
|
||||
|
|
|
@ -363,11 +363,6 @@ class BaseSelectorTestCase(unittest.TestCase):
|
|||
self.assertFalse(s.select(2))
|
||||
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:
|
||||
|
||||
|
|
Loading…
Reference in New Issue