mirror of
https://github.com/python/cpython.git
synced 2025-10-17 20:28:43 +00:00
Issue #20311: asyncio: Add a granularity attribute to BaseEventLoop: maximum
between the resolution of the BaseEventLoop.time() method and the resolution of the selector. The granuarility is used in the scheduler to round time and deadline.
This commit is contained in:
parent
635fca9704
commit
f67255ab94
6 changed files with 44 additions and 1 deletions
|
@ -144,6 +144,12 @@ a different clock than :func:`time.time`.
|
||||||
Return the current time, as a :class:`float` value, according to the
|
Return the current time, as a :class:`float` value, according to the
|
||||||
event loop's internal clock.
|
event loop's internal clock.
|
||||||
|
|
||||||
|
.. attribute:: BaseEventLoop.granularity
|
||||||
|
|
||||||
|
Granularity of the time: maximum between the resolution of the
|
||||||
|
:meth:`BaseEventLoop.time` method and the resolution of the selector (see
|
||||||
|
:attr:`selectors.BaseSelector.resolution`).
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
The :func:`asyncio.sleep` function.
|
The :func:`asyncio.sleep` function.
|
||||||
|
|
|
@ -18,6 +18,7 @@ import collections
|
||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
import heapq
|
import heapq
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
@ -96,6 +97,7 @@ 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):
|
||||||
|
@ -603,6 +605,8 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||||
elif self._scheduled:
|
elif self._scheduled:
|
||||||
# Compute the desired timeout.
|
# Compute the desired timeout.
|
||||||
when = self._scheduled[0]._when
|
when = self._scheduled[0]._when
|
||||||
|
# round deadline aways from zero
|
||||||
|
when = math.ceil(when / self.granularity) * self.granularity
|
||||||
deadline = max(0, when - self.time())
|
deadline = max(0, when - self.time())
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
timeout = deadline
|
timeout = deadline
|
||||||
|
@ -629,6 +633,8 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||||
|
|
||||||
# Handle 'later' callbacks that are ready.
|
# Handle 'later' callbacks that are ready.
|
||||||
now = self.time()
|
now = self.time()
|
||||||
|
# round current time aways from zero
|
||||||
|
now = math.ceil(now / self.granularity) * self.granularity
|
||||||
while self._scheduled:
|
while self._scheduled:
|
||||||
handle = self._scheduled[0]
|
handle = self._scheduled[0]
|
||||||
if handle._when > now:
|
if handle._when > now:
|
||||||
|
|
|
@ -34,6 +34,7 @@ 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, *,
|
||||||
|
|
|
@ -1116,6 +1116,29 @@ class EventLoopTestsMixin:
|
||||||
r.close()
|
r.close()
|
||||||
w.close()
|
w.close()
|
||||||
|
|
||||||
|
def test_timeout_rounding(self):
|
||||||
|
def _run_once():
|
||||||
|
self.loop._run_once_counter += 1
|
||||||
|
orig_run_once()
|
||||||
|
|
||||||
|
orig_run_once = self.loop._run_once
|
||||||
|
self.loop._run_once_counter = 0
|
||||||
|
self.loop._run_once = _run_once
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
@tasks.coroutine
|
||||||
|
def wait():
|
||||||
|
loop = self.loop
|
||||||
|
calls.append(loop._run_once_counter)
|
||||||
|
yield from tasks.sleep(loop.granularity * 10, loop=loop)
|
||||||
|
calls.append(loop._run_once_counter)
|
||||||
|
yield from tasks.sleep(loop.granularity / 10, loop=loop)
|
||||||
|
calls.append(loop._run_once_counter)
|
||||||
|
|
||||||
|
self.loop.run_until_complete(wait())
|
||||||
|
calls.append(self.loop._run_once_counter)
|
||||||
|
self.assertEqual(calls, [1, 3, 5, 6])
|
||||||
|
|
||||||
|
|
||||||
class SubprocessTestsMixin:
|
class SubprocessTestsMixin:
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,9 @@ def list_to_buffer(l=()):
|
||||||
class BaseSelectorEventLoopTests(unittest.TestCase):
|
class BaseSelectorEventLoopTests(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.loop = TestBaseSelectorEventLoop(unittest.mock.Mock())
|
selector = unittest.mock.Mock()
|
||||||
|
selector.resolution = 1e-3
|
||||||
|
self.loop = TestBaseSelectorEventLoop(selector)
|
||||||
|
|
||||||
def test_make_socket_transport(self):
|
def test_make_socket_transport(self):
|
||||||
m = unittest.mock.Mock()
|
m = unittest.mock.Mock()
|
||||||
|
|
|
@ -36,6 +36,11 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #20311: asyncio: Add a granularity attribute to BaseEventLoop: maximum
|
||||||
|
between the resolution of the BaseEventLoop.time() method and the resolution
|
||||||
|
of the selector. The granuarility is used in the scheduler to round time and
|
||||||
|
deadline.
|
||||||
|
|
||||||
- Issue #20311: selectors: Add a resolution attribute to BaseSelector.
|
- Issue #20311: selectors: Add a resolution attribute to BaseSelector.
|
||||||
|
|
||||||
- Issue #20189: unittest.mock now no longer assumes that any object for
|
- Issue #20189: unittest.mock now no longer assumes that any object for
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue