[Python-checkins] cpython: Issue #20505: Remove resolution and _granularity from selectors and asyncio

victor.stinner python-checkins at python.org
Fri Feb 7 23:37:01 CET 2014


http://hg.python.org/cpython/rev/d853955491a2
changeset:   89027:d853955491a2
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Fri Feb 07 23:34:58 2014 +0100
summary:
  Issue #20505: Remove resolution and _granularity from selectors and asyncio

* Remove selectors.BaseSelector.resolution attribute
* Remove asyncio.BaseEventLoop._granularity attribute

files:
  Doc/library/asyncio-eventloop.rst         |  13 ------
  Doc/library/selectors.rst                 |   4 -
  Lib/asyncio/base_events.py                |  13 +-----
  Lib/asyncio/proactor_events.py            |   1 -
  Lib/asyncio/selector_events.py            |   1 -
  Lib/selectors.py                          |  21 ----------
  Lib/test/test_asyncio/test_base_events.py |   3 +-
  Lib/test/test_asyncio/test_events.py      |  23 +++-------
  Lib/test/test_selectors.py                |   5 --
  9 files changed, 10 insertions(+), 74 deletions(-)


diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst
--- a/Doc/library/asyncio-eventloop.rst
+++ b/Doc/library/asyncio-eventloop.rst
@@ -118,19 +118,6 @@
 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)
 
diff --git a/Doc/library/selectors.rst b/Doc/library/selectors.rst
--- a/Doc/library/selectors.rst
+++ b/Doc/library/selectors.rst
@@ -98,10 +98,6 @@
    :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.
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -96,7 +96,6 @@
         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 @@
             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:
diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py
--- a/Lib/asyncio/proactor_events.py
+++ b/Lib/asyncio/proactor_events.py
@@ -365,7 +365,6 @@
         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()
 
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -36,7 +36,6 @@
             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, *,
diff --git a/Lib/selectors.py b/Lib/selectors.py
--- a/Lib/selectors.py
+++ b/Lib/selectors.py
@@ -83,11 +83,6 @@
     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 @@
         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 @@
             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 @@
             super().__init__()
             self._epoll = select.epoll()
 
-        @property
-        def resolution(self):
-            return 1e-3
-
         def fileno(self):
             return self._epoll.fileno()
 
@@ -468,10 +451,6 @@
             super().__init__()
             self._kqueue = select.kqueue()
 
-        @property
-        def resolution(self):
-            return 1e-9
-
         def fileno(self):
             return self._kqueue.fileno()
 
diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py
--- a/Lib/test/test_asyncio/test_base_events.py
+++ b/Lib/test/test_asyncio/test_base_events.py
@@ -124,7 +124,8 @@
         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)
diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py
--- a/Lib/test/test_asyncio/test_events.py
+++ b/Lib/test/test_asyncio/test_events.py
@@ -1170,28 +1170,19 @@
         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:
diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py
--- a/Lib/test/test_selectors.py
+++ b/Lib/test/test_selectors.py
@@ -363,11 +363,6 @@
         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:
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list