From a6f24bd26a8d39292c07d5e72cde645277ec3362 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 3 Dec 2018 21:08:13 +0200 Subject: [PATCH 1/3] 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 | 6 +++- 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(+), 42 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 b3b07554ec19fa..65e0529e1d90c8 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -165,6 +165,17 @@ def _run_until_complete_cb(fut): futures._get_loop(fut).stop() +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 + + class _SendfileFallbackProtocol(protocols.Protocol): def __init__(self, transp): if not isinstance(transp, transports._FlowControlMixin): diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 66bfb0ab11ee50..49ac2b4a7a4845 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -13,7 +13,6 @@ from . import base_events from . import constants -from . import events from . import futures from . import protocols from . import sslproto @@ -444,6 +443,11 @@ class _ProactorSocketTransport(_ProactorReadPipeTransport, _sendfile_compatible = constants._SendfileMode.TRY_NATIVE + 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 diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 116c08d6ff7fdd..d2861d34332a48 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -39,17 +39,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 - 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 - - class BaseSelectorEventLoop(base_events.BaseEventLoop): """Selector event loop. @@ -733,7 +722,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 59d321e1c691c1..c4107909cd6e1d 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 @@ -14,7 +13,6 @@ import asyncio from asyncio import base_events from asyncio import constants -from asyncio import events from test.test_asyncio import utils as test_utils from test import support from test.support.script_helper import assert_python_ok @@ -272,7 +270,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): @@ -2056,5 +2054,31 @@ def test_negative_offset(self): self.run_loop(self.loop.sock_sendfile(sock, self.file, -1)) +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 68b6ee9abbf11a..e94af28bade37b 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -15,7 +15,6 @@ from asyncio.selector_events import _SelectorTransport from asyncio.selector_events import _SelectorSocketTransport from asyncio.selector_events import _SelectorDatagramTransport -from asyncio.selector_events import _set_nodelay from test.test_asyncio import utils as test_utils @@ -1746,30 +1745,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 00000000000000..91f86e604ea865 --- /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 1cf6cacbcbc47ba5e06f2c38566aa8f33c60ae35 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 3 Dec 2018 21:23:42 +0200 Subject: [PATCH 2/3] Add missing imports --- Lib/test/test_asyncio/test_base_events.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index c4107909cd6e1d..559ed3ec527924 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 @@ -13,6 +14,7 @@ import asyncio from asyncio import base_events from asyncio import constants +from asyncio import events from test.test_asyncio import utils as test_utils from test import support from test.support.script_helper import assert_python_ok From ed1cd6f776c9dbf40ea1fe786f09ccda09fd3f62 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 3 Dec 2018 21:54:15 +0200 Subject: [PATCH 3/3] Update proactor_events.py --- Lib/asyncio/proactor_events.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 49ac2b4a7a4845..e350e8bc0c2485 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -13,6 +13,7 @@ from . import base_events from . import constants +from . import events from . import futures from . import protocols from . import sslproto