https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
I took an example from this article from the section acquire/release
and rewrote it with bools.
-Thread 1-
y.store (20, memory_order_release);
-Thread 2-
x.store (10, memory_order_release);
-Thread 3-
assert (y.load (memory_order_acquire) == 20 && x.load (memory_order_acquire) == 0)
-Thread 4-
assert (y.load (memory_order_acquire) == 0 && x.load (memory_order_acquire) == 10)
This article says:
If this example were written using the sequentially consistent model, then one of the stores must happen-before the other (although the order isn't determined until run-time), the values are synchronized between threads, and if one assert passes, the other assert must therefore fail.
Why does my program sometimes print success!!! iteration number:
if I'm using seq_cst memory order?
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
bool test() {
std::atomic_bool is_started{false};
std::atomic_bool x{false};
std::atomic_bool y{false};
std::atomic_bool x_before_y{false}; // indicates that store(x, true) happened-before store(y, true)
std::atomic_bool y_before_x{false}; // indicates that store(y, true) happened-before store(x, true)
std::thread t1([&]{ // -Thread 1-
while (!is_started.load()) {}
y.store(true, std::memory_order_seq_cst); // store(y, true)
});
std::thread t2([&]{ // -Thread 2-
while (!is_started.load()) {}
x.store(true, std::memory_order_seq_cst); // store(x, true)
});
std::thread t3([&] { // -Thread 3-
while (!is_started.load()) {}
if (!y.load(std::memory_order_seq_cst) && x.load(std::memory_order_seq_cst)) {
x_before_y.store(true);
}
});
std::thread t4([&]{ // -Thread 4-
while (!is_started.load()) {}
if (y.load(std::memory_order_seq_cst) && !x.load(std::memory_order_seq_cst)) {
y_before_x.store(true);
}
});
is_started.store(true);
t1.join();
t2.join();
t3.join();
t4.join();
return x_before_y.exchange(false) && y_before_x.exchange(false);
}
int main() {
for (int i = 0; i < 100000000; i++) {
if (test()){
std::cout << "success!!! iteration number: " << i << std::endl;
return 1;
}
if (i % 1000000 == 0) {
std::cout << "still running" << std::endl;
}
}
std::cout << "fail :(" << std::endl;
return 0;
}
As @j6t mentioned in comment, example from the article doesn't show the difference between the memory orders