Skip to main content

fence

Function fence 

1.0.0 ยท Source
pub fn fence(order: Ordering)
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);
    }
}