Skip to content

Commit e6a5767

Browse files
authored
gh-93297: Make asyncio task groups prevent child tasks from being GCed (#93299)
1 parent 70cfe56 commit e6a5767

File tree

2 files changed

+7
-13
lines changed

2 files changed

+7
-13
lines changed

Lib/asyncio/taskgroups.py

+6-13
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
__all__ = ["TaskGroup"]
55

6-
import weakref
7-
86
from . import events
97
from . import exceptions
108
from . import tasks
@@ -19,8 +17,7 @@ def __init__(self):
1917
self._loop = None
2018
self._parent_task = None
2119
self._parent_cancel_requested = False
22-
self._tasks = weakref.WeakSet()
23-
self._unfinished_tasks = 0
20+
self._tasks = set()
2421
self._errors = []
2522
self._base_error = None
2623
self._on_completed_fut = None
@@ -29,8 +26,6 @@ def __repr__(self):
2926
info = ['']
3027
if self._tasks:
3128
info.append(f'tasks={len(self._tasks)}')
32-
if self._unfinished_tasks:
33-
info.append(f'unfinished={self._unfinished_tasks}')
3429
if self._errors:
3530
info.append(f'errors={len(self._errors)}')
3631
if self._aborting:
@@ -93,7 +88,7 @@ async def __aexit__(self, et, exc, tb):
9388
# can be cancelled multiple times if our parent task
9489
# is being cancelled repeatedly (or even once, when
9590
# our own cancellation is already in progress)
96-
while self._unfinished_tasks:
91+
while self._tasks:
9792
if self._on_completed_fut is None:
9893
self._on_completed_fut = self._loop.create_future()
9994

@@ -114,7 +109,7 @@ async def __aexit__(self, et, exc, tb):
114109

115110
self._on_completed_fut = None
116111

117-
assert self._unfinished_tasks == 0
112+
assert not self._tasks
118113

119114
if self._base_error is not None:
120115
raise self._base_error
@@ -141,15 +136,14 @@ async def __aexit__(self, et, exc, tb):
141136
def create_task(self, coro, *, name=None, context=None):
142137
if not self._entered:
143138
raise RuntimeError(f"TaskGroup {self!r} has not been entered")
144-
if self._exiting and self._unfinished_tasks == 0:
139+
if self._exiting and not self._tasks:
145140
raise RuntimeError(f"TaskGroup {self!r} is finished")
146141
if context is None:
147142
task = self._loop.create_task(coro)
148143
else:
149144
task = self._loop.create_task(coro, context=context)
150145
tasks._set_task_name(task, name)
151146
task.add_done_callback(self._on_task_done)
152-
self._unfinished_tasks += 1
153147
self._tasks.add(task)
154148
return task
155149

@@ -169,10 +163,9 @@ def _abort(self):
169163
t.cancel()
170164

171165
def _on_task_done(self, task):
172-
self._unfinished_tasks -= 1
173-
assert self._unfinished_tasks >= 0
166+
self._tasks.discard(task)
174167

175-
if self._on_completed_fut is not None and not self._unfinished_tasks:
168+
if self._on_completed_fut is not None and not self._tasks:
176169
if not self._on_completed_fut.done():
177170
self._on_completed_fut.set_result(True)
178171

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make asyncio task groups prevent child tasks from being GCed

0 commit comments

Comments
 (0)