8000 gh-127529: Correct asyncio.selector_events.BaseSelectorEventLoop._accept_connection's behaviour for handling ConnectionAbortedError by jb2170 · Pull Request #127532 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-127529: Correct asyncio.selector_events.BaseSelectorEventLoop._accept_connection's behaviour for handling ConnectionAbortedError #127532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 3, 2025
10 changes: 7 additions & 3 deletions Lib/asyncio/selector_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,13 @@ def _accept_connection(
logger.debug("%r got a new connection from %r: %r",
server, addr, conn)
conn.setblocking(False)
except (BlockingIOError, InterruptedError, ConnectionAbortedError):
# Early exit because the socket accept buffer is empty.
return None
except ConnectionAbortedError:
# Discard connections that were aborted before accept().
continue
except (BlockingIOError, InterruptedError):
# Early exit because of a signal or
# the socket accept buffer is empty.
return
except OSError as exc:
# There's nowhere to send the error, so just log it.
if exc.errno in (errno.EMFILE, errno.ENFILE,
Expand Down
25 changes: 25 additions & 0 deletions Lib/test/test_asyncio/test_selector_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,31 @@ def test_accept_connection_multiple(self):
self.loop.run_until_complete(asyncio.sleep(0))
self.assertEqual(sock.accept.call_count, backlog)

def test_accept_connection_skip_connectionabortederror(self):
sock = mock.Mock()

def mock_sock_accept():
# mock accept(2) returning -ECONNABORTED every-other
# time that it's called. This applies most to OpenBSD
# whose sockets generate this errno more reproducibly than
# Linux and other OS.
if sock.accept.call_count % 2 == 0:
raise ConnectionAbortedError
return (mock.Mock(), mock.Mock())

sock.accept.side_effect = mock_sock_accept
backlog = 100
# test that _accept_connection's loop calls sock.accept
# all 100 times, continuing past ConnectionAbortedError
# instead of unnecessarily returning early
mock_obj = mock.patch.object
with mock_obj(self.loop, '_accept_connection2') as accept2_mock:
self.loop._accept_connection(
mock.Mock(), sock, backlog=backlog)
# as in test_accept_connection_multiple avoid task pending
# warnings by using asyncio.sleep(0)
self.loop.run_until_complete(asyncio.sleep(0))
self.assertEqual(sock.accept.call_count, backlog)

class SelectorTransportTests(test_utils.TestCase):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Correct behavior of
:func:`!asyncio.selector_events.BaseSelectorEventLoop._accept_connection`
in handling :exc:`ConnectionAbortedError` in a loop. This improves
performance on OpenBSD
Loading
0