Consider the example below. Assume that barrier is initialized to 0.
There is one producer thread and two consumer threads that constantly check barrier. If the barrier is set, they decrease runcnt. The producer thread waits for runcnt to reach 0. I am confused about the order of multiple store operations inside the producer.
If the order is like it is written, I think the code would run as expected. But if the barrier store is reordered before runcnt store, it seems the assert check would fail.
Am I missing anything? Is there a way to fix this?
extern atomic<int> barrier[2];
atomic_int runcnt{0};
void producer() {
runcnt.store(2, memory_order_relaxed);
barrier[0].store(1, memory_order_relaxed);
barrier[1].store(1, memory_order_relaxed);
while (runcnt.load(memory_order_relaxed)) {
cpu_pause();
}
}
void consumer(unsigned index) {
while (true) {
if (barrier[index].exchange(false, memory_order_relaxed)) {
int prev = runcnt.fetch_sub(1, memory_order_relaxed);
assert(prev > 0);
}
}
}
If the order is like it is written - You mean if the operations happen to become visible in an order that would also be allowed by
seq_cst? You could of course require that withseq_cston all the operations.I think the minimum in the reader side is for
barrier[i].exchangeto beacquire.And in the writer side, both
barrier[i]stores need to berelease, or to put onestd::atomic_thread_fence(release)right afterruncnt.store, so it's between that and either barrier store.That makes the
exchangesynchronize with whicheverbarrierstore it loads, assuming that it loaded a1so theifbody runs at all.runcnt.store(relaxed);barrier[0].store(release);barrier[1].store(relaxed)would not be sufficient in the C++ memory model or even when compiling for some ISAs: the final relaxed store can pass the release store because it's only a 1-way barrier. This is a key difference between fences and operations: https://preshing.com/20131125/acquire-and-release-fences-dont-work-the-way-youd-expect/ . Even making the middle storeseq_cstwouldn't be sufficient, it's still just an operation, not 2-way fence.