The following code is a race condition:

#include 
#include 
#include 
using namespace std;

int main(int argc, char** argv)
{
	atomic_bool flag{false};
	bool data{false};

	thread t1([&]() {
		data = true;
		flag.store(true, memory_order_relaxed);
	});

	thread t2([&]() {
		while(flag.load(memory_order_relaxed) == false);
		cout << "data = " << boolalpha << data << endl;
	});

	t1.join();
	t2.join();

	return 1;
}

Because

memory_order_relaxed
is used the compiler and the CPU are free to reorder the two writes in thread
t1
. They are also free to reorder the two reads in thread
t2
. Hence undefined behavior.

The fix is to either use

memory_order_seq_cst
on the
store
and
load
calls, or
memory_order_release
on the
store
call and
memory_order_acquire
on the load call. Release prevents any prior loads and stores to be reordered past it; acquire guarantees that all stores from the thread that released the atomic variable are visible to the current thread; it also prevents reads and writes to be reordered before it. See here.

Leave a Reply