From cf63b80bc482ef971ecb6d3ed9a1dc4a93d73744 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 27 May 2022 15:46:57 -0700 Subject: [PATCH] gh-93297: Make asyncio task groups prevent child tasks from being GCed (GH-93299) (#93305) (cherry picked from commit e6a57678cafe18ca132ee9510252168fcc392a8d) Co-authored-by: Yury Selivanov Co-authored-by: Yury Selivanov --- Lib/asyncio/taskgroups.py | 19 ++++++------------- ...2-05-27-13-18-18.gh-issue-93297.e2zuHz.rst | 1 + 2 files changed, 7 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-05-27-13-18-18.gh-issue-93297.e2zuHz.rst diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 6af21f3a15d..9e0610deed2 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -3,8 +3,6 @@ __all__ = ["TaskGroup"] -import weakref - from . import events from . import exceptions from . import tasks @@ -19,8 +17,7 @@ class TaskGroup: self._loop = None self._parent_task = None self._parent_cancel_requested = False - self._tasks = weakref.WeakSet() - self._unfinished_tasks = 0 + self._tasks = set() self._errors = [] self._base_error = None self._on_completed_fut = None @@ -29,8 +26,6 @@ class TaskGroup: info = [''] if self._tasks: info.append(f'tasks={len(self._tasks)}') - if self._unfinished_tasks: - info.append(f'unfinished={self._unfinished_tasks}') if self._errors: info.append(f'errors={len(self._errors)}') if self._aborting: @@ -93,7 +88,7 @@ class TaskGroup: # can be cancelled multiple times if our parent task # is being cancelled repeatedly (or even once, when # our own cancellation is already in progress) - while self._unfinished_tasks: + while self._tasks: if self._on_completed_fut is None: self._on_completed_fut = self._loop.create_future() @@ -114,7 +109,7 @@ class TaskGroup: self._on_completed_fut = None - assert self._unfinished_tasks == 0 + assert not self._tasks if self._base_error is not None: raise self._base_error @@ -141,7 +136,7 @@ class TaskGroup: def create_task(self, coro, *, name=None, context=None): if not self._entered: raise RuntimeError(f"TaskGroup {self!r} has not been entered") - if self._exiting and self._unfinished_tasks == 0: + if self._exiting and not self._tasks: raise RuntimeError(f"TaskGroup {self!r} is finished") if context is None: task = self._loop.create_task(coro) @@ -149,7 +144,6 @@ class TaskGroup: task = self._loop.create_task(coro, context=context) tasks._set_task_name(task, name) task.add_done_callback(self._on_task_done) - self._unfinished_tasks += 1 self._tasks.add(task) return task @@ -169,10 +163,9 @@ class TaskGroup: t.cancel() def _on_task_done(self, task): - self._unfinished_tasks -= 1 - assert self._unfinished_tasks >= 0 + self._tasks.discard(task) - if self._on_completed_fut is not None and not self._unfinished_tasks: + if self._on_completed_fut is not None and not self._tasks: if not self._on_completed_fut.done(): self._on_completed_fut.set_result(True) diff --git a/Misc/NEWS.d/next/Library/2022-05-27-13-18-18.gh-issue-93297.e2zuHz.rst b/Misc/NEWS.d/next/Library/2022-05-27-13-18-18.gh-issue-93297.e2zuHz.rst new file mode 100644 index 00000000000..a8e4cd93d30 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-05-27-13-18-18.gh-issue-93297.e2zuHz.rst @@ -0,0 +1 @@ +Make asyncio task groups prevent child tasks from being GCed