In the code below, the value of x
in thread 2 will always be 10, because of the atomic thread fences.
int x;
atomic<bool> b(false);
// thread 1:
x = 10;
atomic_thread_fence(memory_order_release);
b = true;
// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(x == 10); // x will always be 10
But in the following code, will *x
always be 10 in thread 2?
int* x = new int;
atomic<bool> b(false);
// thread 1:
*x = 10;
atomic_thread_fence(memory_order_release);
b = true;
// thread 2:
while(!b){}
atomic_thread_fence(memory_order_acquire);
assert(*x == 10); // will *x always be 10?
In both cases you get 10
, there is no difference here whether the store is done directly or through a pointer.
You do not need the memory fence here because b = true
is essentially b.store(true, std::memory_order_seq_cst)
- an acquire-release with a fence.
Such a memory order prevents the compiler from reordering stores and loads around the operation and makes the preceding stores visible to other threads when this store becomes visible.
If you compare the generated code of these two functions:
#include <atomic>
int x;
std::atomic<bool> b(false);
void f() {
x = 10;
std::atomic_thread_fence(std::memory_order_release);
b = true;
}
void g() {
x = 10;
b = true;
}
it is identical:
f():
movl $10, x(%rip)
movb $1, b(%rip)
mfence
ret
g():
movl $10, x(%rip)
movb $1, b(%rip)
mfence
ret
In your particular case though, it seems to me that you need no more than std::memory_order_release
store to b
to make the store to x
also visible to other threads, the fence is unnecessary. I.e. b.store(true, std::memory_order_release)
is enough here. The consumer code needs to do b.load(std::memory_order_acquire)
.
Standard mutexes do acquire memory order on lock and release memory order on unlock (this is where terms acquire/release come from), there are no fences involved.
It is very rare that an explicit fence is required, mostly in hardware drivers. In user-space mode code fences are often put because of misunderstanding of C++11 memory model. Fences are the most expensive atomic synchronization mechanism, this is the main reason they are avoided.
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