-
Notifications
You must be signed in to change notification settings - Fork 349
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
Support detached threads still running when the main thread exits ("thread leaks") #1371
Comments
I always found it surprising that it was fine to have threads still exist when the main thead exits. The other threads don't get shut down time at all, they are just killed. I wonder if we should at least warn about it. |
Some libraries might have "background threads" to do stuff, that does not seem unreasonable to me -- I doubt rayon's thread pool or crossbeam's epoch cleanup thread will be joined ever, and indeed I don't think there is a good way to join them. |
In other words, "detaching" a thread IMO is an explicit marker that it is entirely okay for this thread to be suddenly killed when the program is done. I am fine warning about this for non-detached threads, see item 3 above. Detaching then serves as explicit opt-out from that warning. |
Detaching is solely a library thing. Dropping a |
Nope, docs explicitly say
It is UB to join on a detached thread, and @vakaras patch detects that UB. |
oh, interesting. In that case it may be impossible to do a leak check without running those threads to completion after the main thread exits. Since they may never terminate, I'm not sure how we can achieve that though. The reason I think it's impossible is that the threads may hold integer addresses of memory that they could free, but we can't tell whether that's the case. |
That's already a problem (#1318), so I don't think it should affect our decision here. |
so... we treat everything that the thread refers to with provenance pointers the same way we treat them in statics? That seems reasonable to me, maybe with a flag to disable it specifically when ppl want to debug leaking from threads that they didn't clean up properly (e.g. when accidentally detaching them) |
We have agreement then. :)
In that case I'd just say we have a flag to warn about detached threads still sitting around when the main thread exits -- there is little point in accepting that silently, but then complaining about all its stack memory leaking. |
true! That's much better. Oh, we could even just warn when detaching, to show the user where it's happening 😄 |
Following on from jonhoo/bus#29 (comment), I believe that Rust currently does run destructors for anything in thread-local storage. It does this by using various hooks that the OS provides, such as To follow-on from your comment there Ralf, I believe that function is called when each thread exits. There is also |
I made an experiment and it confirmed what I suspected -- destructors of still-running detached threads are not run. When the process |
Oh, that's really interesting. Makes me wonder what all that logic I linked to is then doing? It sure seems like it's trying to arrange for thread-locals to be cleaned up... Who might be a good person to ping to ask about this do you think? |
All that logic is ensuring thread-locals get cleaned up when a thread exits cleanly. |
With #1858 it is at least possible to execute programs that leave behind some threads, by passing What remains (and what would actually be non-trivial work) is to support checking for memory leaks when there are remaining threads (and possibly checking for threads that are neither detached nor joined). |
also ignore 'thread leaks' with -Zmiri-ignore-leaks This is a step towards #1371. The remaining hard part would be supporting checking for memory leaks when there are threads still running. For now we elegantly avoid this problem by using the same flag to control both of these checks. :)
Unfortunately, this error can come up when using @m-ou-se I am curious, why do our scoped threads hand-roll their own implementation of |
That's to make sure the memory (and pid/tid) usage doesn't grow without bounds for long-lived scopes. For example: fn main() {
let config = &read_config();
let socket = start_listening(config);
thread::scope(|s| {
loop {
let connection = socket.accept();
s.spawn(move || handle_connection(connection, config));
}
});
} Detaching the threads means they clean up after themselves as soon as they finish. Keeping their join handle around until the end of the scope means this program would have to keep around an ever increasing list of join handles. |
This is carefully constructed to allow Miri to test most of `ndarray` without slowing down CI/CD very badly; as a result, it skips a number of slow tests. See #1446 for a list. It also excludes `blas` because Miri cannot call `cblas_gemm`, and it excludes `rayon` because it considers the still-running thread pool to be a leak. `rayon` can be re-added when rust-lang/miri#1371 is resolved.
With #1284 we can run multi-threaded programs in Miri. However, right now we error if, when the main thread exits, there are any other threads still left. We should properly support that case.
For that, we need to:
Cc @vakaras
The text was updated successfully, but these errors were encountered: