Repository navigation
Open
Labels
A-destructorsArea: Destructors (`Drop`, …)Area: Destructors (`Drop`, …)A-thread-localsArea: Thread local storage (TLS)Area: Thread local storage (TLS)C-bugCategory: This is a bug.Category: This is a bug.E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.Call for participation: An issue has been fixed and does not reproduce, but no test has been added.T-libsRelevant to the library team, which will review and decide on the PR/issue.Relevant to the library team, which will review and decide on the PR/issue.
Description
There are some platforms where TLS destructors are run when the main thread exits, and there are some platforms where this does not happen,~~ and there are some platforms where things just go crazy ~~. For example, testing this program:
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
println!("wut");
}
}
thread_local!(static FOO: Foo = Foo);
fn main() {
FOO.with(|_| {});
}This should print Foo dtor, but prints nothing on some targets:
- musl (e.g. i686-unknown-linux-musl, x86_64-unknown-linux-musl)
- android (e.g. arm-linux-androideabi)
- Linux with glibc before 2.18
- Maybe more?
Here's a more complicated testcase that also involves initializing a destructor while destructors are being run; ideally this will be added to the test suite at some point:
struct Bar;
impl Drop for Bar {
fn drop(&mut self) {
println!("Bar dtor");
}
}
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
println!("Foo dtor");
// We initialize another thread-local inside the dtor, which is an interesting corner case.
thread_local!(static BAR: Bar = Bar);
BAR.with(|_| {});
}
}
thread_local!(static FOO: Foo = Foo);
fn main() {
FOO.with(|_| {});
}some older notes
- Linux ELF TLS - appears to work
- Linux pthread destructors - appear to not work (valgrind shows memory leak for a simple print statement #19776)
- OSX -
appears to call destructors, but the program above specifically causes some form of memory corrupting, triggering an assert in mallocfixed by OSX: fix #57534 registering thread dtors while running thread dtors #57655 - Windows GNU/MSVC - appears to
notwork.We're listening forDLL_PROCESS_DETACHbut for some reason we're not getting that notification.
Metadata
Metadata
Assignees
Labels
A-destructorsArea: Destructors (`Drop`, …)Area: Destructors (`Drop`, …)A-thread-localsArea: Thread local storage (TLS)Area: Thread local storage (TLS)C-bugCategory: This is a bug.Category: This is a bug.E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.Call for participation: An issue has been fixed and does not reproduce, but no test has been added.T-libsRelevant to the library team, which will review and decide on the PR/issue.Relevant to the library team, which will review and decide on the PR/issue.
Activity
ranma42 commentedon Sep 17, 2015
The memory corruption in MacOSX seems to be related to the TLS usage during
Dropof a TLS value.If the
print!invocation is replaced withstdout().write, no crash occurs.However, if
stdoutis used withinmain, the process dies with the errorboth if
dropcallsprint!and if it callswrite.Auto merge of #28585 - ranma42:simpler-panic, r=alexcrichton
Auto merge of #28631 - ranma42:robust-panic, r=alexcrichton
ranma42 commentedon Sep 29, 2015
pthread destructors seem to have the same behaviour on MacOS X as on Linux (i.e. they are not run from when the main thread terminates). Googling around seems to suggest that this is a known (expected?) fact. Specifically, TLS destructors are only run when
pthread_exitis invoked, which for non-main threads happens implicitly.The only authoritative source I was able to find is http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html
alexcrichton commentedon Sep 29, 2015
Yeah I think solving this in the case of pthreads will either require us documenting "dtors may not run" or perhaps adding our own atexit handler or something like that to handle this (e.g. some custom code on our end). Not entirely sure what that would look like though!
ranma42 commentedon Sep 29, 2015
In order to make the
atexithandler practical we would need to keep explicitly track the active destructors (at least on the main thread), i.e. basically keep the list ofDTORSas in the pthread fallback implementation. This might not be as bad as it looks; in fact it might allow us to reuse some code (currently the Linux fallback dtor and the normal Windows dtor are basically doing the same thing in a slightly different way).Another option would be to start the
mainfunction in a new thread and immediately joining it inlang_start. This would be about as effective as hooking the destructors in the Rustatexit, because for Rust application either way would work just fine and for Rust libraries used by non-Rust applications TLS would not be destroyed in either case.The C
atexitfunction would suffer from the same limitations as the Rustatexit(need to track dtors), but it might help in non-Rust applications, assuming that go through the normal termination (exit, not_Exitnorquick_exitnor an abort of any kind) as expected by the C runtime.Documenting "dtors may not run" looks like a reasonable compromise to me, especially if we can guarantee that this will at most happen for the main thread (and, ideally, only on non-Rust apps). I believe that this guarantee is especially important if threads are spawned and joined (instead of managed as a thread pool) to ensure that leaks are bounded.
alexcrichton commentedon Sep 29, 2015
Yeah this probably isn't so bad as we already do it in some cases, so it'd just be a matter of shuffling things around.
Unfortunately I think this won't work because there's a number of GUI frameworks (or something like that) which only work on the main thread (I think on Windows in particular), so running off-the-main-thread by default may be a bit of a heavy hammer to fix this!
I agree it's probably not that bad, but if we could get OSX and Windows working reliably, it's more pressure for us to get pthreads working reliably :). I feel like Windows is pretty easy to fix, it seems like some small error is just being missed there. OSX also feels the same way to me in terms of weird things happening. The pthreads case also isn't super critical on Linux because it's only used on older linuxes.
Overall I think we may still have enough rope left to climb out and close this issue, so I wouldn't be quite willing just yet to close it out by documenting things may not run.
retep998 commentedon Mar 1, 2017
Windows itself does not actually care which thread you do the UI on, as long as you're consistent. Once you create a window on a given thread, that thread now has a message queue which you're obligated to pump forever.
mtak- commentedon Jan 12, 2019
Just confirmed the memory corruption in the OP on OSX occurs for the same reason listed here #57534 (comment). It is not specific to the main thread. AFAICT it is UB on macos to register destructors during thread cleanup via
_tlv_atexit,Rollup merge of rust-lang#57655 - mtak-:fix-tls-dtors-macos, r=alexcr…
23 remaining items
RalfJung commentedon Jun 23, 2024
Turns out musl is still broken, I opened a new issue for that: #126858.
Auto merge of rust-lang#126829 - RalfJung:main-thread-tls, r=workingj…
Auto merge of rust-lang#126829 - RalfJung:main-thread-tls, r=workingj…
Auto merge of rust-lang#126829 - RalfJung:main-thread-tls, r=workingj…
RalfJung commentedon Jul 6, 2024
I gave up on the PR since even some glibc targets are effected (including the one we test on CI), so now we have two issues tracking the same thing -- this and #126858.
EDIT: I have closed the other issue.
[-]TLS destructors on the main thread are a little sketchy[/-][+]TLS destructors on the main thread are not always run[/+]Fix panic when using sync client from a GCC destructor
surban commentedon Dec 3, 2024
Testing on FreeBSD 14 shows that TLS destructors are not run for the main thread.
Fix panic when using sync client from a GCC destructor
Fix panic when using sync client from a GCC destructor
Fix panic when using sync client from a GCC destructor
Fix panic when using sync client from a GCC destructor
Fix panic when using sync client from a GCC destructor