Based on standard there is the following example
#include <atomic>
#include <cassert>
#include <thread>
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> z = {0};
void write_x()
{
x.store(true, std::memory_order_seq_cst);
}
void write_y()
{
y.store(true, std::memory_order_seq_cst);
}
void read_x_then_y()
{
while (!x.load(std::memory_order_seq_cst))
;
if (y.load(std::memory_order_seq_cst))
++z;
}
void read_y_then_x()
{
while (!y.load(std::memory_order_seq_cst))
;
if (x.load(std::memory_order_seq_cst))
++z;
}
int main()
{
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x);
a.join(); b.join(); c.join(); d.join();
assert(z.load() != 0); // will never happen
}
Based on standard load of x/y happens after x/y are stored. Due to this no assert is fired If the code above is changed as following
#include <atomic>
#include <cassert>
#include <thread>
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> z = {0};
void write_x()
{
x.store(true, std::memory_order_release);
}
void write_y()
{
y.store(true, std::memory_order_release);
}
void read_x_then_y()
{
while (!x.load(std::memory_order_acquire))
;
if (y.load(std::memory_order_acquire))
++z;
}
void read_y_then_x()
{
while (!y.load(std::memory_order_acquire))
;
if (x.load(std::memory_order_acquire))
++z;
}
int main()
{
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x);
a.join(); b.join(); c.join(); d.join();
assert(z.load() != 0); // will never happen
}
Then the assert is fired. But my question is when the stored value x/y is known to the other threads? Based on the standard there is the following statement
All memory writes (including non-atomic and relaxed atomic) that happened-before the atomic store from the point of view of thread A, become visible side-effects in thread B. That is, once the atomic load is completed, thread B is guaranteed to see everything thread A wrote to memory. This promise only holds if B actually returns the value that A stored, or a value from later in the release sequence.
Thread writes value x. Is the new value x known to thread B when thread c or d loads the variable x? What is exactly the race condition that fires the assert?
Based on standard load of x/y happens after x/y are stored. Due to this no assert is fired
More precisely, the single total modification order of sequentially consistent operations means that the operations of store x, store y, and the loads happen in some order.
In particular, either the store of x or store of y happens first in this modification order. Let's say it's x. Then, in the thread that waits for y to be true, afterward it must load true for x.
What is exactly the race condition that fires the assert?
There is no ordering. read_x_then_y may load x true and then y false, and read_y_then_x may load y true and then x false.