From 6b9d7586171526a598b9bfcf71373cde4a2e3e37 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 3 Dec 2018 21:08:13 +0200 Subject: [PATCH 1/3] [3.6] bpo-35380: Enable TCP_NODELAY for proactor event loop (GH-10867). (cherry picked from commit 3bc0ebab17bf5a2c29d2214743c82034f82e6573) Co-authored-by: Andrew Svetlov --- Lib/asyncio/base_events.py | 11 +++++++ Lib/asyncio/proactor_events.py | 5 ++++ Lib/asyncio/selector_events.py | 13 +------- Lib/test/test_asyncio/test_base_events.py | 30 +++++++++++++++++-- Lib/test/test_asyncio/test_selector_events.py | 26 ---------------- .../2018-12-03-14-41-11.bpo-35380.SdRF9l.rst | 1 + 6 files changed, 45 insertions(+), 41 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index accd669250cbefb..652c8ac06123559 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -182,6 +182,17 @@ def _ensure_resolved(address, *, family=0, type=socket.SOCK_STREAM, proto=0, proto=proto, flags=flags) +if hasattr(socket, 'TCP_NODELAY'): + def _set_nodelay(sock): + if (sock.family in {socket.AF_INET, socket.AF_INET6} and + sock.type == socket.SOCK_STREAM and + sock.proto == socket.IPPROTO_TCP): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) +else: + def _set_nodelay(sock): + pass + + def _run_until_complete_cb(fut): exc = fut._exception if (isinstance(exc, BaseException) diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 6f621ef0cc4f188..079b7a24db42abf 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -350,6 +350,11 @@ class _ProactorSocketTransport(_ProactorReadPipeTransport, transports.Transport): """Transport for connected sockets.""" + def __init__(self, loop, sock, protocol, waiter=None, + extra=None, server=None): + super().__init__(loop, sock, protocol, waiter, extra, server) + base_events._set_nodelay(sock) + def _set_extra(self, sock): self._extra['socket'] = sock try: diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index bc7c740cc2af382..af21d5ee8914f59 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -40,17 +40,6 @@ def _test_selector_event(selector, fd, event): return bool(key.events & event) -if hasattr(socket, 'TCP_NODELAY'): - def _set_nodelay(sock): - if (sock.family in {socket.AF_INET, socket.AF_INET6} and - base_events._is_stream_socket(sock.type) and - sock.proto == socket.IPPROTO_TCP): - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) -else: - def _set_nodelay(sock): - pass - - class BaseSelectorEventLoop(base_events.BaseEventLoop): """Selector event loop. @@ -691,7 +680,7 @@ def __init__(self, loop, sock, protocol, waiter=None, # Disable the Nagle algorithm -- small writes will be # sent without waiting for the TCP ACK. This generally # decreases the latency (in some cases significantly.) - _set_nodelay(self._sock) + base_events._set_nodelay(self._sock) self._loop.call_soon(self._protocol.connection_made, self) # only start reading when connection_made() has been called diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 09f71f2f39002dc..24a50b1beb2da93 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -1,7 +1,6 @@ """Tests for base_events.py""" import errno -import logging import math import os import socket @@ -27,7 +26,6 @@ except ImportError: from asyncio.test_support import assert_python_ok - MOCK_ANY = mock.ANY PY34 = sys.version_info >= (3, 4) @@ -288,7 +286,7 @@ def cb(): loop.set_debug(debug) if debug: msg = ("Non-thread-safe operation invoked on an event loop other " - "than the current one") + "than the current one") with self.assertRaisesRegex(RuntimeError, msg): loop.call_soon(cb) with self.assertRaisesRegex(RuntimeError, msg): @@ -1839,5 +1837,31 @@ def runner(loop): +class TestSelectorUtils(test_utils.TestCase): + def check_set_nodelay(self, sock): + opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) + self.assertFalse(opt) + + base_events._set_nodelay(sock) + + opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) + self.assertTrue(opt) + + @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'), + 'need socket.TCP_NODELAY') + def test_set_nodelay(self): + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, + proto=socket.IPPROTO_TCP) + with sock: + self.check_set_nodelay(sock) + + sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, + proto=socket.IPPROTO_TCP) + with sock: + sock.setblocking(False) + self.check_set_nodelay(sock) + + + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index 533d2898e7fc582..6716e8916cd5059 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -17,7 +17,6 @@ from asyncio.selector_events import _SelectorSslTransport from asyncio.selector_events import _SelectorSocketTransport from asyncio.selector_events import _SelectorDatagramTransport -from asyncio.selector_events import _set_nodelay MOCK_ANY = mock.ANY @@ -1858,30 +1857,5 @@ def test_fatal_error_connected(self, m_exc): exc_info=(ConnectionRefusedError, MOCK_ANY, MOCK_ANY)) -class TestSelectorUtils(test_utils.TestCase): - def check_set_nodelay(self, sock): - opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) - self.assertFalse(opt) - - _set_nodelay(sock) - - opt = sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) - self.assertTrue(opt) - - @unittest.skipUnless(hasattr(socket, 'TCP_NODELAY'), - 'need socket.TCP_NODELAY') - def test_set_nodelay(self): - sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, - proto=socket.IPPROTO_TCP) - with sock: - self.check_set_nodelay(sock) - - sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, - proto=socket.IPPROTO_TCP) - with sock: - sock.setblocking(False) - self.check_set_nodelay(sock) - - if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst b/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst new file mode 100644 index 000000000000000..91f86e604ea8650 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-12-03-14-41-11.bpo-35380.SdRF9l.rst @@ -0,0 +1 @@ +Enable TCP_NODELAY on Windows for proactor asyncio event loop. From 9c6f746d3353b6d742988675e349022c778f34ea Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 3 Dec 2018 23:13:34 +0200 Subject: [PATCH 2/3] Update test_base_events.py --- Lib/test/test_asyncio/test_base_events.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 24a50b1beb2da93..3014c620d9149b8 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -1,6 +1,7 @@ """Tests for base_events.py""" import errno +import logging import math import os import socket From 4e680f6e4481b49759c1543f76c2929556588547 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 4 Dec 2018 20:28:54 +0200 Subject: [PATCH 3/3] Fix test --- Lib/asyncio/base_events.py | 2 +- Lib/test/test_asyncio/test_base_events.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 652c8ac06123559..48dd1fc54ae42c6 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -185,7 +185,7 @@ def _ensure_resolved(address, *, family=0, type=socket.SOCK_STREAM, proto=0, if hasattr(socket, 'TCP_NODELAY'): def _set_nodelay(sock): if (sock.family in {socket.AF_INET, socket.AF_INET6} and - sock.type == socket.SOCK_STREAM and + _is_stream_socket(sock.type) and sock.proto == socket.IPPROTO_TCP): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) else: diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 3014c620d9149b8..052a559c3cbb549 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -1863,6 +1863,5 @@ def test_set_nodelay(self): self.check_set_nodelay(sock) - if __name__ == '__main__': unittest.main()