简体   繁体   中英

initialization order of thread_local vs. global variables

Ch:

#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.

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.

There is no guarantee and there cannot be any form of guarantee of it - at least currently.

Imagine following case, you have another unrelated static global variable Z that uses your thread_local variable during initialization or, say, even creates another thread and uses it - all during its initialization.

Now it just happens that this static global variable Z gets initialized prior to your static global variable global . This implies that the thread_local variable had to be initialized prior to your static global variable.

Note: currently, there is no way to guarantee in which order static global variables are being initialized - a known issue of C++. So if you use one global variable inside another it might or might not lead to an error - technically a UB. Don't think that it affects thread_local variables in any way as their initialization mechanism tends to be very different.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM