https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61991
Bug ID: 61991 Summary: Destructors not always called for statically initialized thread_local objects Product: gcc Version: 4.9.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: scovich at gmail dot com If a thread_local object is statically initialized---trivial or constexpr constructor---but has a non-trivial destructor, the destructor will only run if some *other* thread_local object needs dynamic initialization *and* if at least one such object is accessed by the thread during its lifetime. Accessing members of the object itself does nothing, because it is statically initialized. Example: #include <cstdio> static thread_local struct X { ~X() { printf("bye!\n"); } } x; static thread_local int y = printf("initialized y\n"); int main() { //printf("%d\n", y); } Compiling the above with `g++ -std=gnu++11 bug.cpp' gives an executable that produces no output when run. Uncomment the printf in main() and recompile, and the resulting executable prints: initialized y 14 bye! The only hint of trouble at compile time is that the compiler may warn about an unused variable. However, that warning only comes if the offending object is never accessed otherwise (perhaps because it is an exit guard of some type), has static storage class, *and* no other dynamic thread_local storage exists... an unlikely combination. Looking at the assembly code output, the problem is obvious: X::~X is only registered with __cxa_thread_exit if __tls_init is called, and the latter is only called if the thread accesses a TLS object that needs dynamic initialization. Under Cygwin, I hit the further problem that __tls_init doesn't even contain the any calls to __cxa_thread_exit. That's probably a separate bug, though, and I don't know whose problem it might be. If there's no easy fix, might I suggest a loud warning somewhere in the docs might be appropriate so people have a way to know about the limitation? I tried searching for this online, but Google didn't turn anything up. (Discovered in 4.8.3, still there in 4.9.0, and given the nature of the bug I suspect it's in more recent versions, too).