GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-116720: Fix corner cases of taskgroups by gvanrossum · Pull Request #117407 · python/cpython

Conversation

@gvanrossum

@gvanrossum gvanrossum commented Mar 31, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member
  • Make test_timeouts pass (test_timeout_taskgroup)
  • Add tests derives from examples in the issue
  • Add test for new uncancel() behavior (reset _must_cancel)
  • Add news entry
  • Add What's New entry
  • Update docs for uncancel()
  • Update docs for task groups
  • Add Co-Authored-By: @arthur-tacca
  • Issue: Nested TaskGroup can silently swallow cancellation request from parent TaskGroup #116720

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot mentioned this pull request Mar 31, 2024
Nested TaskGroup can silently swallow cancellation request from parent TaskGroup #116720
Closed
@gvanrossum
gvanrossum force-pushed the fix-tg branch 2 times, most recently from e97f22a to 5912d70 Compare April 2, 2024 17:37
@gvanrossum

gvanrossum commented Apr 2, 2024

Copy link
Copy Markdown
Member Author

@arthur-tacca, what do you think of this PR? Does it pass all your tests? Do you want to contribute in any way (e.g. by converting your example programs into unittests)?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gvanrossum

gvanrossum commented Apr 2, 2024

Copy link
Copy Markdown
Member Author

