std::sync::atomic

Function compiler_fence

1.21.0 · source
pub fn compiler_fence(order: Ordering)
Expand description

A “compiler-only” atomic fence.

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 ordered:

  • 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 ordered with respect to that code. compiler_fence can be used to establish synchronization between a thread and its signal handler, the same way that fence can 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_fence can 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, but restricts the kinds of memory re-ordering the compiler is allowed to do. compiler_fence corresponds to atomic_signal_fence in C and C++.

§Panics

Panics if order is Relaxed.

§Examples

Without compiler_fence, the assert_eq! in following code is not guaranteed to succeed, despite everything happening in a single thread. To see why, remember that the compiler is free to swap the stores to IMPORTANT_VARIABLE and IS_READY since they are both Ordering::Relaxed. If it does, and the signal handler is invoked right after IS_READY is updated, then the signal handler will see IS_READY=1, but IMPORTANT_VARIABLE=0. Using a compiler_fence remedies this situation.

use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::atomic::Ordering;
use std::sync::atomic::compiler_fence;

static IMPORTANT_VARIABLE: AtomicUsize = AtomicUsize::new(0);
static IS_READY: AtomicBool = AtomicBool::new(false);

fn main() {
    IMPORTANT_VARIABLE.store(42, Ordering::Relaxed);
    // prevent earlier writes from being moved beyond this point
    compiler_fence(Ordering::Release);
    IS_READY.store(true, Ordering::Relaxed);
}

fn signal_handler() {
    if IS_READY.load(Ordering::Relaxed) {
        assert_eq!(IMPORTANT_VARIABLE.load(Ordering::Relaxed), 42);
    }
}