Skip to content
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

Avoid refcycles in run exc #3120

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions src/trio/_core/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -1726,6 +1726,7 @@ def close(self) -> None:
self.asyncgens.close()
if "after_run" in self.instruments:
self.instruments.call("after_run")
self.system_nursery = None
# This is where KI protection gets disabled, so we do it last
self.ki_manager.close()

Expand Down Expand Up @@ -1936,6 +1937,7 @@ def task_exited(self, task: Task, outcome: Outcome[object]) -> None:
task._activate_cancel_status(None)
self.tasks.remove(task)
if task is self.init_task:
self.init_task = None
# If the init task crashed, then something is very wrong and we
# let the error propagate. (It'll eventually be wrapped in a
# TrioInternalError.)
Expand All @@ -1946,6 +1948,7 @@ def task_exited(self, task: Task, outcome: Outcome[object]) -> None:
raise TrioInternalError
else:
if task is self.main_task:
self.main_task = None
self.main_task_outcome = outcome
outcome = Value(None)
assert task._parent_nursery is not None, task
Expand Down Expand Up @@ -2101,14 +2104,14 @@ def deliver_ki(self) -> None:
def _deliver_ki_cb(self) -> None:
if not self.ki_pending:
return
# Can't happen because main_task and run_sync_soon_task are created at
# the same time -- so even if KI arrives before main_task is created,
# we won't get here until afterwards.
assert self.main_task is not None
if self.main_task_outcome is not None:
# We're already in the process of exiting -- leave ki_pending set
# and we'll check it again on our way out of run().
return
# Can't happen because main_task and run_sync_soon_task are created at
# the same time -- so even if KI arrives before main_task is created,
# we won't get here until afterwards.
assert self.main_task is not None
self.main_task._attempt_delivery_of_pending_ki()

################
Expand Down Expand Up @@ -2411,12 +2414,15 @@ def run(
sniffio_library.name = prev_library
# Inlined copy of runner.main_task_outcome.unwrap() to avoid
# cluttering every single Trio traceback with an extra frame.
if isinstance(runner.main_task_outcome, Value):
return cast("RetT", runner.main_task_outcome.value)
elif isinstance(runner.main_task_outcome, Error):
raise runner.main_task_outcome.error
else: # pragma: no cover
raise AssertionError(runner.main_task_outcome)
try:
if isinstance(runner.main_task_outcome, Value):
return cast("RetT", runner.main_task_outcome.value)
elif isinstance(runner.main_task_outcome, Error):
raise runner.main_task_outcome.error
else: # pragma: no cover
raise AssertionError(runner.main_task_outcome)
finally:
del runner


# Explicit .../"Any" not allowed
Expand Down Expand Up @@ -2826,6 +2832,7 @@ def unrolled_run(
if isinstance(runner.main_task_outcome, Error):
ki.__context__ = runner.main_task_outcome.error
runner.main_task_outcome = Error(ki)
del runner


################################################################
Expand Down
Loading