Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static local variable initialisation in multithreaded environment

Tags:

Suppose there is a function (member function possibly)

SomeType foo() {     static SomeType var = generateVar();     return var; } 

How var will be initialised if foo will be called 'for first time' from multiple threads simultaneously?

  1. Is it guaranteed that generateVar() will be called only once in any scenario (if used of course)?
  2. Is it guaranteed that foo will return the same value when called multiple times in any scenario?
  3. Is there a difference in behaviour for primitive or non-primitive types?
like image 451
Andrew Avatar asked May 24 '13 12:05

Andrew


People also ask

Can we use static variable in multithreading?

Static variable is a shared resource, which can be used to exchange some information among different threads. And we need to be careful while accessing such a shared resource. Hence, we need to make sure that the access to static variables in multi-threaded environment is synchronized. This is a correct statement.

Are local static variables thread-safe?

With C++11, static variables with block scope have an additional guarantee; they will be initialized in a thread-safe way.

Is static initialization thread-safe?

Starting in C++11, scoped static initialization is now thread-safe, but it comes with a cost: Reentrancy now invokes undefined behavior.] The rule for static variables at block scope (as opposed to static variables with global scope) is that they are initialized the first time execution reaches their declaration.

Are local variables shared between threads?

The local variables of a function are unique to each thread that runs the function. However, the static and global variables are shared by all threads in the process.


1 Answers

Concerning C++03:

The abstract machine defined by the C++03 Standard does not contain a formal definition of what a thread is, and what the outcome of a program should be if an object is accessed concurrently.

There is no notion of synchronization primitive, ordering of operations performed in different threads, data race, and so on. Therefore, by definition, every multi-threaded C++03 program contains undefined behavior.

Of course, in practice implementations do provide a documented behavior, but there is nothing in the Standard that specifies what this behavior should be. Therefore, I would say it depends on your compiler.

The rest of the answer will focus on C++11, which does define the semantics of concurrent operations.

Concerning C++11:

Is it guaranteed that generateVar() will be called only once in any scenario (if used of course)?

No, not in any scenario.

The initialization of var is guaranteed to be thread-safe, so generateVar() won't be entered concurrently, but if an exception is thrown by generateVar(), or by the copy constructor or move constructor of SomeType (if SomeType is a UDT, of course), then initialization will be re-attempted the next time the flow of execution enters the declaration - which means generateVar() will get called again.

Per paragraph 6.7/4 of the C++11 Standard on the initialization of block-scope variables with static storage duration:

[...] 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. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined. [...]

Concerning your next question:

Is it guaranteed that foo will return the same value when called multiple times in any scenario?

If it will manage to return a value (see above), then yes.

Is there a difference in behaviour for primitive or non-primitive types?

No, there isn't, except that there is no such a thing as a copy constructor or move constructor for primitive types, so there is also no risk that copy-initialization will result in throwing an exception (unless of course generateVar() throws).

like image 69
Andy Prowl Avatar answered Sep 28 '22 14:09

Andy Prowl