Skip to content

Commit

Permalink
Avoid creating reference cycles in case of nursery cancel
Browse files Browse the repository at this point in the history
  • Loading branch information
belm0 committed Jan 8, 2021
1 parent bbd6b43 commit de07659
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 2 deletions.
2 changes: 1 addition & 1 deletion trio/_core/_multierror.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def push_tb_down(tb, exc, preserved):
preserved = set()
new_root_exc = filter_tree(root_exc, preserved)
push_tb_down(None, root_exc, preserved)
# Delete the local functions avoid a reference cycle (see
# Delete the local functions to avoid a reference cycle (see
# test_simple_cancel_scope_usage_doesnt_create_cyclic_garbage)
del filter_tree, push_tb_down
return new_root_exc
Expand Down
7 changes: 6 additions & 1 deletion trio/_core/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,12 @@ def aborted(raise_cancel):
popped = self._parent_task._child_nurseries.pop()
assert popped is self
if self._pending_excs:
return MultiError(self._pending_excs)
try:
return MultiError(self._pending_excs)
finally:
# avoid a garbage cycle
# (see test_nursery_cancel_doesnt_create_cyclic_garbage)
del self._pending_excs

def start_soon(self, async_fn, *args, name=None):
"""Creates a child task, scheduling ``await async_fn(*args)``.
Expand Down
23 changes: 23 additions & 0 deletions trio/_core/tests/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -2226,3 +2226,26 @@ async def do_a_cancel():
finally:
gc.set_debug(old_flags)
gc.garbage.clear()


@pytest.mark.skipif(
sys.implementation.name != "cpython", reason="Only makes sense with refcounting GC"
)
async def test_nursery_cancel_doesnt_create_cyclic_garbage():
# https://github.com/python-trio/trio/issues/1770#issuecomment-730229423
gc.collect()

old_flags = gc.get_debug()
try:
for i in range(3):
async with _core.open_nursery() as nursery:
gc.collect()
gc.set_debug(gc.DEBUG_LEAK)
nursery.cancel_scope.cancel()

gc.collect()
gc.set_debug(0)
assert not gc.garbage
finally:
gc.set_debug(old_flags)
gc.garbage.clear()

0 comments on commit de07659

Please sign in to comment.