You can see here that init_xnext_x is definitely not thread-safe, but init_x_tsnext_x_ts never initializes the flag variable at cs:dword_1400035D4 -- it is zero-initialized at application start, so there are no races and init_x_tsnext_x_ts is thread-safe.
You can see here that init_x is definitely not thread-safe, but init_x_ts never initializes the flag variable at cs:dword_1400035D4 -- it is zero-initialized at application start, so there are no races and init_x_ts is thread-safe.
You can see here that next_x is definitely not thread-safe, but next_x_ts never initializes the flag variable at cs:dword_1400035D4 -- it is zero-initialized at application start, so there are no races and next_x_ts is thread-safe.
YouSection 6.7.4 of C++11 states that variables with static storage duration are correct ininitialized thread-safe:
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
But neither of VC++ 2012 or 2013 Preview implement this, so yes, you'll need some protection to make your reasoning that using a mutex will not workfunction thread-safe. Your initialization of
C++11 also says this about atomic_flagATOMIC_FLAG_INIT is safe. In the standard, in section 29.7.4:
VC++ does happen to implement this properly. ATOMIC_FLAG_INIT is 0 in VC++, and VC++ zero-initializes all statics at application start, not in the function call. So, your use of this is safe and there will be no race to initialize lock.
Test Code:
struct nontrivial { nontrivial() : x(123) {} int x; }; __declspec(dllexport) int next_x() { static nontrivial x; return ++x.x; } __declspec(dllexport) int next_x_ts() { static std::atomic_flag flag = ATOMIC_FLAG_INIT; while(flag.test_and_set()); static nontrivial x; flag.clear(); return ++x.x; } next_x:
mov eax, cs:dword_1400035E4 test al, 1 ; checking if x has been initialized. jnz short loc_140001021 ; if it has, go down to the end. or eax, 1 mov cs:dword_1400035E4, eax ; otherwise, set it as initialized. mov eax, 7Bh inc eax ; /O2 is on, how'd this inc sneak in!? mov cs:dword_1400035D8, eax ; init x.x to 124 and return. retn loc_140001021: mov eax, cs:dword_1400035D8 inc eax mov cs:dword_1400035D8, eax retn next_x_ts:
loc_140001032: lock bts cs:dword_1400035D4, 0 ; flag.test_and_set(). jb short loc_140001032 ; spin until set. mov eax, cs:dword_1400035E0 test al, 1 ; checking if x has been initialized. jnz short loc_14000105A ; if it has, go down to end. or eax, 1 ; otherwise, set is as initialized. mov cs:dword_1400035E8, 7Bh ; init x.x with 123. mov cs:dword_1400035E0, eax loc_14000105A: lock btr cs:dword_1400035D4, 0 ; flag.clear(). mov eax, cs:dword_1400035E8 inc eax mov cs:dword_1400035E8, eax retn You can see here that init_x is definitely not thread-safe, but init_x_ts never initializes the flag variable at cs:dword_1400035D4 -- it is zero-initialized at application start, so there are no races and init_x_ts is thread-safe.
You are correct in your reasoning that using a mutex will not work. Your initialization of atomic_flag is safe. In the standard, section 29.7.4:
So there will be no race to initialize lock.
Section 6.7.4 of C++11 states that variables with static storage duration are initialized thread-safe:
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
But neither of VC++ 2012 or 2013 Preview implement this, so yes, you'll need some protection to make your function thread-safe.
C++11 also says this about ATOMIC_FLAG_INIT, in section 29.7.4:
VC++ does happen to implement this properly. ATOMIC_FLAG_INIT is 0 in VC++, and VC++ zero-initializes all statics at application start, not in the function call. So, your use of this is safe and there will be no race to initialize lock.
Test Code:
struct nontrivial { nontrivial() : x(123) {} int x; }; __declspec(dllexport) int next_x() { static nontrivial x; return ++x.x; } __declspec(dllexport) int next_x_ts() { static std::atomic_flag flag = ATOMIC_FLAG_INIT; while(flag.test_and_set()); static nontrivial x; flag.clear(); return ++x.x; } next_x:
mov eax, cs:dword_1400035E4 test al, 1 ; checking if x has been initialized. jnz short loc_140001021 ; if it has, go down to the end. or eax, 1 mov cs:dword_1400035E4, eax ; otherwise, set it as initialized. mov eax, 7Bh inc eax ; /O2 is on, how'd this inc sneak in!? mov cs:dword_1400035D8, eax ; init x.x to 124 and return. retn loc_140001021: mov eax, cs:dword_1400035D8 inc eax mov cs:dword_1400035D8, eax retn next_x_ts:
loc_140001032: lock bts cs:dword_1400035D4, 0 ; flag.test_and_set(). jb short loc_140001032 ; spin until set. mov eax, cs:dword_1400035E0 test al, 1 ; checking if x has been initialized. jnz short loc_14000105A ; if it has, go down to end. or eax, 1 ; otherwise, set is as initialized. mov cs:dword_1400035E8, 7Bh ; init x.x with 123. mov cs:dword_1400035E0, eax loc_14000105A: lock btr cs:dword_1400035D4, 0 ; flag.clear(). mov eax, cs:dword_1400035E8 inc eax mov cs:dword_1400035E8, eax retn You can see here that init_x is definitely not thread-safe, but init_x_ts never initializes the flag variable at cs:dword_1400035D4 -- it is zero-initialized at application start, so there are no races and init_x_ts is thread-safe.
You are correct in your reasoning that using a mutex will not work. Your initialization of atomic_flag is safe. In the standard, section 29.7.4:
The macro
ATOMIC_FLAG_INITshall be defined in such a way that it can be used to initialize an object of typeatomic_flagto the clear state. For a static-duration object, that initialization shall be static.
So there will be no race to initialize lock.