Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

initialization order of thread_local vs. global variables

C.h:

#include <iostream>

class C {
public:
  explicit C(int id) { std::cout<<"Initialized "<<id<<"\n"; }
};

1.cpp:

#include "C.h"

C global(1);

2.cpp:

#include "C.h"

thread_local C thread(2);

int main() {}

My question is: Is it guaranteed that global will be initialized before thread?

The C++ standard is somewhat vague on this point, as far as I understand it. It says (from the C++17 n4659 draft):

[basic.start.static] Static initialization

Variables with static storage duration are initialized as a consequence of program initiation. Variables with thread storage duration are initialized as a consequence of thread execution.

It stands to reason that "program initiation" happen before "thread execution", but since both those expressions appear in the standard only in that place, I'm seeking advise from actual language lawyers.

like image 663
Shachar Shemesh Avatar asked Mar 23 '20 12:03

Shachar Shemesh


1 Answers

I'm going to use the C++20 working draft since the wording there is a little cleaner, although none of the real rules have changed.

First, thread_local behaves basically like static as far as non-local goes: [basic.stc.thread]/2:

[ Note: A variable with thread storage duration is initialized as specified in [basic.start.static], [basic.start.dynamic], and [stmt.dcl] and, if constructed, is destroyed on thread exit ([basic.start.term]). — end note ]

Yes, it's a note. But a non-local object declared thread_local is basically static so this makes sense.

Now, neither global nor thread have constant initialization - so both are zero initialized and then they have to undergo dynamic initialization. To [basic.start.dynamic]!

Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, is partially-ordered if the variable is an inline variable that is not an implicitly or explicitly instantiated specialization, and otherwise is ordered.

Neither of our variables are specializations, neither of them are inline. So both are ordered.

A declaration D is appearance-ordered before a declaration E if

  • D appears in the same translation unit as E, or
  • the translation unit containing E has an interface dependency on the translation unit containing D,

in either case prior to E.

Our declarations are not appearance-ordered with respect to each other.

Dynamic initialization of non-local variables V and W with static storage duration are ordered as follows:

Alright, sub-bullet 1:

If V and W have ordered initialization and the definition of V is appearance-ordered before the definition of W, or if V has partially-ordered initialization, W does not have unordered initialization, and for every definition E of W there exists a definition D of V such that D is appearance-ordered before E,

Doesn't apply. It's a complicated condition, but it doesn't apply.

Otherwise, if the program starts a thread other than the main thread before either V or W is initialized, it is unspecified in which threads the initializations of V and W occur; the initializations are unsequenced if they occur in the same thread.

Nope, no threads.

Otherwise, the initializations of V and W are indeterminately sequenced.

There we go. global and thread are indeterminately sequenced.


Note also that:

It is implementation-defined whether the dynamic initialization of a non-local inline variable with static storage duration is sequenced before the first statement of main or is deferred.

and:

It is implementation-defined whether the dynamic initialization of a non-local non-inline variable with thread storage duration is sequenced before the first statement of the initial function of a thread or is deferred.

like image 161
Barry Avatar answered Oct 05 '22 15:10

Barry