summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/asyncio/proactor_events.py8
-rw-r--r--Lib/asyncio/windows_events.py8
-rw-r--r--Lib/test/test_asyncio/test_proactor_events.py1
-rw-r--r--Lib/test/test_asyncio/test_windows_events.py20
4 files changed, 35 insertions, 2 deletions
diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py
index 830d8ed..54e4deb 100644
--- a/Lib/asyncio/proactor_events.py
+++ b/Lib/asyncio/proactor_events.py
@@ -766,6 +766,14 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
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
diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py
index 12e87ab..a5ad6fc 100644
--- a/Lib/asyncio/windows_events.py
+++ b/Lib/asyncio/windows_events.py
@@ -318,8 +318,12 @@ class ProactorEventLoop(proactor_events.BaseProactorEventLoop):
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
diff --git a/Lib/test/test_asyncio/test_proactor_events.py b/Lib/test/test_asyncio/test_proactor_events.py
index 007039a..e4809c3 100644
--- a/Lib/test/test_asyncio/test_proactor_events.py
+++ b/Lib/test/test_asyncio/test_proactor_events.py
@@ -736,6 +736,7 @@ class BaseProactorEventLoopTests(test_utils.TestCase):
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)
diff --git a/Lib/test/test_asyncio/test_windows_events.py b/Lib/test/test_asyncio/test_windows_events.py
index 6b00570..33388a8 100644
--- a/Lib/test/test_asyncio/test_windows_events.py
+++ b/Lib/test/test_asyncio/test_windows_events.py
@@ -211,6 +211,26 @@ class ProactorTests(test_utils.TestCase):
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):