Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-116720: Fix corner cases of taskgroups#117407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
gvanrossum merged 21 commits intopython:mainfromgvanrossum:fix-tg
Apr 9, 2024

Conversation

gvanrossum
Copy link
Member

@gvanrossumgvanrossum commentedMar 31, 2024
edited
Loading

  • Make test_timeouts pass (test_timeout_taskgroup)
  • Add tests derives from examples in the issue
  • Add test for newuncancel() behavior (reset_must_cancel)
  • Add news entry
  • Add What's New entry
  • Update docs foruncancel()
  • Update docs for task groups
  • Add Co-Authored-By:@arthur-tacca

@gvanrossum
Copy link
MemberAuthor

@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)?

@gvanrossum
Copy link
MemberAuthor

@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() toreset the internal "must cancel" flag when the pending cancellation count reaches zero.

@gvanrossum
Copy link
MemberAuthor

@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.

@agronholm
Copy link
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() toreset the internal "must cancel" flag when the pending cancellation count reaches zero.

Sure, I can take a look.

@agronholm
Copy link
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 inCancelScope._deliver_cancellation() where it will skip explicitly cancelling a task if this flag is already set.

@gvanrossum
Copy link
MemberAuthor

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

@agronholm
Copy link
Contributor

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

_must_cancel.

@@ -255,6 +255,8 @@ def uncancel(self):
"""
if self._num_cancels_requested > 0:
self._num_cancels_requested -= 1
if self._num_cancels_requested == 0:
self._must_cancel = False
Copy link
Contributor

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?

Copy link
MemberAuthor

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 unlessself._must_cancel is set.

Copy link
Contributor

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 thatPy_CLEAR() in the C version, but this explains it.

Copy link
MemberAuthor

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. :-)

@gvanrossum
Copy link
MemberAuthor

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

@gvanrossumgvanrossum marked this pull request as ready for reviewApril 5, 2024 22:09
@gvanrossum
Copy link
MemberAuthor

@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 addingCo-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

@arthur-tacca
Copy link

@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) theif self._parent_cancel_requested check. The check forself._parent_task.cancelling() also looks good. As for the change toTask (uncancel() will rest_must_cancel ifcancelling 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 explicituncancel() 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 withasyncio.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 atry /except* surrounding the task group to handle the exceptions in theExceptionGroup without losing the cancellation." This is a bit specific to the particular example I gave. For a start, it also applies totry /finally. It also couldstop aexcept* orfinally block running in full (if it hasawait ... 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 aCancelledError will be raised at the nextawait, 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

@@ -139,6 +140,11 @@ async def __aexit__(self, et, exc, tb):
self._errors.append(exc)

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

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,

gvanrossum reacted with thumbs up emoji
Copy link
Contributor

@willingcwillingc left a 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!

@@ -139,6 +140,11 @@ async def __aexit__(self, et, exc, tb):
self._errors.append(exc)

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

Choose a reason for hiding this comment

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

Suggested change
# re-cancelit, while keeping the cancel count stable.
#un-cancel andre-cancelthe parent task, which will keep the cancel count stable.

gvanrossum reacted with thumbs up emoji
@@ -1369,6 +1391,15 @@ Task Object
catching :exc:`CancelledError`, it needs to call this method to remove
the cancellation state.

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

@willingcwillingcApr 7, 2024
edited
Loading

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 checksif a previous:meth:`cancel` call had arranged for

gvanrossum reacted with thumbs up emoji
@@ -1369,6 +1391,15 @@ Task Object
catching :exc:`CancelledError`, it needs to call this method to remove
the cancellation state.

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,
Copy link
Contributor

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.

gvanrossum reacted with thumbs up emoji
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
Copy link
Contributor

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

gvanrossum reacted with thumbs up emoji
(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
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice!

Copy link
Contributor

@willingcwillingc left a 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.

@gvanrossumgvanrossum merged commitfa58e75 intopython:mainApr 9, 2024
@gvanrossumgvanrossum deleted the fix-tg branchApril 9, 2024 15:17
@Tinche
Copy link
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?

@gvanrossum
Copy link
MemberAuthor

@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.

diegorusso pushed a commit to diegorusso/cpython that referenced this pull requestApr 17, 2024
This prevents external cancellations of a task group's parent task tobe dropped when an internal cancellation happens at the same time.Also strengthen the semantics of uncancel() to clear self._must_cancelwhen the cancellation count reaches zero.Co-Authored-By: Tin Tvrtković <tinchester@gmail.com>Co-Authored-By: Arthur Tacca
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@willingcwillingcwillingc approved these changes

@1st11st1Awaiting requested review from 1st11st1 is a code owner

@asvetlovasvetlovAwaiting requested review from asvetlovasvetlov is a code owner

@kumaraditya303kumaraditya303Awaiting requested review from kumaraditya303kumaraditya303 is a code owner

@agronholmagronholmAwaiting requested review from agronholm

Assignees

@gvanrossumgvanrossum

Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

5 participants
@gvanrossum@agronholm@arthur-tacca@Tinche@willingc

[8]ページ先頭

©2009-2025 Movatter.jp