Skip to content

Commit 99a9b34

Browse files
bpo-37658: Actually return result in race condition (GH-29202) (GH-29832)
(cherry picked from commit 934a826) Co-authored-by: Sam Bull <[email protected]> Co-authored-by: Sam Bull <[email protected]>
1 parent 031e2bb commit 99a9b34

File tree

3 files changed

+13
-36
lines changed

3 files changed

+13
-36
lines changed

Lib/asyncio/tasks.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,9 @@ async def wait_for(fut, timeout, *, loop=None):
449449

450450
await _cancel_and_wait(fut, loop=loop)
451451
try:
452-
fut.result()
452+
return fut.result()
453453
except exceptions.CancelledError as exc:
454454
raise exceptions.TimeoutError() from exc
455-
else:
456-
raise exceptions.TimeoutError()
457455

458456
waiter = loop.create_future()
459457
timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
@@ -489,11 +487,9 @@ async def wait_for(fut, timeout, *, loop=None):
489487
# exception, we should re-raise it
490488
# See https://bugs.python.org/issue40607
491489
try:
492-
fut.result()
490+
return fut.result()
493491
except exceptions.CancelledError as exc:
494492
raise exceptions.TimeoutError() from exc
495-
else:
496-
raise exceptions.TimeoutError()
497493
finally:
498494
timeout_handle.cancel()
499495

Lib/test/test_asyncio/test_tasks.py

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,20 +1121,16 @@ def gen():
11211121
self.assertEqual(res, "ok")
11221122

11231123
def test_wait_for_cancellation_race_condition(self):
1124-
def gen():
1125-
yield 0.1
1126-
yield 0.1
1127-
yield 0.1
1128-
yield 0.1
1124+
async def inner():
1125+
with contextlib.suppress(asyncio.CancelledError):
1126+
await asyncio.sleep(1)
1127+
return 1
11291128

1130-
loop = self.new_test_loop(gen)
1129+
async def main():
1130+
result = await asyncio.wait_for(inner(), timeout=.01)
1131+
assert result == 1
11311132

1132-
fut = self.new_future(loop)
1133-
loop.call_later(0.1, fut.set_result, "ok")
1134-
task = loop.create_task(asyncio.wait_for(fut, timeout=1))
1135-
loop.call_later(0.1, task.cancel)
1136-
res = loop.run_until_complete(task)
1137-
self.assertEqual(res, "ok")
1133+
asyncio.run(main())
11381134

11391135
def test_wait_for_waits_for_task_cancellation(self):
11401136
loop = asyncio.new_event_loop()
@@ -1213,24 +1209,6 @@ async def inner():
12131209
with self.assertRaises(FooException):
12141210
loop.run_until_complete(foo())
12151211

1216-
def test_wait_for_raises_timeout_error_if_returned_during_cancellation(self):
1217-
loop = asyncio.new_event_loop()
1218-
self.addCleanup(loop.close)
1219-
1220-
async def foo():
1221-
async def inner():
1222-
try:
1223-
await asyncio.sleep(0.2)
1224-
except asyncio.CancelledError:
1225-
return 42
1226-
1227-
inner_task = self.new_task(loop, inner())
1228-
1229-
await asyncio.wait_for(inner_task, timeout=_EPSILON)
1230-
1231-
with self.assertRaises(asyncio.TimeoutError):
1232-
loop.run_until_complete(foo())
1233-
12341212
def test_wait_for_self_cancellation(self):
12351213
loop = asyncio.new_event_loop()
12361214
self.addCleanup(loop.close)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix issue when on certain conditions ``asyncio.wait_for()`` may allow a
2+
coroutine to complete successfully, but fail to return the result,
3+
potentially causing memory leaks or other issues.

0 commit comments

Comments
 (0)