diff options
author | Guido van Rossum <guido@python.org> | 2024-04-09 15:17:28 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-09 15:17:28 (GMT) |
commit | fa58e75a8605146a89ef72b58b4529669ac48366 (patch) | |
tree | 1e15a0af9593b52529a6e637703ea878d6d295e0 /Lib/asyncio | |
parent | 22b25d1ebaab7b8c4833a8c120c8b4699a830f40 (diff) | |
download | cpython-fa58e75a8605146a89ef72b58b4529669ac48366.zip cpython-fa58e75a8605146a89ef72b58b4529669ac48366.tar.gz cpython-fa58e75a8605146a89ef72b58b4529669ac48366.tar.bz2 |
gh-116720: Fix corner cases of taskgroups (#117407)
This prevents external cancellations of a task group's parent task to
be dropped when an internal cancellation happens at the same time.
Also strengthen the semantics of uncancel() to clear self._must_cancel
when the cancellation count reaches zero.
Co-Authored-By: Tin Tvrtković <tinchester@gmail.com>
Co-Authored-By: Arthur Tacca
Diffstat (limited to 'Lib/asyncio')
-rw-r--r-- | Lib/asyncio/taskgroups.py | 19 | ||||
-rw-r--r-- | Lib/asyncio/tasks.py | 2 |
2 files changed, 15 insertions, 6 deletions
diff --git a/Lib/asyncio/taskgroups.py b/Lib/asyncio/taskgroups.py index 57f0123..f2ee964 100644 --- a/Lib/asyncio/taskgroups.py +++ b/Lib/asyncio/taskgroups.py @@ -77,12 +77,6 @@ class TaskGroup: propagate_cancellation_error = exc else: propagate_cancellation_error = None - if self._parent_cancel_requested: - # If this flag is set we *must* call uncancel(). - if self._parent_task.uncancel() == 0: - # If there are no pending cancellations left, - # don't propagate CancelledError. - propagate_cancellation_error = None if et is not None: if not self._aborting: @@ -130,6 +124,13 @@ class TaskGroup: if self._base_error is not None: raise self._base_error + if self._parent_cancel_requested: + # If this flag is set we *must* call uncancel(). + if self._parent_task.uncancel() == 0: + # If there are no pending cancellations left, + # don't propagate CancelledError. + propagate_cancellation_error = None + # Propagate CancelledError if there is one, except if there # are other errors -- those have priority. if propagate_cancellation_error is not None and not self._errors: @@ -139,6 +140,12 @@ class TaskGroup: self._errors.append(exc) if self._errors: + # If the parent task is being cancelled from the outside + # of the taskgroup, un-cancel and re-cancel the parent task, + # which will keep the cancel count stable. + if self._parent_task.cancelling(): + self._parent_task.uncancel() + self._parent_task.cancel() # Exceptions are heavy objects that can have object # cycles (bad for GC); let's not keep a reference to # a bunch of them. diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 7fb697b9..dadcb5b 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -255,6 +255,8 @@ class Task(futures._PyFuture): # Inherit Python Task implementation """ if self._num_cancels_requested > 0: self._num_cancels_requested -= 1 + if self._num_cancels_requested == 0: + self._must_cancel = False return self._num_cancels_requested def __eager_start(self): |