This program:
#include <iostream>
struct Foo {
Foo() {
std::cout << "Foo()\n";
}
~Foo() {
std::cout << "~Foo()\n";
}
};
struct Bar {
Bar() {
std::cout << "Bar()\n";
}
~Bar() {
std::cout << "~Bar()\n";
thread_local Foo foo;
}
};
Bar bar;
int main() {
return 0;
}
Prints
Bar()
~Bar()
Foo()
for me (GCC 6.1, Linux, x86-64). ~Foo() is never called. Is that the expected behaviour?
thread_local is used to mark a variable with thread storage duration, which means it is created when the thread starts and cleaned up when the thread ends. Note: For C++, C++11 or later is required to use the thread_local keyword.
In C++, thread_local is defined as a specifier to define the thread-local data and this data is created when the thread is created and destroyed when the thread is also destroyed, hence this thread-local data is known as thread-local storage.
If thread_local is the only storage class specifier applied to a block scope variable, static is also implied.
The Standard does not cover this case; the strictest reading would be that it is legal to initialize a thread_local
in the destructor of an object with static storage duration, but it is illegal to allow the program to continue to normal completion.
The problem arises in [basic.start.term]:
1 - Destructors ([class.dtor]) for initialized objects (that is, objects whose lifetime ([basic.life]) has begun) with static storage duration are called as a result of returning from main and as a result of calling std::exit ([support.start.term]). Destructors for initialized objects with thread storage duration within a given thread are called as a result of returning from the initial function of that thread and as a result of that thread calling std::exit. The completions of the destructors for all initialized objects with thread storage duration within that thread are sequenced before the initiation of the destructors of any object with static storage duration. [...]
So the completion of bar::~Bar::foo::~Foo
is sequenced before the initiation of bar::~Bar
, which is a contradiction.
The only get-out could be to argue that [basic.start.term]/1 only applies to objects whose lifetime has begun at the point of program/thread termination, but contra [stmt.dcl] has:
5 - The destructor for a block-scope object with static or thread storage duration will be executed if and only if it was constructed. [ Note: [basic.start.term] describes the order in which block-scope objects with static and thread storage duration are destroyed. — end note ]
This is clearly intended to apply only to normal thread and program termination, by return from main or from a thread function, or by calling std::exit
.
Also, [basic.stc.thread] has:
A variable with thread storage duration shall be initialized before its first odr-use ([basic.def.odr]) and, if constructed, shall be destroyed on thread exit.
The "shall" here is an instruction to the implementor, not to the user.
Note that there is nothing wrong with beginning the lifetime of the destructor-scoped thread_local
, since [basic.start.term]/2 does not apply (it is not previously destroyed). That is why I believe that undefined behavior occurs when you allow the program to continue to normal completion.
Similar questions have been asked before, though about static vs. static storage duration rather than thread_local
vs. static; Destruction of objects with static storage duration (and https://groups.google.com/forum/#!topic/comp.std.c++/Tunyu2IJ6w0), and Destructor of a static object constructed within the destructor of another static object. I'm inclined to agree with James Kanze on the latter question that [defns.undefined] applies here, and the behavior is undefined because the Standard does not define it. The best way forward would be for someone with standing to open a defect report (covering all the combinations of static
s and thread_local
s initialized within the destructors of static
s and thread_local
s), to hope for a definitive answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With