Fence Pairs With Fence

int data = 0;                       // plain int, not atomic
std::atomic<bool> ready{false};

// producer thread
data = 42;
std::atomic_thread_fence(std::memory_order_release);
ready.store(true, std::memory_order_relaxed);

// consumer thread
while (!ready.load(std::memory_order_relaxed)) { }
std::atomic_thread_fence(std::memory_order_acquire);
std::cout << data;

Every atomic operation here is relaxed. Is the consumer guaranteed to print 42?

A

Yes, but only incidentally: the busy-wait loop acts as a compiler barrier, and the fences themselves contribute nothing.

B

Yes: the release fence synchronizes with the acquire fence through the ready store that the consumer's load reads, which orders the write to data before the read.

C

No: a fence only restrains the thread that executes it, and relaxed operations create no cross-thread edge for the fences to attach to.

D

No: data is a plain int written in one thread and read in another, which is a data race whatever the fences do.