Is Local Static Variable Initialization Thread-Safe in C++11

Is local static variable initialization thread-safe in C++11? [duplicate]

The relevant section 6.7:

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 control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

Then there's a footnote:

The implementation must not introduce any deadlock around execution of the initializer.

So yes, you're safe.

(This says nothing of course about the subsequent access to the variable through the reference.)

Local Static variable initialization is thread safe [duplicate]

Yes, it will be thread safe, but only since C++11. Static variables are initialized in thread safe way, they are often also called magic statics.

For more see here: http://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables

If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once).
Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison.

Also - in your code you call CreateEmployee(); during initialization of static i, and CreateEmployee( also initializes a static variable. This should also be OK, you can find in standard following footnote:

The implementation must not introduce any deadlock around execution of the
initializer.

As to your second question, from the code you have shown I dont see that it is OK to use static variable as a way to gain thread safety.

Are you aware that specifying a variable as static inside function body allows you to assign it only once? This means your CreateEmployee() will always return the same Employee instance.

Cost of thread-safe local static variable initialization in C++11?

A look at the generated assembler code helps.

Source

#include <vector>

std::vector<int> &get(){
static std::vector<int> v;
return v;
}
int main(){
return get().size();
}

Assembler

std::vector<int, std::allocator<int> >::~vector():
movq (%rdi), %rdi
testq %rdi, %rdi
je .L1
jmp operator delete(void*)
.L1:
rep ret
get():
movzbl guard variable for get()::v(%rip), %eax
testb %al, %al
je .L15
movl get()::v, %eax
ret
.L15:
subq $8, %rsp
movl guard variable for get()::v, %edi
call __cxa_guard_acquire
testl %eax, %eax
je .L6
movl guard variable for get()::v, %edi
movq $0, get()::v(%rip)
movq $0, get()::v+8(%rip)
movq $0, get()::v+16(%rip)
call __cxa_guard_release
movl $__dso_handle, %edx
movl get()::v, %esi
movl std::vector<int, std::allocator<int> >::~vector(), %edi
call __cxa_atexit
.L6:
movl get()::v, %eax
addq $8, %rsp
ret
main:
subq $8, %rsp
call get()
movq 8(%rax), %rdx
subq (%rax), %rdx
addq $8, %rsp
movq %rdx, %rax
sarq $2, %rax
ret

Compared to

Source

#include <vector>

static std::vector<int> v;
std::vector<int> &get(){
return v;
}
int main(){
return get().size();
}

Assembler

std::vector<int, std::allocator<int> >::~vector():
movq (%rdi), %rdi
testq %rdi, %rdi
je .L1
jmp operator delete(void*)
.L1:
rep ret
get():
movl v, %eax
ret
main:
movq v+8(%rip), %rax
subq v(%rip), %rax
sarq $2, %rax
ret
movl $__dso_handle, %edx
movl v, %esi
movl std::vector<int, std::allocator<int> >::~vector(), %edi
movq $0, v(%rip)
movq $0, v+8(%rip)
movq $0, v+16(%rip)
jmp __cxa_atexit

I'm not that great with assembler, but I can see that in the first version v has a lock around it and get is not inlined whereas in the second version get is essentially gone.

You can play around with various compilers and optimization flags, but it seems no compiler is able to inline or optimize out the locks, even though the program is obviously single threaded.

You can add static to get which makes gcc inline get while preserving the lock.

To know how much these locks and additional instructions cost for your compiler, flags, platform and surrounding code you would need to make a proper benchmark.

I would expect the locks to have some overhead and be significantly slower than the inlined code, which becomes insignificant when you actually do work with the vector, but you can never be sure without measuring.

C++11 Thread-safe initialization of function-local static const objects

Since C++11 all static local variables are guaranteed to be initialized only once in a thread-safe manner.

As per cppreference:

If multiple threads attempt to initialize the same static local
variable concurrently, the initialization occurs exactly once (similar
behavior can be obtained for arbitrary functions with std::call_once).
Note: usual implementations of this feature use variants of the
double-checked locking pattern, which reduces runtime overhead for
already-initialized local statics to a single non-atomic boolean
comparison.

So, for your questions:

  1. yes
  2. yes
  3. yes

C++11 Singleton. Static variable is thread safe? Why?

In C++11 (and forward), the construction of the function local static AppSettings is guaranteed to be thread-safe. Note: Visual Studio did not implement this aspect of C++11 until VS-2015.

The compiler will lay down a hidden flag along side of AppSettings that indicates whether it is:

  • Not constructed.
  • Being constructed.
  • Is constructed.

The first thread through will find the flag set to "not constructed" and attempt to construct the object. Upon successful construction the flag will be set to "is constructed". If another thread comes along and finds the flag set to "being constructed", it will wait until the flag is set to "is constructed".

If the construction fails with an exception, the flag will be set to "not constructed", and construction will be retried on the next pass through (either on the same thread or a different thread).

The object instance will remain constructed for the remainder of your program, until main() returns, at which time instance will be destructed.

Every time any thread of execution passes through AppSettings::GetInstance(), it will reference the exact same object.

In C++98/03, the construction was not guaranteed to be thread safe.

If the constructor of AppSettings recursively enters AppSettings::GetInstance(), the behavior is undefined.

If the compiler can see how to construct instance "at compile time", it is allowed to.

If AppSettings has a constexpr constructor (the one used to construct instance), and the instance is qualified with constexpr, the compiler is required to construct instance at compile time. If instance is constructed at compile time, the "not-constructed/constructed" flag will be optimized away.

Is C++ static member variable initialization thread-safe?

It's more a question of function-scoped static variables vs. every other kind of static variable, rather than scoped vs. globals.

All non-function-scope static variables are constructed before main(), while there is only one active thread. Function-scope static variables are constructed the first time their containing function is called. The standard is silent on the question of how function-level statics are constructed when the function is called on multiple threads. However, every implementation I've worked with uses a lock around the constructor (with a twice-checked flag) to guarantee thread-safety.

C++: Is initializing a static local variable with a IIFE thread-safe?

Yes. C++11 (and above) guarantees no data races between multiple threads trying to initialize a static local variable. If the code inside your lambda is thread-safe, the initialization will be as well.

Using a lambda, function call, or constructor doesn't change the thread-safety of the initialization.



Related Topics



Leave a reply



Submit