diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 961dbfb4b96303e..d8d32d545c52a1c 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -206,6 +206,7 @@ def _accept_connection( protocol_factory, sock, sslcontext, server, backlog, ssl_handshake_timeout, ssl_shutdown_timeout, context) + return else: raise # The event loop will catch, log and ignore it. else: diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index 4bb5d4fb816a9a6..0461d7eeca116c3 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -1,6 +1,7 @@ """Tests for selector_events.py""" import collections +import errno import selectors import socket import sys @@ -402,6 +403,24 @@ def mock_sock_accept(): self.loop.run_until_complete(asyncio.sleep(0)) self.assertEqual(sock.accept.call_count, backlog + 1) + def test_accept_connection_reschedules_once_on_resource_error(self): + # When accept() fails with a resource error (EMFILE), _accept_connection + # re-runs the error branch backlog+1 times, logging and rescheduling + # _start_serving once per iteration. With early return after first + # exception we avoid this behaviour + sock = mock.Mock() + sock.accept.side_effect = OSError(errno.EMFILE, 'too many open files') + + self.loop.call_exception_handler = mock.Mock() + self.loop._remove_reader = mock.Mock() + self.loop.call_later = mock.Mock() + + self.loop._accept_connection(mock.Mock(), sock, backlog=100) + + self.assertEqual(sock.accept.call_count, 1) + self.assertEqual(self.loop.call_exception_handler.call_count, 1) + self.assertEqual(self.loop.call_later.call_count, 1) + class SelectorTransportTests(test_utils.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2026-06-18-00-39-58.gh-issue-151615.PBsCE1.rst b/Misc/NEWS.d/next/Library/2026-06-18-00-39-58.gh-issue-151615.PBsCE1.rst new file mode 100644 index 000000000000000..1d08dbacef1f79b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-18-00-39-58.gh-issue-151615.PBsCE1.rst @@ -0,0 +1,2 @@ +Fix :mod:`asyncio` servers repeatedly logging and rescheduling on a single +event loop iteration when ``accept()`` fails with a resource errors.