Skip to content

Commit ba56189

Browse files
committed
gh-155418: Fix TaskGroup hang when a task cancels it before suspending
1 parent b93576a commit ba56189

3 files changed

Lines changed: 37 additions & 0 deletions

File tree

Lib/asyncio/taskgroups.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ def create_task(self, coro, **kwargs):
222222
# the current task too early. gh-128550, gh-128588
223223
self._tasks.add(task)
224224
task.add_done_callback(self._on_task_done)
225+
# gh-155418: an eager task can cancel the group before joining _tasks
226+
if self._aborting and not task.done():
227+
task.cancel()
225228
try:
226229
return task
227230
finally:

Lib/test/test_asyncio/test_taskgroups.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,38 @@ async def test_taskgroup_cancel_before_create_task(self):
11541154
with self.assertRaises(RuntimeError):
11551155
tg.create_task(asyncio.sleep(1))
11561156

1157+
async def test_taskgroup_cancel_from_child_before_first_suspension(self):
1158+
# gh-155418: an eager task can cancel the group before joining _tasks
1159+
async def child(tg):
1160+
tg.cancel()
1161+
await asyncio.sleep(10)
1162+
self.fail("the child was not cancelled")
1163+
1164+
async with asyncio.TaskGroup() as tg:
1165+
task = tg.create_task(child(tg))
1166+
self.assertTrue(task.cancelled())
1167+
1168+
async def test_taskgroup_cancel_keeps_outer_cancellation(self):
1169+
# gh-155433: any cancellation from outside the group must propagate.
1170+
async def child():
1171+
try:
1172+
await asyncio.sleep(10)
1173+
finally:
1174+
await asyncio.sleep(0.1)
1175+
1176+
async def body():
1177+
async with asyncio.TaskGroup() as tg:
1178+
tg.create_task(child())
1179+
await asyncio.sleep(0)
1180+
tg.cancel()
1181+
1182+
task = asyncio.create_task(body())
1183+
await asyncio.sleep(0.01)
1184+
task.cancel('message')
1185+
with self.assertRaises(asyncio.CancelledError) as cm:
1186+
await task
1187+
self.assertEqual('message', cm.exception.args[0])
1188+
11571189
async def test_taskgroup_cancel_before_exception(self):
11581190
async def raise_exc(parent_tg: asyncio.TaskGroup):
11591191
parent_tg.cancel()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :class:`asyncio.TaskGroup` hang when a task created by
2+
:func:`asyncio.eager_task_factory` cancels the group before suspending.

0 commit comments

Comments
 (0)