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)
Releaseordering semantics synchronizes with a fence with (at least)Acquireordering semantics. - fence - atomic synchronization: a fence with (at least)
Releaseordering semantics synchronizes with an atomic operation with (at least)Acquireordering semantics. - fence - fence synchronization: a fence with (at least)
Releaseordering semantics synchronizes with a fence with (at least)Acquireordering 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)
Releaseordering semantics on some atomic object ‘m’,
- an atomic operation ‘X’ with (at least)
-
is paired on thread 2 with:
- an atomic read ‘Y’ with any order on ‘m’,
- followed by a fence ‘B’ with (at least)
Acquireordering 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)
Releaseordering semantics, - followed by an atomic write ‘X’ with any ordering on some atomic object ‘m’,
- a fence ‘A’ with (at least)
-
is paired on thread 2 with:
- an atomic operation ‘Y’ with (at least)
Acquireordering semantics.
- an atomic operation ‘Y’ with (at least)
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)
Releaseordering semantics, - followed by an atomic write ‘X’ with any ordering on some atomic object ‘m’,
- a fence ‘A’ which has (at least)
-
is paired on thread 2 with:
- an atomic read ‘Y’ with any ordering on ‘m’,
- followed by a fence ‘B’ with (at least)
Acquireordering 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);
}
}