[Python-checkins] cpython: asyncio: Pause accepting whenever accept() returns certain errors. Fixes
guido.van.rossum
python-checkins at python.org
Fri Nov 1 22:25:47 CET 2013
http://hg.python.org/cpython/rev/3ffa457c8d7c
changeset: 86825:3ffa457c8d7c
user: Guido van Rossum <guido at dropbox.com>
date: Fri Nov 01 14:12:50 2013 -0700
summary:
asyncio: Pause accepting whenever accept() returns certain errors. Fixes asyncio issue #78.
files:
Lib/asyncio/constants.py | 5 ++-
Lib/asyncio/selector_events.py | 21 +++++++---
Lib/test/test_asyncio/test_base_events.py | 13 +++++-
3 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/Lib/asyncio/constants.py b/Lib/asyncio/constants.py
--- a/Lib/asyncio/constants.py
+++ b/Lib/asyncio/constants.py
@@ -1,4 +1,7 @@
"""Constants."""
+# After the connection is lost, log warnings after this many write()s.
+LOG_THRESHOLD_FOR_CONNLOST_WRITES = 5
-LOG_THRESHOLD_FOR_CONNLOST_WRITES = 5
+# Seconds to wait before retrying accept().
+ACCEPT_RETRY_DELAY = 1
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -5,6 +5,7 @@
"""
import collections
+import errno
import socket
try:
import ssl
@@ -98,15 +99,23 @@
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(
diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py
--- a/Lib/test/test_asyncio/test_base_events.py
+++ b/Lib/test/test_asyncio/test_base_events.py
@@ -1,5 +1,6 @@
"""Tests for base_events.py"""
+import errno
import logging
import socket
import time
@@ -8,6 +9,7 @@
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 @@
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__':
--
Repository URL: http://hg.python.org/cpython
More information about the Python-checkins
mailing list