pub fn compiler_fence(order: Ordering)Expand description
An atomic fence for synchronization within a single thread.
Like fence, this function establishes synchronization with other atomic operations and
fences. However, unlike fence, compiler_fence only establishes synchronization with
operations in the same thread. This may at first sound rather useless, since code within a
thread is typically already totally ordered and does not need any further synchronization.
However, there are cases where code can run on the same thread without being synchronized:
- The most common case is that of a signal handler: a signal handler runs in the same thread
as the code it interrupted, but it is not synchronized with that code.
compiler_fencecan be used to establish synchronization between a thread and its signal handler, the same way thatfencecan be used to establish synchronization across threads. - Similar situations can arise in embedded programming with interrupt handlers, or in custom
implementations of preemptive green threads. In general,
compiler_fencecan establish synchronization with code that is guaranteed to run on the same hardware CPU.
See fence for how a fence can be used to achieve synchronization. Note that just like
fence, synchronization still requires atomic operations to be used in both threads – it is
not possible to perform synchronization entirely with fences and non-atomic operations.
compiler_fence does not emit any machine code. However, note that compiler_fence is also
not a “compiler barrier”. It can be helpful to think of a compiler_fence as preventing the
compiler from reordering certain types of memory operations around it, but that is a simplified
model which fails to capture some of the nuances. The only actual guarantee made by
compiler_fence is establishing synchronization with signal handlers and similar kinds of code,
under the rules described in the fence documentation.
compiler_fence corresponds to atomic_signal_fence in C and C++.
§Panics
Panics if order is Relaxed.
§Examples
Without the two compiler_fence calls, the read of IMPORTANT_VARIABLE in signal_handler
is undefined behavior due to a data race, despite everything happening in a single thread.
This is because the signal handler is considered to run concurrently with its associated
thread, and explicit synchronization is required to pass data between a thread and its
signal handler. The code below uses two compiler_fence calls to establish the usual
release-acquire synchronization pattern (see fence for an image).
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::atomic::compiler_fence;
static mut IMPORTANT_VARIABLE: usize = 0;
static IS_READY: AtomicBool = AtomicBool::new(false);
fn main() {
unsafe { IMPORTANT_VARIABLE = 42 };
// Marks earlier writes as being released with future relaxed stores.
compiler_fence(Ordering::Release);
IS_READY.store(true, Ordering::Relaxed);
}
fn signal_handler() {
if IS_READY.load(Ordering::Relaxed) {
// Acquires writes that were released with relaxed stores that we read from.
compiler_fence(Ordering::Acquire);
assert_eq!(unsafe { IMPORTANT_VARIABLE }, 42);
}
}