Skip to content

Commit 7590d32

Browse files
committed
[3.7] bpo-37658: Fix asyncio.wait_for() to respect waited task status (pythonGH-21894)
Currently, if `asyncio.wait_for()` itself is cancelled it will always raise `CancelledError` regardless if the underlying task is still running. This is similar to a race with the timeout, which is handled already.. (cherry picked from commit a2118a1) Co-authored-by: Elvis Pranskevichus <elvis@magic.io>
1 parent 4e02981 commit 7590d32

3 files changed

Lines changed: 24 additions & 3 deletions

File tree

Lib/asyncio/tasks.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -434,9 +434,12 @@ async def wait_for(fut, timeout, *, loop=None):
434434
try:
435435
await waiter
436436
except futures.CancelledError:
437-
fut.remove_done_callback(cb)
438-
fut.cancel()
439-
raise
437+
if fut.done():
438+
return fut.result()
439+
else:
440+
fut.remove_done_callback(cb)
441+
fut.cancel()
442+
raise
440443

441444
if fut.done():
442445
return fut.result()

Lib/test/test_asyncio/test_tasks.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,22 @@ def gen():
825825
res = loop.run_until_complete(task)
826826
self.assertEqual(res, "ok")
827827

828+
def test_wait_for_cancellation_race_condition(self):
829+
def gen():
830+
yield 0.1
831+
yield 0.1
832+
yield 0.1
833+
yield 0.1
834+
835+
loop = self.new_test_loop(gen)
836+
837+
fut = self.new_future(loop)
838+
loop.call_later(0.1, fut.set_result, "ok")
839+
task = loop.create_task(asyncio.wait_for(fut, timeout=1))
840+
loop.call_later(0.1, task.cancel)
841+
res = loop.run_until_complete(task)
842+
self.assertEqual(res, "ok")
843+
828844
def test_wait_for_waits_for_task_cancellation(self):
829845
loop = asyncio.new_event_loop()
830846
self.addCleanup(loop.close)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:meth:`asyncio.wait_for` now properly handles races between cancellation of
2+
itself and the completion of the wrapped awaitable.

0 commit comments

Comments
 (0)