@agronholm I wonder if you could review this PR? (It's in draft mode because there are no tests or doc yet, but the changes to taskgroups.py and tasks.py are as I plan to keep them.)

It might affect anyio because of two things: (a) Better support for nested taskgroups; (b) change to uncancel() to reset the internal "must cancel" flag when the pending cancellation count reaches zero.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gvanrossum

gvanrossum commented Apr 2, 2024

Copy link
Copy Markdown
Member Author

@Tinche Same question for you -- you might have an opinion on the changes that I'm proposing to make to asyncio task and task group behavior here.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@agronholm

agronholm commented Apr 2, 2024

Copy link
Copy Markdown
Contributor

@agronholm I wonder if you could review this PR? (It's in draft mode because there are no tests or doc yet, but the changes to taskgroups.py and tasks.py are as I plan to keep them.)

It might affect anyio because of two things: (a) Better support for nested taskgroups; (b) change to uncancel() to reset the internal "must cancel" flag when the pending cancellation count reaches zero.

Sure, I can take a look.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@agronholm

agronholm commented Apr 2, 2024

Copy link
Copy Markdown
Contributor

It's hard to definitively say if this will affect AnyIO, but I did run the task group tests against this PR and they passed. The only place where AnyIO looks at this flag is in CancelScope._deliver_cancellation() where it will skip explicitly cancelling a task if this flag is already set.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gvanrossum

gvanrossum commented Apr 2, 2024

Copy link
Copy Markdown
Member Author

Sorry, which flag is that? ‘cancelling()’ or ‘_must_cancel’?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@agronholm

agronholm commented Apr 2, 2024

Copy link
Copy Markdown
Contributor

Sorry, which flag is that? ‘cancelling()’ or ‘_must_cancel’?

_must_cancel.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

agronholm
agronholm reviewed Apr 2, 2024
View reviewed changes
Comment thread Lib/asyncio/tasks.py
if self._num_cancels_requested > 0:
self._num_cancels_requested -= 1
if self._num_cancels_requested == 0:
self._must_cancel = False

@agronholm agronholm Apr 2, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this also clear the cancel message?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@gvanrossum gvanrossum Apr 3, 2024

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt it -- self._cancel_message is never cleared, and it's never used unless self._must_cancel is set.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@agronholm agronholm Apr 3, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay – I was just confused by that Py_CLEAR() in the C version, but this explains it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@gvanrossum gvanrossum Apr 4, 2024

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you're right, that CLEAR is unnecessary there too. :-)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
gvanrossum added 8 commits April 4, 2024 09:23
@gvanrossum
Fix taskgroups handling of parent cancellation
90695c5
@gvanrossum
When errors and parent cancellation are both pending, re-cancel parent
6c4876d
@gvanrossum
When uncancel() reaches zero, clear _must_cancel
df6e8b7
This fixes the one failing test in test_timeouts.
Surprisingly, it doesn't break any other asyncio tests.
@gvanrossum
Remove 'experimental' comment
c762e36
@gvanrossum
Remove unneeded Py_CLEAR(self->task_cancel_msg)
4989042
@gvanrossum
Simple test for preserving cancelling() level
3f91f2d
Co-Authored-By: @Tinche
@gvanrossum
Add test for nested task groups
55ff73f
Co-Authored-By: @arthur-tacca
@gvanrossum
Add test that is fixed by the re-cancel in __aexit__
50fd8d6
Co-Authored-By: @arthur-tacca
@gvanrossum
gvanrossum force-pushed the fix-tg branch from 35c807b to 50fd8d6 Compare April 4, 2024 19:55
gvanrossum added 4 commits April 4, 2024 14:57
@gvanrossum
Test that uncancel() sets _must_cancel to False
93ba76a
@gvanrossum
Move a stray asyncio news item into the asyncio section
f1f89c0
@gvanrossum
Add blurb
48c6dda
@gvanrossum
Add what's new entry
1e20728
@gvanrossum

gvanrossum commented Apr 4, 2024

Copy link
Copy Markdown
Member Author

Still to do are docs updates for uncancel and for task groups. Also, I'd love help with shortening the news blurb.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gvanrossum added 4 commits April 5, 2024 15:03
@gvanrossum
Update uncancel docs
bc1522c
@gvanrossum
Update task group docs
7f6e5ff
@gvanrossum
Merge remote-tracking branch 'origin/main' into fix-tg
56251ff
@gvanrossum
Fix markup error
c54fb22
@gvanrossum
gvanrossum marked this pull request as ready for review April 5, 2024 22:09
@gvanrossum
gvanrossum requested review from 1st1, asvetlov, kumaraditya303 and willingc as code owners April 5, 2024 22:09
@gvanrossum gvanrossum self-assigned this Apr 5, 2024
@bedevere-app bedevere-app Bot added the awaiting core review label Apr 5, 2024
@gvanrossum
gvanrossum requested a review from agronholm April 5, 2024 22:09
@gvanrossum

gvanrossum commented Apr 5, 2024

Copy link
Copy Markdown
Member Author

@willingc Would you be my reviewer for this PR? I feel particularly challenged by the various documentation updates -- this is a pretty subtle improvement (given how long it's taken before someone reported the issue :-) and I'm struggling describing the changes and the end state appropriately in various places:

  • Changelog (Misc/NEWS)
  • What's new in 3.13
  • asyncio-task.rst: uncancel docs and task group docs

I'd say that currently the Changelog news item is probably too long, and there's too much duplication between all three forms of documentation.

@arthur-tacca: I haven't heard from you on the issue in a while. Your feedback on any part of this PR would be much appreciated. I plan to give you credit by adding Co-Authored-By: Arthur Tacca to the final commit message, since I cribbed most of the unit tests from your original demonstrations. I will also add (Inspired by an issue reported by Arthur Tacca in :gh:`116720`.) to the "what's new" section.

willingc reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gvanrossum
Add attribution to Arthur Tacca issue
6d4b3d7
@arthur-tacca

arthur-tacca commented Apr 7, 2024

Copy link
Copy Markdown

@gvanrossum Sorry for ghosting you. I had some limited time for open source between jobs (which I actually planned to spend mainly on something else), which has run out and it's now hard for me to post even short comments like this one.

Super-brief review: Your code changes look good to me. As I said in an earlier comment, I think it's good that you've just moved (rather than duplicated) the if self._parent_cancel_requested check. The check for self._parent_task.cancelling() also looks good. As for the change to Task (uncancel() will rest _must_cancel if cancelling reaches 0), I can't immediately think of a situation it would naturally arise when using task groups (as opposed to manually triggering it with an explicit uncancel() call) and I don't have time to try and figure out one, but it looks like a sensible thing to do if that situation does arise; perhaps it would come up with asyncio.Timeout objects, which I haven't experimented with at all.

Credit: Thanks for crediting me, that's much appreciated. I don't think my contributions are significant enough to need a CLA but in any case I have signed one (for a small change to typing extensions).

Changelog:

  • "... For example, when two task groups are nested and both experience an exception in a child task simultaneously, it was possible that the outer task group would hang, because ..." I'm not sure "would hang" is accurate. It would process the rest of the logic after the inner task group.
  • "This allows a try / except* surrounding the task group to handle the exceptions in the ExceptionGroup without losing the cancellation." This is a bit specific to the particular example I gave. For a start, it also applies to try / finally. It also could stop a except* or finally block running in full (if it has await ... in it), which is sort of the opposite of what the text says. Perhaps better to say something deliberately a bit less specific, e.g. more along the lines of "This ensures that a CancelledError will be raised at the next await, so the cancellation is not lost".

Docs changes: "Task groups are careful not to drop outside cancellations when they collide with a cancellation internal to the task group." I must admit, I would struggle to understand what this means if I hadn't been involved in this issue. But that isn't very helpful since I don't have a better suggestion to give.

willingc and gvanrossum reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

willingc
willingc reviewed Apr 7, 2024
View reviewed changes
Comment thread Lib/asyncio/taskgroups.py Outdated
self._errors.append(exc)

if self._errors:
# If the parent task is being cancelled from the outside,

@willingc willingc Apr 7, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# If the parent task is being cancelled from the outside,
# If the parent task is being cancelled from the outside of the taskgroup,

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gvanrossum reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
willingc
willingc approved these changes Apr 7, 2024
View reviewed changes

@willingc willingc left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gvanrossum Overall, this looks good. I made a few suggestions on wording, but the existing text is also fine. Thanks!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Lib/asyncio/taskgroups.py Outdated

if self._errors:
# If the parent task is being cancelled from the outside,
# re-cancel it, while keeping the cancel count stable.

@willingc willingc Apr 7, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# re-cancel it, while keeping the cancel count stable.
# un-cancel and re-cancel the parent task, which will keep the cancel count stable.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gvanrossum reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread Doc/library/asyncio-task.rst Outdated
the cancellation state.

When this method decrements the cancellation count to zero,
if a previous :meth:`cancel` call had arranged for a

@willingc willingc Apr 7, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if a previous :meth:`cancel` call had arranged for a
the method checks if a previous :meth:`cancel` call had arranged for

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gvanrossum reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread Doc/library/asyncio-task.rst Outdated

When this method decrements the cancellation count to zero,
if a previous :meth:`cancel` call had arranged for a
:exc:`CancelledError` to be thrown into the task,

@willingc willingc Apr 7, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:exc:`CancelledError` to be thrown into the task,
:exc:`CancelledError` to be thrown into the task.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gvanrossum reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread Doc/library/asyncio-task.rst Outdated
When this method decrements the cancellation count to zero,
if a previous :meth:`cancel` call had arranged for a
:exc:`CancelledError` to be thrown into the task,
but this hadn't been done yet, that arrangement will be

@willingc willingc Apr 7, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
but this hadn't been done yet, that arrangement will be
If this hadn't been done yet, that arrangement will be

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gvanrossum reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread Doc/whatsnew/3.13.rst
(Contributed by Arthur Tacca and Jason Zhang in :gh:`115957`.)

* Improved behavior of :class:`asyncio.TaskGroup` when an outside cancellation
collides with an internal cancellation. For example, when two task groups

@willingc willingc Apr 7, 2024

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting core review labels Apr 7, 2024
gvanrossum added 4 commits April 8, 2024 13:41
@gvanrossum
Wording changes suggested by Carol Willing
b086609
@gvanrossum
Changelog/whatsnew updates from Arthur Tacca
8ac42f6
Also switch to consistently using internal/external
instead of inside/outside.
@gvanrossum
Attempted doc improvements inspired by Arthur Tacca's feedback
ac87a8e
@gvanrossum
Merge remote-tracking branch 'origin/main' into fix-tg
be9365c
@gvanrossum
gvanrossum requested a review from willingc April 9, 2024 00:17
willingc
willingc approved these changes Apr 9, 2024
View reviewed changes

@willingc willingc left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Thanks everyone.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@gvanrossum
gvanrossum merged commit fa58e75 into python:main Apr 9, 2024
@bedevere-app bedevere-app Bot removed the awaiting merge label Apr 9, 2024
@gvanrossum
gvanrossum deleted the fix-tg branch April 9, 2024 15:17
@Tinche

Tinche commented Apr 16, 2024

Copy link
Copy Markdown
Contributor

@gvanrossum would you mind if I copied your solution into quattro to serve as a backport of sorts, for 3.11 and 3.12? Quattro is Apache 2 licensed, that's compatible right?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gvanrossum

gvanrossum commented Apr 16, 2024

Copy link
Copy Markdown
Member Author

@gvanrossum would you mind if I copied your solution into quattro to serve as a backport of sorts, for 3.11 and 3.12? Quattro is Apache 2 licensed, that's compatible right?

That should be absolutely fine -- if I understand the PSF license correctly, all you need to do is add this text somewhere

Copyright © 2001-2024 Python Software Foundation; All Rights Reserved

and add a copy of the PSF license for 3.13 somewhere.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

diegorusso pushed a commit to diegorusso/cpython that referenced this pull request Apr 17, 2024
@gvanrossum @Tinche @diegorusso
pythongh-116720: Fix corner cases of taskgroups (python#117407)
611e52e
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ć <[email protected]>
Co-Authored-By: Arthur Tacca
This was referenced Sep 28, 2024
tests/test_run_app.py::TestShutdown hang on py3.13 with aiohappyeyeballs == 2.4.2 aio-libs/aiohttp#9325
Closed
Using eager task factory with aiohttp 3.10.0 and cpython 3.12+ raise an AssertionError in stdlib asyncio.staggered (fixed in aiohappyeyeballs 2.4.3+) aio-libs/aiohttp#8599
Closed
Fix TimerContext not uncancelling the current task aio-libs/aiohttp#9326
Merged
Bump aiohttp to 3.10.8 home-assistant/core#127009
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@willingc willingc willingc approved these changes
@1st1 1st1 Awaiting requested review from 1st1 1st1 is a code owner
@asvetlov asvetlov Awaiting requested review from asvetlov asvetlov is a code owner
@kumaraditya303 kumaraditya303 Awaiting requested review from kumaraditya303 kumaraditya303 is a code owner
@agronholm agronholm Awaiting requested review from agronholm

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@gvanrossum gvanrossum

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

5 participants

@gvanrossum @agronholm @arthur-tacca @Tinche @willingc
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.