From 3c5d4a6e8c21b0fcc75574b0b4b45575492a772b Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 16 Dec 2024 09:15:17 +0000 Subject: [PATCH] Use settings from Trio for Selector waker socketpair (#836) --- docs/versionhistory.rst | 7 +++++++ src/anyio/_core/_asyncio_selector_thread.py | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 50745252..518aef88 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -3,6 +3,13 @@ Version history This library adheres to `Semantic Versioning 2.0 `_. +**UNRELEASED** + +- Configure ``SO_RCVBUF``, ``SO_SNDBUF`` and ``TCP_NODELAY`` on the selector + thread waker socket pair. This should improve the performance of ``wait_readable()`` + and ``wait_writable()`` when using the ``ProactorEventLoop`` + (`#836 `_; PR by @graingert) + **4.7.0** - Updated ``TaskGroup`` to work with asyncio's eager task factories diff --git a/src/anyio/_core/_asyncio_selector_thread.py b/src/anyio/_core/_asyncio_selector_thread.py index d98c3040..f4d18cf0 100644 --- a/src/anyio/_core/_asyncio_selector_thread.py +++ b/src/anyio/_core/_asyncio_selector_thread.py @@ -21,6 +21,23 @@ def __init__(self) -> None: self._send, self._receive = socket.socketpair() self._send.setblocking(False) self._receive.setblocking(False) + # This somewhat reduces the amount of memory wasted queueing up data + # for wakeups. With these settings, maximum number of 1-byte sends + # before getting BlockingIOError: + # Linux 4.8: 6 + # macOS (darwin 15.5): 1 + # Windows 10: 525347 + # Windows you're weird. (And on Windows setting SNDBUF to 0 makes send + # blocking, even on non-blocking sockets, so don't do that.) + self._receive.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1) + self._send.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1) + # On Windows this is a TCP socket so this might matter. On other + # platforms this fails b/c AF_UNIX sockets aren't actually TCP. + try: + self._send.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + except OSError: + pass + self._selector.register(self._receive, EVENT_READ) self._closed = False