diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2014-07-05 13:29:41 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2014-07-05 13:29:41 (GMT) |
commit | a9acbe82e7822e555b669139fdd8a7cb7667492c (patch) | |
tree | 6d0d3962f744ab352e231cc2aba1874531f947bb /Lib/asyncio | |
parent | 5021cb553c2c6bf7219882c36b9f6b2bdee5bd24 (diff) | |
download | cpython-a9acbe82e7822e555b669139fdd8a7cb7667492c.zip cpython-a9acbe82e7822e555b669139fdd8a7cb7667492c.tar.gz cpython-a9acbe82e7822e555b669139fdd8a7cb7667492c.tar.bz2 |
Closes #21886, #21447: Fix a race condition in asyncio when setting the result
of a Future with call_soon(). Add an helper, a private method, to set the
result only if the future was not cancelled.
Diffstat (limited to 'Lib/asyncio')
-rw-r--r-- | Lib/asyncio/coroutines.py | 6 | ||||
-rw-r--r-- | Lib/asyncio/futures.py | 6 | ||||
-rw-r--r-- | Lib/asyncio/proactor_events.py | 2 | ||||
-rw-r--r-- | Lib/asyncio/queues.py | 2 | ||||
-rw-r--r-- | Lib/asyncio/selector_events.py | 5 | ||||
-rw-r--r-- | Lib/asyncio/tasks.py | 3 | ||||
-rw-r--r-- | Lib/asyncio/unix_events.py | 4 |
7 files changed, 21 insertions, 7 deletions
diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index 71a1ec4..7654a0b 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -64,6 +64,12 @@ class CoroWrapper: self.gen = gen self.func = func self._source_traceback = traceback.extract_stack(sys._getframe(1)) + # __name__, __qualname__, __doc__ attributes are set by the coroutine() + # decorator + + def __repr__(self): + return ('<%s %s>' + % (self.__class__.__name__, _format_coroutine(self))) def __iter__(self): return self diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index fcc90d1..022fef7 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -316,6 +316,12 @@ class Future: # So-called internal methods (note: no set_running_or_notify_cancel()). + def _set_result_unless_cancelled(self, result): + """Helper setting the result only if the future was not cancelled.""" + if self.cancelled(): + return + self.set_result(result) + def set_result(self, result): """Mark the future done and set its result. diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index b76f69e..a80876f 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -38,7 +38,7 @@ class _ProactorBasePipeTransport(transports._FlowControlMixin, self._server.attach(self) self._loop.call_soon(self._protocol.connection_made, self) if waiter is not None: - self._loop.call_soon(waiter.set_result, None) + self._loop.call_soon(waiter._set_result_unless_cancelled, None) def _set_extra(self, sock): self._extra['pipe'] = sock diff --git a/Lib/asyncio/queues.py b/Lib/asyncio/queues.py index 57afb05..41551a9 100644 --- a/Lib/asyncio/queues.py +++ b/Lib/asyncio/queues.py @@ -173,7 +173,7 @@ class Queue: # run, we need to defer the put for a tick to ensure that # getters and putters alternate perfectly. See # ChannelTest.test_wait. - self._loop.call_soon(putter.set_result, None) + self._loop.call_soon(putter._set_result_unless_cancelled, None) return self._get() diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index df64aec..2a17034 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -481,7 +481,7 @@ class _SelectorSocketTransport(_SelectorTransport): self._loop.add_reader(self._sock_fd, self._read_ready) self._loop.call_soon(self._protocol.connection_made, self) if waiter is not None: - self._loop.call_soon(waiter.set_result, None) + self._loop.call_soon(waiter._set_result_unless_cancelled, None) def pause_reading(self): if self._closing: @@ -690,7 +690,8 @@ class _SelectorSslTransport(_SelectorTransport): self._loop.add_reader(self._sock_fd, self._read_ready) self._loop.call_soon(self._protocol.connection_made, self) if self._waiter is not None: - self._loop.call_soon(self._waiter.set_result, None) + self._loop.call_soon(self._waiter._set_result_unless_cancelled, + None) def pause_reading(self): # XXX This is a bit icky, given the comment at the top of diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index dd191e7..8c7217b 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -487,7 +487,8 @@ def as_completed(fs, *, loop=None, timeout=None): def sleep(delay, result=None, *, loop=None): """Coroutine that completes after a given time (in seconds).""" future = futures.Future(loop=loop) - h = future._loop.call_later(delay, future.set_result, result) + h = future._loop.call_later(delay, + future._set_result_unless_cancelled, result) try: return (yield from future) finally: diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py index 5f728b5..535ea22 100644 --- a/Lib/asyncio/unix_events.py +++ b/Lib/asyncio/unix_events.py @@ -269,7 +269,7 @@ class _UnixReadPipeTransport(transports.ReadTransport): self._loop.add_reader(self._fileno, self._read_ready) self._loop.call_soon(self._protocol.connection_made, self) if waiter is not None: - self._loop.call_soon(waiter.set_result, None) + self._loop.call_soon(waiter._set_result_unless_cancelled, None) def _read_ready(self): try: @@ -353,7 +353,7 @@ class _UnixWritePipeTransport(transports._FlowControlMixin, self._loop.call_soon(self._protocol.connection_made, self) if waiter is not None: - self._loop.call_soon(waiter.set_result, None) + self._loop.call_soon(waiter._set_result_unless_cancelled, None) def get_write_buffer_size(self): return sum(len(data) for data in self._buffer) |