try:
if f is not None:
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)
except exceptions.CancelledError:
# _close_self_pipe() has been called, stop waiting for data
if self._self_reading_future is not None:
ov = self._self_reading_future._ov
self._self_reading_future.cancel()
- # self_reading_future was just cancelled so it will never be signalled
- # Unregister it otherwise IocpProactor.close will wait for it forever
+ # self_reading_future was just cancelled so if it hasn't been
+ # 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:
self._proactor._unregister(ov)
self._self_reading_future = None
def test_loop_self_reading_fut(self):
fut = mock.Mock()
+ self.loop._self_reading_future = fut
self.loop._loop_self_reading(fut)
self.assertTrue(fut.result.called)
self.proactor.recv.assert_called_with(self.ssock, 4096)
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):
--- /dev/null
+Restarting a ``ProactorEventLoop`` on Windows no longer logs spurious
+``ConnectionResetErrors``.