This already happens if the initialization is dynamic. The standard requires that variables with "thread storage duration" and dynamic initialization be initialized sometime between the start of the thread and the 'first odr-use'. However, since you generally can't control exactly when that initialization will occur (other than sometime after the thread object is created and sometime before the thread ends - assuming the thread local variable actually gets used by the thread) the problem is that the thread local variable might get initialized with a value that your main thread sets after the thread is created.
For a concrete example, consider:
#include <stdio.h>
#include <chrono>
#include <functional>
#include <thread>
#include <string>
using std::string;
enum class trace_level { none, error, warning, log, debug, verbose };
trace_level log_level = trace_level::log;
static thread_local trace_level min_level = log_level;
void f(string const& s)
{
printf("%s, min_level == %d\n", s.c_str(), (int) min_level);
}
int main()
{
std::thread t1{std::bind(f,"thread 1")};
//TODO: std::this_thread::sleep_for(std::chrono::milliseconds(50));
log_level = trace_level::verbose;
std::thread t2{std::bind(f,"thread 2")};
t1.join();
t2.join();
}
With the sleep_for()
call commented out as above, I get the following output (usually):
C:\so-test>test
thread 1, min_level == 5
thread 2, min_level == 5
However, with the sleep_for()
uncommented, I get (again - usually):
C:\so-test>test
thread 1, min_level == 3
thread 2, min_level == 5
So as long as you're willing to live with a bit of uncertainty regarding which logging level a thread will get if the level gets changed in the main thread soon after the thread starts, you can probably just do what you're looking to do pretty naturally.
There's one remaining caveat - data races. The code above has a data race on the log_level
variable, so it actually has undefined behavior. The fix for that is to make the variable either an atomic type or wrap it in a class that uses a mutex to protect updates and reads from data races. So change the declaration of the global log_level
to:
std::atomic<trace_level> log_level(trace_level::log);
Standards citations:
3.6.2 Initialization of non-local variables [basic.start.init]
... Non-local variables with thread storage duration are initialized as a consequence of thread execution. ...
and
3.7.2/2 Thread storage duration [basic.stc.thread]
A variable with thread storage duration shall be initialized before its first odr-use (3.2) and, if constructed, shall be destroyed on thread exit.