-
Notifications
You must be signed in to change notification settings - Fork 356
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
Data reachable from thread-local storage of the main thread should not be considered leaked #2881
Comments
Yes, that is by design. Threads come and go and so having a reference pointed-to by a thread local after the thread disappeared and all its thread-local storage got cleaned up is a leak. |
In Rust, And yeah as Ralf points out thread-locals aren't leaked. So I'm not sure, which side of this comparison do you think is confusing? |
Ah ok that makes sense. However, Valgrind doesn't detect a leak in the thread_local case. Is there a reason for this disparity between Valgrind and miri? |
I have no idea how valgrind decides what to make a leak or not. 🤷 Does it consider this a leak? IMO it clearly should: use std::cell::Cell;
pub fn main() {
thread_local! {
static REF: Cell<Option<&'static i32>> = Cell::new(None);
}
std::thread::spawn(|| REF.with(|cell| {
let a = 123;
let b = Box::new(a);
let r = Box::leak(b);
cell.set(Some(r));
})).join().unwrap();
// Imagine the program running for a long time while the thread is gone
// and this memory still sits around, unused -- leaked.
} |
I just tried it and yes it is considered a leak by both Valgrind and Miri. Also in my original example, Valgrind marks the memory as "still reachable". |
Okay so looks like valgrind differentiates main thread TLS leaks and other TLS leaks. I guess this makes sense since main thread TLS will always stick around until the end of the program. We could in principle do the same in Miri, I think.
|
This should actually be fairly easy to do: before running the leak check (around here), mark the main thread (thread ID 0) |
thread_local!
considered a leak whereas static
is not
I tried this approach and am running into some issues:
Any ideas? My attempt is here |
Maybe you could try sticking something deeper into the scheduler code? What you probably want to do is hook into the transition from running the main thread to
Based on the logging you added, you're adding something but it's the wrong thing.
That possible. I traced the AllocId that your logs say are added as a static root ( So yeah I wouldn't be surprised if there is some extra layer of indirection that gets torn down by TLS destructors. So there are two possible fixes for this, which both may or may not work:
I feel like the first one has lower chance of going wrong somehow? |
That doesn't sound right. Static roots already apply recursively, everything reachable from then is not considered leaked. There are 2 kinds of thread-local state (thread-local statics and the OS-provided "TLS keys"), and they both get cleaned up on thread exit: Line 274 in 538479b
Line 246 in 538479b
Marking the main thread data as static roots needs to happen before that cleanup. |
Thanks, I was missing the former. Added those as static roots before they're destroyed and verified that the
but still no luck in having the leak check acknowledge the box as reachable:
|
Could you open a PR? That makes it easier to review and track the changes you do. |
Treat thread-local statics on main thread as static roots for leakage analysis Miri currently treats allocations as leaked if they're only referenced in thread-local statics. For threads other than the main thread, this is correct, since the thread can terminate before the program does, but references in the main thread's locals should be treated as living for the duration of the program since the thread lives for the duration of the program. This PR adds thread-local statics and TLS keys as "static roots" for leakage analysis, but does not yet bless the example program from rust-lang#2881. See rust-lang/miri#2881 (comment) Closes rust-lang#2881
Miri does not detect any leak for the following code since the reference is stored in a
static
variable and is therefore accessible for the whole duration of the program.However, replacing the
static
with athread_local!
, Miri complains about a memory leak:The text was updated successfully, but these errors were encountered: