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?
generateVar()
will be called only once in any scenario (if used of course)?foo
will return the same value when called multiple times in any scenario?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.
With C++11, static variables with block scope have an additional guarantee; they will be initialized in a thread-safe way.
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.
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With