Skip to main content

fence

Function fence 

1.6.0 · Source
pub fn fence(order: Ordering)
This item is validated for IEC 61508 (SIL 2) and ISO 26262 (ASIL B).
Expand description

An atomic fence.

Fences create synchronization between themselves and atomic operations or fences in other threads. To achieve this, a fence prevents the compiler and CPU from reordering certain types of memory operations around it.

There are 3 different ways to use an atomic fence:

  • atomic - fence synchronization: an atomic operation with (at least) Release ordering semantics synchronizes with a fence with (at least) Acquire ordering semantics.
  • fence - atomic synchronization: a fence with (at least) Release ordering semantics synchronizes with an atomic operation with (at least) Acquire ordering semantics.
  • fence - fence synchronization: a fence with (at least) Release ordering semantics synchronizes with a fence with (at least) Acquire ordering semantics.

These 3 ways complement the regular, fence-less, atomic - atomic synchronization.

§Atomic - Fence

An atomic operation on one thread will synchronize with a fence on another thread when:

  • on thread 1:

    • an atomic operation ‘X’ with (at least) Release ordering semantics on some atomic object ‘m’,
  • is paired on thread 2 with:

    • an atomic read ‘Y’ with any order on ‘m’,
    • followed by a fence ‘B’ with (at least) Acquire ordering semantics.

This provides a happens-before dependence between X and B.

    Thread 1                                          Thread 2

m.store(3, Release); X ---------
                               |
                               |
                               -------------> Y  if m.load(Relaxed) == 3 {
                                              B      fence(Acquire);
                                                     ...
                                                 }

§Fence - Atomic

A fence on one thread will synchronize with an atomic operation on another thread when:

  • on thread:

    • a fence ‘A’ with (at least) Release ordering semantics,
    • followed by an atomic write ‘X’ with any ordering on some atomic object ‘m’,
  • is paired on thread 2 with:

    • an atomic operation ‘Y’ with (at least) Acquire ordering semantics.

This provides a happens-before dependence between A and Y.

    Thread 1                                          Thread 2

fence(Release);      A
m.store(3, Relaxed); X ---------
                               |
                               |
                               -------------> Y  if m.load(Acquire) == 3 {
                                                     ...
                                                 }

§Fence - Fence

A fence on one thread will synchronize with a fence on another thread when:

  • on thread 1:

    • a fence ‘A’ which has (at least) Release ordering semantics,
    • followed by an atomic write ‘X’ with any ordering on some atomic object ‘m’,
  • is paired on thread 2 with:

    • an atomic read ‘Y’ with any ordering on ‘m’,
    • followed by a fence ‘B’ with (at least) Acquire ordering semantics.

This provides a happens-before dependence between A and B.

    Thread 1                                          Thread 2

fence(Release);      A --------------
m.store(3, Relaxed); X ---------    |
                               |    |
                               |    |
                               -------------> Y  if m.load(Relaxed) == 3 {
                                    |-------> B      fence(Acquire);
                                                     ...
                                                 }

§Mandatory Atomic

Note that in the examples above, it is crucial that the access to m are atomic. Fences cannot be used to establish synchronization between non-atomic accesses in different threads. However, thanks to the happens-before relationship, any non-atomic access that happen-before the atomic operation or fence with (at least) Release ordering semantics are now also properly synchronized with any non-atomic accesses that happen-after the atomic operation or fence with (at least) Acquire ordering semantics.

§Memory Ordering

A fence which has SeqCst ordering, in addition to having both Acquire and Release semantics, participates in the global program order of the other SeqCst operations and/or fences.

Accepts Acquire, Release, AcqRel and SeqCst orderings.

§Panics

Panics if order is Relaxed.

§Examples

use std::sync::atomic::AtomicBool;
use std::sync::atomic::fence;
use std::sync::atomic::Ordering;

// A mutual exclusion primitive based on spinlock.
pub struct Mutex {
    flag: AtomicBool,
}

impl Mutex {
    pub fn new() -> Mutex {
        Mutex {
            flag: AtomicBool::new(false),
        }
    }

    pub fn lock(&self) {
        // Wait until the old value is `false`.
        while self
            .flag
            .compare_exchange_weak(false, true, Ordering::Relaxed, Ordering::Relaxed)
            .is_err()
        {}
        // This fence synchronizes-with store in `unlock`.
        fence(Ordering::Acquire);
    }

    pub fn unlock(&self) {
        self.flag.store(false, Ordering::Release);
    }
}