-
-
Notifications
You must be signed in to change notification settings - Fork 33.3k
gh-140798: Fix memory leak with threading.local when finalizer resurrects threading_local_key
#140836
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?
gh-140798: Fix memory leak with threading.local when finalizer resurrects threading_local_key
#140836
Conversation
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.
Tracer handles Python calls. If a thread's locals contain an object with a Python callback (e.g., a __del__ finalizer), then tracer will handle the call.
When our thread is finalizing, the locals are cleared, as are all objects within them. In this case, the finalizer of our object's __del__ will be called and handled by the tracer. This leads to the recreation of thread locals (threading_local_key and threading_local_sentinel), which leak because we mistakenly believe that the locals were cleared and will not be recreated.
In this PR, we clear the locals after clearing the tracers, so any callbacks from finalizers will not be handled, and the locals cannot be recreated.
|
Thanks for the review, Sergey! But I have to say that I've discovered one even more difficult case without tracing at all: import _thread
import threading
local = _thread._local()
class ClassWithDel:
def __del__(self):
getattr(local, 'c', None)
def thread_func():
local.c = ClassWithDel()
t = threading.Thread(target=thread_func)
t.start()
t.join()There's a leak with ASAN since finalizer of CC @mpage @colesbury @ambv as author and reviewers of #121655. Btw, we can merge this as is and create another issue for this case if this will be clearer. |
|
This does not look like the right approach to me. I think you will end up playing whac-a-mole where each time you "fix" one leak you will introduce other bugs. You need a robust approach to dealing with finalizers like the GC has. |
|
Thanks for the comment, @colesbury . And I'll be grateful for some piece of advice, if possible. |
|
I think you just haven't yet found the new bugs that this PR introduces. The original bug report was only found via fuzzing. I suspect that if you keep fuzzing you will find different ones with this PR. The problems is that we have code in the form: The underlying problem is that when you clear For example, if you move |
|
In general, I agree with you, swapping clear function calls isn't good way of fixing anything. |
It seems that we should find new solution.
threading.local when tracing is activethreading.local when finalizer resurrects thread_local_key
threading.local when finalizer resurrects thread_local_keythreading.local when finalizer resurrects threading_local_key
threading.localwhen tracing is active #140798