mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
asyncio: Pause accepting whenever accept() returns certain errors. Fixes asyncio issue #78.
This commit is contained in:
parent
2b8fc303ad
commit
3317a13253
3 changed files with 30 additions and 9 deletions
|
@ -1,4 +1,7 @@
|
|||
"""Constants."""
|
||||
|
||||
|
||||
# After the connection is lost, log warnings after this many write()s.
|
||||
LOG_THRESHOLD_FOR_CONNLOST_WRITES = 5
|
||||
|
||||
# Seconds to wait before retrying accept().
|
||||
ACCEPT_RETRY_DELAY = 1
|
||||
|
|
|
@ -5,6 +5,7 @@ also includes support for signal handling, see the unix_events sub-module.
|
|||
"""
|
||||
|
||||
import collections
|
||||
import errno
|
||||
import socket
|
||||
try:
|
||||
import ssl
|
||||
|
@ -98,15 +99,23 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
|
|||
try:
|
||||
conn, addr = sock.accept()
|
||||
conn.setblocking(False)
|
||||
except (BlockingIOError, InterruptedError):
|
||||
except (BlockingIOError, InterruptedError, ConnectionAbortedError):
|
||||
pass # False alarm.
|
||||
except Exception:
|
||||
# Bad error. Stop serving.
|
||||
self.remove_reader(sock.fileno())
|
||||
sock.close()
|
||||
except OSError as exc:
|
||||
# There's nowhere to send the error, so just log it.
|
||||
# TODO: Someone will want an error handler for this.
|
||||
logger.exception('Accept failed')
|
||||
if exc.errno in (errno.EMFILE, errno.ENFILE,
|
||||
errno.ENOBUFS, errno.ENOMEM):
|
||||
# Some platforms (e.g. Linux keep reporting the FD as
|
||||
# ready, so we remove the read handler temporarily.
|
||||
# We'll try again in a while.
|
||||
logger.exception('Accept out of system resource (%s)', exc)
|
||||
self.remove_reader(sock.fileno())
|
||||
self.call_later(constants.ACCEPT_RETRY_DELAY,
|
||||
self._start_serving,
|
||||
protocol_factory, sock, ssl, server)
|
||||
else:
|
||||
raise # The event loop will catch, log and ignore it.
|
||||
else:
|
||||
if ssl:
|
||||
self._make_ssl_transport(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Tests for base_events.py"""
|
||||
|
||||
import errno
|
||||
import logging
|
||||
import socket
|
||||
import time
|
||||
|
@ -8,6 +9,7 @@ import unittest.mock
|
|||
from test.support import find_unused_port, IPV6_ENABLED
|
||||
|
||||
from asyncio import base_events
|
||||
from asyncio import constants
|
||||
from asyncio import events
|
||||
from asyncio import futures
|
||||
from asyncio import protocols
|
||||
|
@ -585,11 +587,18 @@ class BaseEventLoopWithSelectorTests(unittest.TestCase):
|
|||
def test_accept_connection_exception(self, m_log):
|
||||
sock = unittest.mock.Mock()
|
||||
sock.fileno.return_value = 10
|
||||
sock.accept.side_effect = OSError()
|
||||
sock.accept.side_effect = OSError(errno.EMFILE, 'Too many open files')
|
||||
self.loop.remove_reader = unittest.mock.Mock()
|
||||
self.loop.call_later = unittest.mock.Mock()
|
||||
|
||||
self.loop._accept_connection(MyProto, sock)
|
||||
self.assertTrue(sock.close.called)
|
||||
self.assertTrue(m_log.exception.called)
|
||||
self.assertFalse(sock.close.called)
|
||||
self.loop.remove_reader.assert_called_with(10)
|
||||
self.loop.call_later.assert_called_with(constants.ACCEPT_RETRY_DELAY,
|
||||
# self.loop._start_serving
|
||||
unittest.mock.ANY,
|
||||
MyProto, sock, None, None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue