timeout_handle.cancel()
for f in fs:
f.remove_done_callback(_on_completion)
+ futures.future_discard_from_awaited_by(f, cur_task)
done, pending = set(), set()
for f in fs:
loop.advance_time(10)
loop.run_until_complete(asyncio.wait([a, b]))
+ def test_wait_discards_awaited_by_for_pending(self):
+ # gh-152569: wait() must remove itself from the await-graph of every
+ # future once it returns, including futures that never resolved.
+ async def coro():
+ immortal = self.loop.create_future()
+ done = self.new_task(self.loop, asyncio.sleep(0))
+ await asyncio.wait({done, immortal},
+ return_when=asyncio.FIRST_COMPLETED)
+ self.assertFalse(immortal._asyncio_awaited_by)
+ immortal.cancel()
+
+ self.loop.run_until_complete(self.new_task(self.loop, coro()))
+
def test_wait_really_done(self):
# there is possibility that some tasks in the pending list
# became done but their callbacks haven't all been called yet
--- /dev/null
+Fix :func:`asyncio.wait` leaking waiting tasks via the await-graph when racing a
+future that never resolves. The waiting task is now discarded from every future's
+``awaited_by`` set once :func:`~asyncio.wait` returns, even for pending futures.