8000 gh-127529: Correct asyncio's `accept_connection` behaviour for handli… · python/cpython@830e106 · GitHub
[go: up one dir, main page]

Skip to content

Commit 830e106

Browse files
gh-127529: Correct asyncio's accept_connection behaviour for handling ConnectionAbortedError (#127532)
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
1 parent bb2dfad commit 830e106

File tree

3 files changed

+36
-3
lines changed

3 files changed

+36
-3
lines changed

Lib/asyncio/selector_events.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,13 @@ def _accept_connection(
180180
logger.debug("%r got a new connection from %r: %r",
181181
server, addr, conn)
182182
conn.setblocking(False)
183-
except (BlockingIOError, InterruptedError, ConnectionAbortedError):
184-
# Early exit because the socket accept buffer is empty.
185-
return None
183+
except ConnectionAbortedError:
184+
# Discard connections that were aborted before accept().
185+
continue
186+
except (BlockingIOError, InterruptedError):
187+
# Early exit because of a signal or
188+
# the socket accept buffer is empty.
189+
return
186190
except OSError as exc:
187191
# There's nowhere to send the error, so just log it.
188192
if exc.errno in (errno.EMFILE, errno.ENFILE,

Lib/test/test_asyncio/test_selector_events.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,31 @@ def test_accept_connection_multiple(self):
364364
self.loop.run_until_complete(asyncio.sleep(0))
365365
self.assertEqual(sock.accept.call_count, backlog)
366366

367+
def test_accept_connection_skip_connectionabortederror(self):
368+
sock = mock.Mock()
369+
370+
def mock_sock_accept():
371+
# mock accept(2) returning -ECONNABORTED every-other
372+
# time that it's called. This applies most to OpenBSD
373+
# whose sockets generate this errno more reproducibly than
374+
# Linux and other OS.
375+
if sock.accept.call_count % 2 == 0:
376+
raise ConnectionAbortedError
377+
return (mock.Mock(), mock.Mock())
378+
379+
sock.accept.side_effect = mock_sock_accept
380+
backlog = 100
381+
# test that _accept_connection's loop calls sock.accept
382+
# all 100 times, continuing past ConnectionAbortedError
383+
# instead of unnecessarily returning early
384+
mock_obj = mock.patch.object
385+
with mock_obj(self.loop, '_accept_connection2') as accept2_mock:
386+
self.loop._accept_connection(
387+
mock.Mock(), sock, backlog=backlog)
388+
# as in test_accept_connection_multiple avoid task pending
389+
# warnings by using asyncio.sleep(0)
390+
self.loop.run_until_complete(asyncio.sleep(0))
391+
self.assertEqual(sock.accept.call_count, backlog)
367392

368393
class SelectorTransportTests(test_utils.TestCase):
369394

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Correct behavior of
2+
:func:`!asyncio.selector_events.BaseSelectorEventLoop._accept_connection`
3+
in handling :exc:`ConnectionAbortedError` in a loop. This improves
4+
performance on OpenBSD.

0 commit comments

Comments
 (0)
0