Are template variables thread safe? they're placed on data segment?

288 Views Asked by At

I'm playing with the new template variables feature from C++14 in order to get used to it (maybe is soon to do this 'cause it seems that some compilers didn't implement it completely).

Now I'm wondering where lies each instance of a template variable. In the tests I did so far they seem to be initialized before any static data so I'm wondering if they're placed in the data segment of the program. Let's see what I've tryed so far, I have a class which prints information on construction and destruction:

struct squealer
{
    squealer(std::string a_name) : m_name(a_name) { std::cout << this << ' ' << m_name << ' ' << __PRETTY_FUNCTION__ << '\n'; }
    ~squealer() { std::cout << this << ' ' << m_name << ' ' << __PRETTY_FUNCTION__ << '\n'; }
    void f() {}
    const std::string m_name;
};

And a program which instances some squealers in local storage, static storage and as template variables, this is the program:

// static storage squealer
squealer s("\"static\"");

// template variable squealer
template <int i> squealer test(std::string(i, 'A'));

// function using a template variable squealer
void f() { test<1>.f(); }

int main(int argc, char **argv)
{
    // local storage squealer
    squealer ss("local");

    // using another template variable squealers
    test<2>.f();
    switch (argc)
    {
        case 1: test<3>.f(); break;
        case 2: test<4>.f(); break;
        case 3: test<5>.f(); break;
        case 4: test<6>.f(); break;
    }

    return 0;
}

Here is the program and this is the output:

A squealer::squealer(std::string)
AA squealer::squealer(std::string)
AAA squealer::squealer(std::string)
AAAA squealer::squealer(std::string)
AAAAA squealer::squealer(std::string)
AAAAAA squealer::squealer(std::string)
"static" squealer::squealer(std::string)
local squealer::squealer(std::string)
local squealer::~squealer()
"static" squealer::~squealer()
AAAAAA squealer::~squealer()
AAAAA squealer::~squealer()
AAAA squealer::~squealer()
AAA squealer::~squealer()
AA squealer::~squealer()
A squealer::~squealer()

As we can see, all the template variables squealer instances are created before the one named "static" and at the end (as expected) the one named local is created, the destruction order is the opposite (as expected too), so: the order of creation/initialization of template variables instances is the same of its appearance on the code regardless of the locality of this appearance and regardless of they're used or not (the f() function is never called).

So the first question is, are this template variables placed on the data segment? I don't know how to test or check it.

The second question is, are all of this template variables squealer instances thread safe? I've readed on n3376 §6.7 the following sentence (emphasis mine):

An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope (3.6.2). Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

From C++11 if all of the template variable squealer instances are in static storage they should be thread safe, isn't it?

Thanks.

2

There are 2 best solutions below

2
On BEST ANSWER

The section of the standard you quote describes block-scope variables with static storage duration, e.g:

int foo() {
  static int bar = 42;
  return bar;
}

of which your program has none. All of your variables with static storage duration are declared at namespace scope, so you need to be looking at [basic.start.init] (3.6.2). Specifically paragraph two which states:

Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place.

...

Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, and otherwise is ordered [ Note: an explicitly specialized static data member or variable template specialization has ordered initialization. —end note ] 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.

In the question's program, all the squealer instances with static storage duration must be dynamically initialized since squealer has a member std::string that cannot be constant-initialized. ::s has ordered initialization, and all of the instances of test have unordered initialization since each is an "implicitly or explicitly instantiated specialization" of the template test. The test instances are guaranteed to be initialized before entering main, but otherwise all bets are off: they may be initialized in any order, possibly before and/or after initialization of ::s and more importantly std::cout. Those initializations are notably not thread-safe: "If a program starts a thread, the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization."

4
On

Variable template specializations are static variables as stated by [temp.inst]/12:

Implicitly instantiated class, function, and variable template specializations are placed in the namespace where the template is defined.

Therefore, usual static initialization rules apply, which means that all specializations are initialized before main() executes.