[Python-checkins] cpython (3.4): asyncio: Only call _check_resolved_address() in debug mode

victor.stinner python-checkins at python.org
Wed Feb 4 14:54:22 CET 2015


https://hg.python.org/cpython/rev/8b52ca4d11b8
changeset:   94501:8b52ca4d11b8
branch:      3.4
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Wed Feb 04 14:51:23 2015 +0100
summary:
  asyncio: Only call _check_resolved_address() in debug mode

* _check_resolved_address() is implemented with getaddrinfo() which is slow
* If available, use socket.inet_pton() instead of socket.getaddrinfo(), because
  it is much faster

Microbenchmark (timeit) on Fedora 21 (Python 3.4, Linux 3.17, glibc 2.20) to
validate the IPV4 address "127.0.0.1" or the IPv6 address "::1":

* getaddrinfo() 10.4 usec per loop
* inet_pton(): 0.285 usec per loop

On glibc older than 2.14, getaddrinfo() always requests the list of all local
IP addresses to the kernel (using a NETLINK socket). getaddrinfo() has other
known issues, it's better to avoid it when it is possible.

files:
  Lib/asyncio/base_events.py           |  48 ++++++++++-----
  Lib/asyncio/proactor_events.py       |   3 +-
  Lib/asyncio/selector_events.py       |   3 +-
  Lib/test/test_asyncio/test_events.py |   4 +
  4 files changed, 40 insertions(+), 18 deletions(-)


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
@@ -75,7 +75,11 @@
 def _check_resolved_address(sock, address):
     # Ensure that the address is already resolved to avoid the trap of hanging
     # the entire event loop when the address requires doing a DNS lookup.
+    #
+    # getaddrinfo() is slow (around 10 us per call): this function should only
+    # be called in debug mode
     family = sock.family
+
     if family == socket.AF_INET:
         host, port = address
     elif family == socket.AF_INET6:
@@ -83,22 +87,34 @@
     else:
         return
 
-    type_mask = 0
-    if hasattr(socket, 'SOCK_NONBLOCK'):
-        type_mask |= socket.SOCK_NONBLOCK
-    if hasattr(socket, 'SOCK_CLOEXEC'):
-        type_mask |= socket.SOCK_CLOEXEC
-    # Use getaddrinfo(flags=AI_NUMERICHOST) to ensure that the address is
-    # already resolved.
-    try:
-        socket.getaddrinfo(host, port,
-                           family=family,
-                           type=(sock.type & ~type_mask),
-                           proto=sock.proto,
-                           flags=socket.AI_NUMERICHOST)
-    except socket.gaierror as err:
-        raise ValueError("address must be resolved (IP address), got %r: %s"
-                         % (address, err))
+    # On Windows, socket.inet_pton() is only available since Python 3.4
+    if hasattr(socket, 'inet_pton'):
+        # getaddrinfo() is slow and has known issue: prefer inet_pton()
+        # if available
+        try:
+            socket.inet_pton(family, host)
+        except OSError as exc:
+            raise ValueError("address must be resolved (IP address), "
+                             "got host %r: %s"
+                             % (host, exc))
+    else:
+        # Use getaddrinfo(flags=AI_NUMERICHOST) to ensure that the address is
+        # already resolved.
+        type_mask = 0
+        if hasattr(socket, 'SOCK_NONBLOCK'):
+            type_mask |= socket.SOCK_NONBLOCK
+        if hasattr(socket, 'SOCK_CLOEXEC'):
+            type_mask |= socket.SOCK_CLOEXEC
+        try:
+            socket.getaddrinfo(host, port,
+                               family=family,
+                               type=(sock.type & ~type_mask),
+                               proto=sock.proto,
+                               flags=socket.AI_NUMERICHOST)
+        except socket.gaierror as err:
+            raise ValueError("address must be resolved (IP address), "
+                             "got host %r: %s"
+                             % (host, err))
 
 def _raise_stop_error(*args):
     raise _StopError
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
@@ -437,7 +437,8 @@
 
     def sock_connect(self, sock, address):
         try:
-            base_events._check_resolved_address(sock, address)
+            if self._debug:
+                base_events._check_resolved_address(sock, address)
         except ValueError as err:
             fut = futures.Future(loop=self)
             fut.set_exception(err)
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
@@ -397,7 +397,8 @@
             raise ValueError("the socket must be non-blocking")
         fut = futures.Future(loop=self)
         try:
-            base_events._check_resolved_address(sock, address)
+            if self._debug:
+                base_events._check_resolved_address(sock, address)
         except ValueError as err:
             fut.set_exception(err)
         else:
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
@@ -1437,6 +1437,10 @@
              'selector': self.loop._selector.__class__.__name__})
 
     def test_sock_connect_address(self):
+        # In debug mode, sock_connect() must ensure that the address is already
+        # resolved (call _check_resolved_address())
+        self.loop.set_debug(True)
+
         addresses = [(socket.AF_INET, ('www.python.org', 80))]
         if support.IPV6_ENABLED:
             addresses.extend((

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


More information about the Python-checkins mailing list