C++ Logo

std-discussion

Advanced search

Clarification needed: block scope thread_locals can be referenced for the first time without control passing through declaration

From: Princeton Ferro <pferro_at_[hidden]>
Date: Mon, 19 Aug 2019 16:22:03 +0000
The standard (C++17) says that variables with thread-storage duration must be initialized before their first ODR use:
[basic.std.thread - 6.7.2, pp 2]
A variable with thread storage duration shall be initialized before its first odr-use (6.2) and, if constructed,
shall be destroyed on thread exit.

Later, the standard says that for thread-local variables with block scope, they must be initialized when control first passes through their declaration:
[stmt.dcl - 9.7, pp 4]
Dynamic initialization of a block-scope variable with static storage duration (6.7.1) or thread storage
duration (6.7.2) is performed the first time control passes through its declaration

Now consider the following example:


#include <thread>
#include <iostream>

struct Object {
    int i;
    Object() : i(3) {}
};

int main(void) {
    static thread_local Object o;

    std::cout << "[main] o.i = " << o.i << std::endl;
    std::thread t([] { std::cout << "[new thread] o.i = " << o.i << std::endl; });
    t.join();
}

o is a block-scope variable with thread storage and a dynamic initializer. The lambda passed into std::thread's constructor refers to o but does not capture it, which should be fine since o is not a variable with automatic storage. However, when control passes through the lambda, it will do so on a new thread. When that happens, it will refer to o for the first time. Because o is a thread-local variable, (6.7.2) implies it should be initialized, but because it is declared in block scope, (9.7) implies that it can only be initialized when control passes through its declaration, which will never happen on the new thread.

What should be done? A few options I can think of:

  1. Because it has thread storage, the variable must be initialized by the time it is first used on any thread. Essentially, the treatment should be the same as if o was declared outside the body of main().
  2. It is an error to refer to a block-scope thread-local variable within a lambda that escapes the block it was declared in.

Received on 2019-08-19 11:24:07