From 3bc0ebab17bf5a2c29d2214743c82034f82e6573 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 3 Dec 2018 21:08:13 +0200 Subject: [PATCH] bpo-35380: Enable TCP_NODELAY for proactor event loop (#10867) --- 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 f5ab6e7b2d2..60a189bdfb7 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -168,6 +168,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 ad23918802f..69d96a81035 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 exceptions from . import protocols @@ -445,6 +444,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 ad093001dd9..112c4b15b8d 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. @@ -742,7 +731,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 6d544d1eda8..53854758a27 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -2,7 +2,6 @@ import concurrent.futures import errno -import logging import math import os import socket @@ -15,7 +14,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 @@ -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): @@ -2075,5 +2073,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 b99e8e69607..d0d171a9853 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -14,7 +14,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 @@ -1344,30 +1343,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 00000000000..91f86e604ea --- /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.