bpo-39010: Fix errors logged on proactor loop restart (#22017)
Stopping and restarting a proactor event loop on windows can lead to spurious errors logged (ConnectionResetError while reading from the self pipe). This fixes the issue by ensuring that we don't attempt to start multiple copies of the self-pipe reading loop.
This commit is contained in:
parent
c3a651ad25
commit
ea5a6363c3
|
@ -768,6 +768,14 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
|
||||||
try:
|
try:
|
||||||
if f is not None:
|
if f is not None:
|
||||||
f.result() # may raise
|
f.result() # may raise
|
||||||
|
if self._self_reading_future is not f:
|
||||||
|
# When we scheduled this Future, we assigned it to
|
||||||
|
# _self_reading_future. If it's not there now, something has
|
||||||
|
# tried to cancel the loop while this callback was still in the
|
||||||
|
# queue (see windows_events.ProactorEventLoop.run_forever). In
|
||||||
|
# that case stop here instead of continuing to schedule a new
|
||||||
|
# iteration.
|
||||||
|
return
|
||||||
f = self._proactor.recv(self._ssock, 4096)
|
f = self._proactor.recv(self._ssock, 4096)
|
||||||
except exceptions.CancelledError:
|
except exceptions.CancelledError:
|
||||||
# _close_self_pipe() has been called, stop waiting for data
|
# _close_self_pipe() has been called, stop waiting for data
|
||||||
|
|
|
@ -318,8 +318,12 @@ class ProactorEventLoop(proactor_events.BaseProactorEventLoop):
|
||||||
if self._self_reading_future is not None:
|
if self._self_reading_future is not None:
|
||||||
ov = self._self_reading_future._ov
|
ov = self._self_reading_future._ov
|
||||||
self._self_reading_future.cancel()
|
self._self_reading_future.cancel()
|
||||||
# self_reading_future was just cancelled so it will never be signalled
|
# self_reading_future was just cancelled so if it hasn't been
|
||||||
# Unregister it otherwise IocpProactor.close will wait for it forever
|
# finished yet, it never will be (it's possible that it has
|
||||||
|
# already finished and its callback is waiting in the queue,
|
||||||
|
# where it could still happen if the event loop is restarted).
|
||||||
|
# Unregister it otherwise IocpProactor.close will wait for it
|
||||||
|
# forever
|
||||||
if ov is not None:
|
if ov is not None:
|
||||||
self._proactor._unregister(ov)
|
self._proactor._unregister(ov)
|
||||||
self._self_reading_future = None
|
self._self_reading_future = None
|
||||||
|
|
|
@ -753,6 +753,7 @@ class BaseProactorEventLoopTests(test_utils.TestCase):
|
||||||
|
|
||||||
def test_loop_self_reading_fut(self):
|
def test_loop_self_reading_fut(self):
|
||||||
fut = mock.Mock()
|
fut = mock.Mock()
|
||||||
|
self.loop._self_reading_future = fut
|
||||||
self.loop._loop_self_reading(fut)
|
self.loop._loop_self_reading(fut)
|
||||||
self.assertTrue(fut.result.called)
|
self.assertTrue(fut.result.called)
|
||||||
self.proactor.recv.assert_called_with(self.ssock, 4096)
|
self.proactor.recv.assert_called_with(self.ssock, 4096)
|
||||||
|
|
|
@ -211,6 +211,26 @@ class ProactorTests(test_utils.TestCase):
|
||||||
fut.cancel()
|
fut.cancel()
|
||||||
fut.cancel()
|
fut.cancel()
|
||||||
|
|
||||||
|
def test_read_self_pipe_restart(self):
|
||||||
|
# Regression test for https://bugs.python.org/issue39010
|
||||||
|
# Previously, restarting a proactor event loop in certain states
|
||||||
|
# would lead to spurious ConnectionResetErrors being logged.
|
||||||
|
self.loop.call_exception_handler = mock.Mock()
|
||||||
|
# Start an operation in another thread so that the self-pipe is used.
|
||||||
|
# This is theoretically timing-dependent (the task in the executor
|
||||||
|
# must complete before our start/stop cycles), but in practice it
|
||||||
|
# seems to work every time.
|
||||||
|
f = self.loop.run_in_executor(None, lambda: None)
|
||||||
|
self.loop.stop()
|
||||||
|
self.loop.run_forever()
|
||||||
|
self.loop.stop()
|
||||||
|
self.loop.run_forever()
|
||||||
|
# If we don't wait for f to complete here, we may get another
|
||||||
|
# warning logged about a thread that didn't shut down cleanly.
|
||||||
|
self.loop.run_until_complete(f)
|
||||||
|
self.loop.close()
|
||||||
|
self.assertFalse(self.loop.call_exception_handler.called)
|
||||||
|
|
||||||
|
|
||||||
class WinPolicyTests(test_utils.TestCase):
|
class WinPolicyTests(test_utils.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Restarting a ``ProactorEventLoop`` on Windows no longer logs spurious
|
||||||
|
``ConnectionResetErrors``.
|
Loading…
Reference in New Issue