-
-
Notifications
You must be signed in to change notification settings - Fork 362
check for nursery misnesting on task exit #3307
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
6e4735a
cb1aafc
b33194d
06640eb
7b60273
121ee3d
a729ebf
b7704d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2013,7 +2013,29 @@ def task_exited(self, task: Task, outcome: Outcome[object]) -> None: | |
lot.break_lot(task) | ||
del GLOBAL_PARKING_LOT_BREAKER[task] | ||
|
||
if ( | ||
if task._child_nurseries: | ||
# Forcefully abort any tasks spawned by the misnested nursery to | ||
# avoid internal errors. | ||
runner = GLOBAL_RUN_CONTEXT.runner | ||
for nursery in task._child_nurseries: | ||
nursery.cancel_scope.cancel() | ||
for child in nursery._children: | ||
if child in runner.runq: | ||
runner.runq.remove(child) | ||
runner.tasks.remove(child) | ||
nursery._children.clear() | ||
try: | ||
# Raise this, rather than just constructing it, to get a | ||
# traceback frame included | ||
raise RuntimeError( | ||
"Nursery stack corrupted: nurseries spawned by " | ||
f"{task!r} was still live when the task exited\n{MISNESTING_ADVICE}", | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be inclined to make this some kind of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the cancel scope misnesting error is So unless we want to do a thorough replacing of a lot of |
||
except RuntimeError as new_exc: | ||
if isinstance(outcome, Error): | ||
new_exc.__context__ = outcome.error | ||
outcome = Error(new_exc) | ||
elif ( | ||
task._cancel_status is not None | ||
and task._cancel_status.abandoned_by_misnesting | ||
and task._cancel_status.parent is None | ||
|
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.
Very cool method to abort tasks but to me having nice behavior in this failure mode isn't important. If it's simple to just cancel the nursery then that's fine by me (if it isn't simple then oh well, this is fine. I just don't know what invariants this is relying on/wouldn't be able to spot bugs in this)
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.
The problem is if I don't do this cleanup then a lot of assumptions are broken elsewhere and the resulting
TrioInternalError
masks theRuntimeError
we want to surface. But yeah, this seems ripe for bugs.I think it's when the child tasks are finished they want to yield back to their caller, which now doesn't exist. So we can't even rely on Cancellation semantics either.
There might be a cleaner solution where we postpone the exiting of the current task after requesting cancellation on the nursery... hmm gonna go try that