29

Consider the following example (lock guards on cout omitted for simplicity).

#include <future>
#include <iostream>
#include <thread>

using namespace std;

struct C
{
  C() { cout << "C constructor\n";}
  ~C() { cout << "C destructor\n";}
};

thread_local C foo;

int main()
{
   int select;
   cin >> select;
   future<void> f[10];
   for ( int i = 0;i < 10; ++i)
       f[i] = async( launch::async,[&](){ if (select) foo; } );
   return 0;
}

On both clang and gcc, this program outputs nothing if the user writes '0', while it prints Constructor/Destructor 10 times if the user inputs a non zero number. Additionally clang complains about an obvious non used expression result.

Since a thread_local storage life-time is supposed to span the entire thread's life, I expected the foo variable to be initialized in every thread regardless of the user input.

I might want to have a thread-local variable for the sole purpose of having a side-effect in the constructor, does the standard mandates that a thread_local object is initialized on its first use?

sbabbi
  • 11,070
  • 2
  • 29
  • 57

1 Answers1

24

The standard allows for this behavior, although it doesn't guarantee it. From 3.7.2/2 [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.

It's also possible that the objects are constructed at some other time (e.g. on program startup), as "before first use" means "at any point as long as it is before" rather than does "just before".

Jon
  • 428,835
  • 81
  • 738
  • 806
  • Thank you, my `CTRL+F` skills are quite limited :) – sbabbi Jun 16 '14 at 23:41
  • 14
    This is somewhat misleading; the standard says that a variable with thread-local storage duration must be initialized sometime before it is odr-used, but it doesn't mandate exactly **when** it will be initialized, and an implementation is even permitted to initialize such variable even if the variable is never odr-used. As stated: it can be initialized at the **latest** when the variable is odr-used, but such initialization can happen at any time before that. – Filip Roséen - refp Jun 16 '14 at 23:43
  • @FilipRoséen-refp: I didn't quite realize yesterday that the answer read like that if taken literally, but you are right on both counts. I have edited to make it clear that "before first use" is meant in the mathematical sense. Thank you for the feedback. – Jon Jun 17 '14 at 08:00
  • 1
    @shuva nothing would change. – Jon Apr 15 '20 at 16:15
  • Hmmm... It would seem that the definition of the global "thread_local C foo;" constitutes ODR-use. This is opposed to, say, a thread_local static that is local to a function which would only be ODR-use'd if that function were entered. I have found that MSVC creates these thread_local globals without them being used, whereas clang/gcc only creates them if they are used. – David Bien Mar 02 '21 at 02:48
  • Check out the section on "static local variables" declared thread_local in this: https://en.cppreference.com/w/cpp/language/storage_duration. It would seem strange that these locals are called out specially as being "static or thread (since C++11) storage duration but are initialized the first time control passes through their declaration" - i.e. even if that static thread_local variable *isn't used* within that method they will still be created - they are still odr-used (I guess). Which would then make me think that MSVC is doing this correctly and gcc/clang is doing it wrong. – David Bien Mar 02 '21 at 03:03
  • @DavidBien The section "static local variables" specifically refer to variables declared at **block scope**. (Recall that if `thread_local` is the only storage class specifier applied to a block scope variable, `static` is implied, so you are right there). However, the `foo` from the question is declared in the global (global namespace) scope, and static isn't implied. – kwsp Aug 21 '22 at 15:31