If I have
atomic<int> cnt=0;
int get_int() noexcept
{
cnt++;
return rand();
}
and then:
void func()
{
static const auto value = get_int();
}
I know that there will be no race condition on initialization of value, but I don't know if
get_int() will be called once, or in my example will cnt be 1 (and not 2, 3, 4, or 5).
Assume multiple threads enter func() and get_int has only 1 callsite in func().
C++11 guarantees that there will be no race condition N3797 - §6.7/4:
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.92 If control re-enters the declaration recursively while the variable is being
initialized, the behavior is undefined. [ Example:
int foo(int i) {
static int s = foo(2*i); // recursive call - undefined
return i+1;
}
- end example ]
It's not re-entrant but thread-safe. Make sure there will not be other parts of the code that will call get_int() before func() anyway.
get_int() will be called only once from that line , but given your code, get_int() could be called beforehand from different locations in the code.
Related
C++11 guarantees that the initialization of static local variables is atomic at the first call of the function. Although the standard doesn't mandate any implementation, the only way to handle this efficiently is double-checked locking.
I asked myself if all objects are initialized are initialized across the same mutex (likely) or if each static object initialization acts on its own mutex (unlikely). So I wrote this litlte C++20-program that uses some variadic and fold expression tricks to have a number of different functions that each initialize their own static object:
#include <iostream>
#include <utility>
#include <latch>
#include <atomic>
#include <chrono>
#include <thread>
using namespace std;
using namespace chrono;
atomic_uint globalAtomic;
struct non_trivial_t
{
non_trivial_t() { ::globalAtomic = ~::globalAtomic; }
non_trivial_t( non_trivial_t const & ) {}
~non_trivial_t() { ::globalAtomic = ~::globalAtomic; }
};
int main()
{
auto createNThreads = []<size_t ... Indices>( index_sequence<Indices ...> ) -> double
{
constexpr size_t N = sizeof ...(Indices);
latch latRun( N );
atomic_uint synch( N );
atomic_int64_t nsSum( 0 );
auto theThread = [&]<size_t I>( integral_constant<size_t, I> )
{
latRun.arrive_and_wait();
if( synch.fetch_sub( 1, memory_order_relaxed ) > 1 )
while( synch.load( memory_order_relaxed ) );
auto start = high_resolution_clock::now();
static non_trivial_t nonTrivial;
nsSum.fetch_add( duration_cast<nanoseconds>( high_resolution_clock::now() - start ).count(), memory_order_relaxed );
};
(jthread( theThread, integral_constant<size_t, Indices>() ), ...);
return (double)nsSum / N;
};
constexpr unsigned N_THREADS = 64;
cout << createNThreads( make_index_sequence<N_THREADS>() ) << endl;
}
I create 64 threads with the above code since my system has up to 64 CPUs in a processor group (Ryzen Threadripper 3990X, Windows 11). The results fulfilled my expectations in a way that each initialization is reported to take about 7.000ns. If each initialization would act on its own mutex the mutex locks would take the short path and you'd have no kernel-contention and the times would be magnitudes lower. So are there any further questions ?
The question I asked myself afterwards is: what happens if the constructor of the static object has its own static object ? Does the standard explicitly mandate that this should work, forcing the implementation to consider that the mutex has to be recursive ?
No, static initialization is not atomic across all objects. Different static objects may get initialized by different threads simultaneously.
It just so happens that GCC and Clang do in fact use a single global recursive mutex (to handle the recursive case you described, which is required to work), but other compilers use a mutex for every static function-local object (i.e. Apple's compiler). Therefore you can't rely on static initialization happening one object at a time - simply because it doesn't, depending on your compiler (and the version of that compiler).
Section 6.7.4 of the standard:
A local object of POD type (basic.types) with static
storage duration initialized with con- stant-expressions is
initialized before its block is first entered. An implementation is
permitted to perform early initialization of other local objects
with static storage duration under the same conditions that an
implementation is permitted to statically initialize an object with
static storage duration in namespace scope (basic.start.init).
Otherwise such an object is initialized the first time control passes
through its declaration; such an object 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 re-enters the declaration (recursively) while the object is being initialized, the behavior is undefined.
The standard only forbids recursive initialization of the same static object; it doesn't forbid the initialization of one static object to require another static object to be initialized. Since the standard explicitly states that all static objects that don't fall in this forbidden category must be initialized when the block containing them is first executed, the case you asked about is allowed.
int getInt1();
int getInt2() { //This could be a constructor, too, and nothing would change
static int result = getInt1();
return result;
}
int getInt3() {
static int result = getInt2(); //Allowed!
return result;
}
This also applies to the case when the constructor of a function-local static object itself contains such a static object. A constructor is really just a function too, which means this case is identical to the example above.
See also: https://manishearth.github.io/blog/2015/06/26/adventures-in-systems-programming-c-plus-plus-local-statics/
Every static local variable has to be atomic. If every single one of them has it's own mutex or double-checked locking then that will be true.
There could also be a single global recursive mutex that allows one thread and one thread only to be initializing static local variables at a time. That works too. But if you have many static local variables and multiple threads accessing them for the first time then that could be horribly slow.
But lets consider your case of a static local variable having a static local variable:
class A {
static int x = foo();
};
void bla() {
static A a;
};
Initializing a requires initializing x. But nothing says there can't be some other thread that also has an A c; and will be initializing x at the same time. So x still needs to be protected even though in the case of bla() it is inside an already static initialization.
Another example (hope that compiles, haven't checked):
void foo() {
static auto fn = []() {
static int x = bla();
};
}
Here x can only ever be initialized when fn is initialized. So the compiler could possibly skip protecting x. That would be an optimization that follows the as-if principal. Apart from timing there is no difference whether x is protected or not. On the other hand locking for x would always succeed and the cost of that is very small. Compilers might not optimize it because nobody invested the time to detect and optimize such cases.
class IA
{
public:
virtual void Print() = 0;
}
IA* GetA();
class A : public IA
{
public:
int num = 10;
A()
{
GetA()->Print();
}
void Print()
{
std::cout << num << std::endl;
}
}
IA* GetA()
{
static A a;
return &a;
}
int main()
{
GetA();
std::cout << "End" << std::endl;
getchar();
return 0;
}
Obviously, class A's constructor function calls itself.
"static A a" will get stuck in a loop.
On VS2013, this code can get out from the loop and print "End" on the console.
On VS2017, this code gets stuck in a loop.
**What does VS2013 do for this code?
Nothing in particular has to happen. According to the C++ standard:
[stmt.dcl] (emphasis mine)
4 Dynamic initialization of a block-scope variable with static storage
duration or thread storage duration is performed 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. If control re-enters the
declaration recursively while the variable is being initialized, the
behavior is undefined. [ Example:
int foo(int i) {
static int s = foo(2*i); // recursive call - undefined
return i+1;
}
— end example ]
The statement I emboldened is exactly what happens in your program. It's also what the standard's example shows as undefined. The language specification says an implementation can do whatever it deems appropriate. So it could cause an infinite loop, or it may not, depending on the synchronization primitives your implementation uses to prevent concurrent reentry into the block (the initialization has to be thread safe).
Even before C++11, the behavior of recursive reentry was undefined. An implementation could do anything to make sure an object is initialized only once, which in turn could produce different results.
But you can't expect anything specific to happen portably. Not to mention undefined behavior always leaves room for a small chance of nasal demons.
The behaviour is undefined. The reason it "worked" in Visual Studio 2013 is that it didn't implement thread safe initialisation of function statics. What is probably happening is that the first call to GetA() creates a and calls the constructor. The second call to GetA() then just returns the partially constructed a. As the body of your constructor doesn't initialise anything calling Print() doesn't crash.
Visual Studio 2017 does implement thread safe initialisation. and presumably locks some mutex on entry to GetA() if a is not initialised, the second call to GetA() then encounters the locked mutex and deadlocks.
Note in both cases this is just my guess from the observed behaviour, the actual behaviour is undefined, for example GetA() may end up creating 2 instances of A.
This question already has answers here:
Why does initialization of local static objects use hidden guard flags?
(2 answers)
Closed 4 years ago.
As in the title - how does program know, that foo is already initialized when function is called second time:
int getFoo()
{
static int foo = 30;
return foo;
}
int main()
{
getFoo();
getFoo();
}
I want to know, whether the program stores some additional information about which static variable was already initialized.
Edit:
I found an answer here:
Why does initialization of local static objects use hidden guard flags?
Like I guessed - most compilers store additional "guard variable".
Have a look at [stmt.dcl]/4:
Dynamic initialization of a block-scope variable with static storage duration or thread storage duration is performed 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.94 If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.
You have to be careful here. Primitive statics are initialised at compile time (as long as the initialisation value is a compile-time contant, as Peter points out), so in your example, GetFoo just, in effect, returns a constant.
HOWEVER...
statics which initialise an object (or initialise a primitive by calling a function) perform said initialisation when the scope in which they are declared is entered for the first time.
Furthermore, as of C++ 11 this has to be done in a threadsafe way, which generates a lot of extra code (although not much runtime overhead, after the first time through) and that might be an issue on, say, a micro-controller where code size often matters.
Here's a concrete example:
#include <iostream>
struct X
{
X () { std::cout << "Initialising m\n"; m = 7; }
int m;
};
void init_x ()
{
static X x;
}
int main () {
std::cout << "main called\n";
init_x ();
std::cout << "init_x returned\n";
}
Output:
main called
Initialising m
init_x returned
Live demo: https://wandbox.org/permlink/NZApcYYGwK36vRD4
Generated code: https://godbolt.org/z/UUcL9s
I'm using a function static variable that I want to initialize once by calling a setter:
void foo() {
static Obj obj;
obj.setName("name"); // this should be called once
// use obj
}
I don't want the setter to be called multiple times, also due to multi threading issues and I can't add a constructor to Obj since I don't own the code. So is this reasonable and thread safe:
void foo() {
static Obj obj = []() {
Obj o;
o.setName("name");
return o;
}();
// use obj
}
Yes, it's thread safe. Quoting n3337 - [stmt.dcl]/4, emphasis mine:
...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.
You perform copy initialization from a lambda's return value, but it's immaterial. The paragraph above doesn't constrain the initialization of obj to be value or direct initialization. All forms of initialization are applicable.
And as a side note, if you must contend with such poorly written types, I'd argue your solution is very idiomatic. It doesn't artificially introduce a new named function to perform the initialization. Instead it keeps initialization code localized. That is a good thing in my book.
How should I work with exceptions throwed from constructors of local static objects? For example I have following code:
class A
{
public:
A() {throw runtime_error("Ooops");}
};
void foo()
{
static A a = A();
cout << "Continue" << endl;
}
int main(void)
{
try
{
foo();
}
catch(...)
{
}
foo(); // Prints continue
return 0;
}
As I understand, in the case of second calling foo method, object a is treated as fully constructed object, and constructor is not called. (More over, it seems like destructor of a due first exception throwing is not called)
If that's true, then that is a compiler bug.
(However, VTT claims that this code produces the expected result with Visual Studio 2015, so I recommend double-checking your results.)
Here's the standard-mandated behaviour:
[C++14: 6.7/4]: The zero-initialization (8.5) of all block-scope variables with static storage duration (3.7.1) or thread storage duration (3.7.2) is performed before any other initialization takes place. Constant initialization (3.6.2) of a block-scope entity with static storage duration, if applicable, is performed before its block is first entered. 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. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined. [..]
GCC 6.3.0 correctly attempts to reconstruct the A (and thus throws again).
More over, it seems like destructor of a due first exception throwing is not called
No, it won't be. You can't destroy an object that was never successfully created in the first place.
[C++14: 15.2/2]: An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked. If the object was allocated in a new-expression, the matching deallocation function (3.7.4.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.
By the way, this doesn't fix the problem, but you should just write:
static A a;
The copy-initialisation from a temporary is pointless.
Each instance of static local variable also implicitly creates a global Boolean variable that will be set to true after static variable is constructed. If constructor throws than the next time method is called there will be another attempt to construct a static variable.