Skip to content

Commit 62924a4

Browse files
Skn0ttmiss-islington
authored andcommitted
gh-152569: Fix asyncio.wait leaking tasks via await-graph on long-lived futures (GH-152585)
(cherry picked from commit f8514dc) Co-authored-by: Simon Knott <info@simonknott.de>
1 parent 3f0ef65 commit 62924a4

3 files changed

Lines changed: 17 additions & 0 deletions

File tree

Lib/asyncio/tasks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ def _on_completion(f):
540540
timeout_handle.cancel()
541541
for f in fs:
542542
f.remove_done_callback(_on_completion)
543+
futures.future_discard_from_awaited_by(f, cur_task)
543544

544545
done, pending = set(), set()
545546
for f in fs:

Lib/test/test_asyncio/test_tasks.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,19 @@ def gen():
12161216
loop.advance_time(10)
12171217
loop.run_until_complete(asyncio.wait([a, b]))
12181218

1219+
def test_wait_discards_awaited_by_for_pending(self):
1220+
# gh-152569: wait() must remove itself from the await-graph of every
1221+
# future once it returns, including futures that never resolved.
1222+
async def coro():
1223+
immortal = self.loop.create_future()
1224+
done = self.new_task(self.loop, asyncio.sleep(0))
1225+
await asyncio.wait({done, immortal},
1226+
return_when=asyncio.FIRST_COMPLETED)
1227+
self.assertFalse(immortal._asyncio_awaited_by)
1228+
immortal.cancel()
1229+
1230+
self.loop.run_until_complete(self.new_task(self.loop, coro()))
1231+
12191232
def test_wait_really_done(self):
12201233
# there is possibility that some tasks in the pending list
12211234
# became done but their callbacks haven't all been called yet
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`asyncio.wait` leaking waiting tasks via the await-graph when racing a
2+
future that never resolves. The waiting task is now discarded from every future's
3+
``awaited_by`` set once :func:`~asyncio.wait` returns, even for pending futures.

0 commit comments

Comments
 (0)