Memory Barrier
A memory barrier is an instruction that prevents memory reordering across itself. No access after the barrier becomes visible until all accesses before it have become visible. Covered in ECE459 L15.
Why do we need barriers?
Compilers and CPUs reorder loads and stores for speed. A barrier is the low-level tool that says “this happens before that”, analogous to a semaphore at a higher level.
x86 barriers
mfence: all loads and stores before become visible before any loads and stores aftersfence: all stores before become visible before all stores afterlfence: all loads before become visible before all loads after
An sfence on one CPU makes stores visible, but another CPU still needs an lfence or mfence to read them in the right order.
Spin-wait flag
f = 0 /* thread 1 */ /* thread 2 */ while (f == 0) /* spin */; x = 42; // memory fence // memory fence printf("%d", x); f = 1;Fences ensure
x = 42is visible beforef = 1, so when thread 1 escapes the spin it prints 42.
In Rust atomics
Ordering::Acquire: later accesses cannot move beforeOrdering::Release: earlier accesses cannot move afterOrdering::SeqCst: full fence, restores SCOrdering::Relaxed: no fence
Cost
Fences block reorderings the compiler and CPU would use for speed, and force a thread to wait for another. SC necessarily generates fences, which is why it is expensive. Use the weakest ordering that is still correct.