Bug report
Bug description:
In BaseSelectorEventLoop._accept_connection when sock.accept() fails when system is out of file descriptors (EMFILE/ENFILE/ENOBUFS/ENOMEM) code removes the reader and schedules a retry, but does not make return, which generates a lot of unnecessary calls.
# Lib/asyncio/selector_events.py:168
def _accept_connection(
self, protocol_factory, sock,
sslcontext=None, server=None, backlog=100,
ssl_handshake_timeout=constants.SSL_HANDSHAKE_TIMEOUT,
ssl_shutdown_timeout=constants.SSL_SHUTDOWN_TIMEOUT, context=None):
for _ in range(backlog + 1):
try:
conn, addr = sock.accept()
if self._debug:
logger.debug("%r got a new connection from %r: %r",
server, addr, conn)
conn.setblocking(False)
except ConnectionAbortedError:
continue
except (BlockingIOError, InterruptedError):
return
except OSError as exc:
if exc.errno in (errno.EMFILE, errno.ENFILE,
errno.ENOBUFS, errno.ENOMEM):
self.call_exception_handler({
'message': 'socket.accept() out of system resource',
'exception': exc,
'socket': trsock.TransportSocket(sock),
})
self._remove_reader(sock.fileno())
self.call_later(constants.ACCEPT_RETRY_DELAY,
self._start_serving,
protocol_factory, sock, sslcontext, server,
backlog, ssl_handshake_timeout,
ssl_shutdown_timeout, context)
else:
raise
The branch except (BlockingIOError, InterruptedError) already do early return, so this branch should too
Reproducer:
import errno, asyncio
from unittest import mock
loop = asyncio.new_event_loop()
sock = mock.Mock()
sock.accept.side_effect = OSError(errno.EMFILE, "too many open files")
loop.call_exception_handler = mock.Mock()
loop._remove_reader = mock.Mock()
loop.call_later = mock.Mock()
loop._accept_connection(mock.Mock(), sock, backlog=100)
print("accept calls :", sock.accept.call_count)
print("retry schedules:", loop.call_later.call_count)
Expected:
accept calls : 1
retry schedules: 1
Actual:
accept calls : 101
retry schedules: 101
Proposed fix is to add the return statement :
except OSError as exc:
if exc.errno in (errno.EMFILE, errno.ENFILE,
errno.ENOBUFS, errno.ENOMEM):
self.call_exception_handler({
'message': 'socket.accept() out of system resource',
'exception': exc,
'socket': trsock.TransportSocket(sock),
})
self._remove_reader(sock.fileno())
self.call_later(constants.ACCEPT_RETRY_DELAY,
self._start_serving,
protocol_factory, sock, sslcontext, server,
backlog, ssl_handshake_timeout,
ssl_shutdown_timeout, context)
return
else:
raise
CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS
Linked PRs
Bug report
Bug description:
In
BaseSelectorEventLoop._accept_connectionwhensock.accept()fails when system is out of file descriptors (EMFILE/ENFILE/ENOBUFS/ENOMEM) code removes the reader and schedules a retry, but does not make return, which generates a lot of unnecessary calls.The branch
except (BlockingIOError, InterruptedError)already do early return, so this branch should tooReproducer:
Expected:
Actual:
Proposed fix is to add the return statement :
CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS
Linked PRs