6

For example, the node::node() constructor in the following snippet accesses the globals node::count and ::tail without any multithread guards. Does the C++ standard guarantee that the output would always be a permutation of 0 1 2 (regardless of the order)?

#include <stdio.h>

struct node *tail;

struct node
{
    static int count;

    int index;
    node *prev;

    node()
    {   index = count++; prev = tail; tail = this; }
};

int node::count;

node one, two[2];

int main(int argc, char *argv[])
{
    for(node *p = tail; p; p = p->prev)
        printf("%d\n", p->index);

    return 0;
}

I am looking for an answer based on the (applicable) standard, not for implementation or compiler specific behaviors. There are a number of related questions on SO but it's not entirely clear how they directly apply to this particular and rather basic case (Is C++ static member variable initialization thread-safe?, Is local static variable initialization thread-safe in C++11? etc).

Community
  • 1
  • 1
dxiv
  • 16,984
  • 2
  • 27
  • 49
  • 1
    Which C++ standard? Did you check this http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm Dynamic Initialization and Destruction with Concurrency, Non-Local Initialization and [basic.start.init] of 14 - http://cpp14.centaur.ath.cx/basic.start.init.html – osgx Jul 06 '16 at 03:10
  • http://stackoverflow.com/questions/211237/static-variables-initialisation-order would be helpful! – Yogi Joshi Jul 06 '16 at 03:12
  • @osgx `Which C++ standard?` I would be interested to find out if the answer changed between the standards. Thank you for the pointers. – dxiv Jul 06 '16 at 03:21
  • @YogiJoshi Thanks, but that's about the *order* of initialization, while I specifically asked about the thread-safety of the initializers (per the `regardless of the order` part of my question). – dxiv Jul 06 '16 at 03:24

1 Answers1

5

Initialization of global variables is guaranteed single-threaded as long as the program doesn't itself start a thread (e.g. in a constructor of some global variable); once that happens, the implementation is then allowed to parallellize remaining initializations, to some extent.

[basic.start.init]/2 ...Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit. If a program starts a thread (30.3), the subsequent initialization of a variable is unsequenced with respect to the initialization of a variable defined in a different translation unit. Otherwise, the initialization of a variable is indeterminately sequenced with respect to the initialization of a variable defined in a different translation unit. If a program starts a thread, the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization. Otherwise, the unordered initialization of a variable is indeterminately sequenced with respect to every other dynamic initialization. [ Note: This definition permits initialization of a sequence of ordered variables concurrently with another sequence. —end note ]

"Indeterminately sequenced" is the part that guarantees single-threaded execution. By definition, the notion of sequenced {before, after, indeterminately} is only meaningful within a single thread:

[intro.execution]/13 Sequenced before is a ... relation between evaluations executed by a single thread...

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • @SamVarshavchik It is meaningless to say that an evaluation on one thread is "sequenced" with an evaluation on a different thread. One could happen before/after the other, but it cannot be sequenced before/after/indeterminately with the other. "**[intro.execution]/13** *Sequenced before* is a ... relation between evaluations executed by a single thread..." – Igor Tandetnik Jul 06 '16 at 03:18
  • The `within a single translation` part is only incidental to the MCVE I posted, of course. And the language is rather confusing. `Otherwise, the initialization of a variable is indeterminately sequenced with respect to the initialization of a variable defined in a different translation unit.` I take this to mean that, assuming no other threads were deliberately launched, even if I moved `node two[2];` to a different .cpp, it would still be `sequenced` with respect to `node one;` - only `indeterminately` so (i.e. order not guaranteed), as opposed to `unsequenced` (i.e. potentially multithread). – dxiv Jul 06 '16 at 03:37