-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
bpo-37788: Fix reference leak when Thread is never joined #26103
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
Conversation
Note the unit test is originally from PR #25226. |
I started a buildbot run here: https://buildbot.python.org/all/#/changes/4460 (edit: previous run was on a bogus changeset) |
I also ran @vstinner 's regression test and it seems to work fine here:
|
When a Thread is not joined after it has stopped, its lock may remain in the _shutdown_locks set until interpreter shutdown. If many threads are created this way, the _shutdown_locks set could therefore grow endlessly. To avoid such a situation, purge expired locks each time a new one is added or removed.
dc79edc
to
16edc6a
Compare
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.
LGTM, thanks.
# does not leak. Test written for reference leak checks. | ||
def noop(): pass | ||
with threading_helper.wait_threads_exit(): | ||
threading.Thread(target=noop).start() |
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.
Would you mind to add a test case that the target function isn't just only the pass
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.
Hmm, what do you mean exactly? I'm not sure I understand your suggestion.
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.
Oh, sorry. I didn't explain it clearly. I suggest that the target of Thread could be more complex ;)
Something like:
def noop():
# we don't what code will run in the thread in fact.
time.sleep(random.random())
with threading_helper.wait_threads_exit():
threading.Thread(target=noop).start()
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.
But what would that change? What matters is what happens after the thread ends.
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.
Hm, give me a second.
No matter how complex of the target of thread, the thread will finish in time in python level. So we don't adding a complex testcase in here is fine. right?
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.
Exactly.
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.
LGTM
Thanks for the fix, @pitrou !
Thanks for checking with the buildbots! Seems that the failures are unrelated to this, so I think we are good to go |
Ok, merging then. |
Thanks @pitrou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.10, 3.8, 3.9. |
Sorry, @pitrou, I could not cleanly backport this to |
GH-26138 is a backport of this pull request to the 3.10 branch. |
Sorry @pitrou, I had trouble checking out the |
…6103) When a Thread is not joined after it has stopped, its lock may remain in the _shutdown_locks set until interpreter shutdown. If many threads are created this way, the _shutdown_locks set could therefore grow endlessly. To avoid such a situation, purge expired locks each time a new one is added or removed. (cherry picked from commit c10c2ec) Co-authored-by: Antoine Pitrou <antoine@python.org>
Thanks for the fix @pitrou, it looks simple :-) |
…onGH-26103) When a Thread is not joined after it has stopped, its lock may remain in the _shutdown_locks set until interpreter shutdown. If many threads are created this way, the _shutdown_locks set could therefore grow endlessly. To avoid such a situation, purge expired locks each time a new one is added or removed.. (cherry picked from commit c10c2ec) Co-authored-by: Antoine Pitrou <antoine@python.org>
…26103) (GH-26138) When a Thread is not joined after it has stopped, its lock may remain in the _shutdown_locks set until interpreter shutdown. If many threads are created this way, the _shutdown_locks set could therefore grow endlessly. To avoid such a situation, purge expired locks each time a new one is added or removed. (cherry picked from commit c10c2ec) Co-authored-by: Antoine Pitrou <antoine@python.org>
…6103) (GH-26142) When a Thread is not joined after it has stopped, its lock may remain in the _shutdown_locks set until interpreter shutdown. If many threads are created this way, the _shutdown_locks set could therefore grow endlessly. To avoid such a situation, purge expired locks each time a new one is added or removed.. (cherry picked from commit c10c2ec) Co-authored-by: Antoine Pitrou <antoine@python.org> Automerge-Triggered-By: GH:pitrou
|
Hey there! |
Note that the previous code was not thread-safe. A thread-safe version would have been: for t in list(threads): if not t.is_alive(): # A set is used for `threads`, because `set.discard()` is O(1) but `list.remove()` is O(n). threads.discard(t) # Python 3.8 and below has a memory leak. python/cpython#26103 t.join()
…thon/cpython#26103" This reverts commit cd4a74a.
When a Thread is not joined after it has stopped, its lock may remain in the _shutdown_locks set until interpreter shutdown. If many threads are created this way, the _shutdown_locks set could therefore grow endlessly. To avoid such a situation, purge expired locks each time a new one is added or removed.
https://bugs.python.org/issue37788
Automerge-Triggered-By: GH:pitrou