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

fix object leak #4964

Merged
merged 12 commits into from
Sep 9, 2024
Merged

fix object leak #4964

merged 12 commits into from
Sep 9, 2024

Conversation

willmcgugan
Copy link
Collaborator

@willmcgugan willmcgugan commented Sep 3, 2024

Fixes #4959

This turned out to be quite a rabbit hole. I fixed a class of problems where something was holding on to references to DOM nodes, and preventing garbage collection.

One culprit was count_parameters. The @lru_cache was holding on to a reference chain. I refactored that method to cache in a different way.

Caching or cache-like structures were also holding on to references. I've added clearing of these "caches" to make garbage collection more prompt.

I've made some references weak references, to prevent potential reference cycles.

But the largest part of this work was related to context vars. These weren't being managed well, and it was leaving references around to the app or widgets, which would prevent entire branches from being collected.

The context var issue I fixed by writing a context var context manager, which resets the context var. DOMNodes and App have grown a _context() which sets and restores the active app and message pump.

Incidentally, I think this is the cause of some of the weirdness in tests where tests work in a run but fail in isolation. Errors in managing the context vars could result in an app being shared across tests.

Many of the tests had to be updated after the context var changes. For some reason the pilot fixture broke. I think because of the way that pytest runs async tests.

I'm pretty sure these changes improve shutdown time. Although I haven't profiled.

@willmcgugan willmcgugan marked this pull request as draft September 3, 2024 16:26
@willmcgugan willmcgugan marked this pull request as ready for review September 7, 2024 19:31
@@ -1355,8 +1362,8 @@ def call_from_thread(

async def run_callback() -> CallThreadReturnType:
"""Run the callback, set the result or error on the future."""
self._set_active()
return await invoke(callback_with_args)
with self._context():
Copy link

Choose a reason for hiding this comment

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

An interesting trick, I might use that in some of my code. Thanks for the tip in the tweet!

Comment on lines +64 to +77
if nodes_remaining:
print("NODES REMAINING")

import objgraph

objgraph.show_backrefs(
[
obj
for obj in gc.get_objects()
if any(cls.__name__ == "App" for cls in obj.__class__.__mro__)
],
filename="graph.png",
max_depth=15,
)
Copy link
Member

Choose a reason for hiding this comment

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

Does this need to be removed or commented out?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's marked as xfail for now. When not run in isolation it fails because pytest keeps references around, presumably to inspect and report errors.

tests/test_unmount.py Outdated Show resolved Hide resolved
Comment on lines 203 to 204
getattr(obj, "_watchers", {}).clear()
getattr(obj, "__computes", []).clear()
Copy link
Member

Choose a reason for hiding this comment

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

I feel we should either rename both __watchers and __computes to single underscore prefix or neither.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Watchers was promoted because I needed to clear it from outside of the class. __computes didn't need that.

On reflection though, I can add a method to Reactive to do that, and I can keep the __

Copy link
Member

@darrenburns darrenburns 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 - just a few minor comments. I tried this PR out in Posting to exercise it and could see no issues.

@willmcgugan willmcgugan merged commit cfa093e into main Sep 9, 2024
20 checks passed
@willmcgugan willmcgugan deleted the fix-object-leak branch September 9, 2024 10:08
@peppedilillo
Copy link

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

clear_panes and memory leak
4 participants