Reputation: 4078
Let's say we have 2 threads. One producer and one consumer. We have the producer that produce a data, and the consumer that use this data. However the guard is not atomic !
bool isDataReady = false;
int data = 0;
void Producer() {
data = 42;
std::atomic_thread_fence(std::memory_order_release);
isDataReady = true;
}
void Consumer() {
while(!isDataReady);
std::atomic_thread_fence(std::memory_order_acquire);
assert(data == 42);
}
I wonder why is there a data race on isDataReady
.
Normally, the correct code should be to use relaxed
ordering on an atomic bool variable.
Is it because the write (transaction) to isDataReady could be not finished before the read? And even if it is the case, is it really an issue?
Upvotes: 2
Views: 291
Reputation: 10315
This data race is dangerous and you should care about eliminating it. It may not manifest due to your luck, but it will eventually cause a headache.
This code has problems due to a few issues:
While compiling Consumer
compiler is not aware that isDataReady
can change in background so it is perfectly reasonable to emit while(!isDataReady)
an infinite loop or just nothing (due to forward progress guarantee, as was pointed out in comments).
If write and/or read to bool
is not atomic (which is not the case on most platforms, yet is theoretically possible) any of reads can cause getting garbage data.
Memory fence with std::memory_order_release
ensures that changes that happend in the thread will be visible after other thread calls fence with std::memory_order_acquire
(at least in simplification). So, the change of bool variable may be invisible in other thread.
Due to superscalar architecture of modern processorss, operations may be reordered in runtime by processor. So order of memory writes in Producer
visible from Consumer
may be different than the one put in the code.
Upvotes: 4