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).

Reply via email to