summaryrefslogtreecommitdiffstats
path: root/Lib/asyncio
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2024-04-09 15:17:28 (GMT)
committerGitHub <noreply@github.com>2024-04-09 15:17:28 (GMT)
commitfa58e75a8605146a89ef72b58b4529669ac48366 (patch)
tree1e15a0af9593b52529a6e637703ea878d6d295e0 /Lib/asyncio
parent22b25d1ebaab7b8c4833a8c120c8b4699a830f40 (diff)
downloadcpython-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.py19
-rw-r--r--Lib/asyncio/tasks.py2
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):