From 4151061855b571bf8a7579daa7875b8e243057b9 Mon Sep 17 00:00:00 2001 From: CtrlZvi Date: Sun, 20 May 2018 03:21:10 -0700 Subject: bpo-26819: Prevent proactor double read on resume (#6921) The proactor event loop has a race condition when reading with pausing/resuming. `resume_reading()` unconditionally schedules the read function to read from the current future. If `resume_reading()` was called before the previously scheduled done callback fires, this results in two attempts to get the data from the most recent read and an assertion failure. This commit tracks whether or not `resume_reading` needs to reschedule the callback to restart the loop, preventing a second attempt to read the data. --- Lib/asyncio/proactor_events.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Lib/asyncio') diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index b675c8200c..877dfb0746 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -161,6 +161,7 @@ class _ProactorReadPipeTransport(_ProactorBasePipeTransport, extra=None, server=None): super().__init__(loop, sock, protocol, waiter, extra, server) self._paused = False + self._reschedule_on_resume = False if protocols._is_buffered_protocol(protocol): self._loop_reading = self._loop_reading__get_buffer @@ -180,6 +181,7 @@ class _ProactorReadPipeTransport(_ProactorBasePipeTransport, if self._read_fut is not None and not self._read_fut.done(): self._read_fut.cancel() self._read_fut = None + self._reschedule_on_resume = True if self._loop.get_debug(): logger.debug("%r pauses reading", self) @@ -188,7 +190,9 @@ class _ProactorReadPipeTransport(_ProactorBasePipeTransport, if self._closing or not self._paused: return self._paused = False - self._loop.call_soon(self._loop_reading, self._read_fut) + if self._reschedule_on_resume: + self._loop.call_soon(self._loop_reading, self._read_fut) + self._reschedule_on_resume = False if self._loop.get_debug(): logger.debug("%r resumes reading", self) @@ -208,6 +212,7 @@ class _ProactorReadPipeTransport(_ProactorBasePipeTransport, def _loop_reading__data_received(self, fut=None): if self._paused: + self._reschedule_on_resume = True return data = None @@ -257,6 +262,7 @@ class _ProactorReadPipeTransport(_ProactorBasePipeTransport, def _loop_reading__get_buffer(self, fut=None): if self._paused: + self._reschedule_on_resume = True return nbytes = None -- cgit v1.2.1