-
Notifications
You must be signed in to change notification settings - Fork 61
Description
Certain concurrent algorithms can benefit from splitting SeqCst fences into fast and slow paths, where instead of executing a regular fence(SeqCst) in both, the fast path simply executes a compiler fence to prevent compiler reordering, and the slow path executes a process-wide memory barrier, such as membarrier on Linux, or FlushProcessWriteBuffers on Windows. The membarrier crate provides an abstraction over this. P1202R4 - Asymmetric Fences proposes to add this to C++ in the form of:
void asymmetric_thread_fence_heavy(memory_order order) noexcept;
void asymmetric_thread_fence_light(memory_order order) noexcept;One use case for asymmetric fences is in memory reclamation algorithms, such as haphazard, a Rust implementation of Hazard Pointers.
The formal spec from P1202R4:
X.Z Asymmetric fences [atomics.fences.asym]
This section introduces synchronization primitives called heavyweight-fences and
lightweight-fences. Like fences, heavyweight-fences and lightweight-fences can have acquire
semantics, release semantics, or both, and can be sequentially consistent (in which case they
are included in the total order S on memory_order::seq_cst operations). A heavyweight-fence
has all the synchronization effects of a fence as specified in 31.11 [atomic.fences]. [ Note:
Heavyweight-fences and lightweight-fences are distinct from fences. -- end note ]If there are evaluations A and B, and atomic operations X and Y, both operating on some atomic
object M, such that A is sequenced before X, X modifies M, Y is sequenced before B, and Y
reads the value written by X or a value written by any side effect in the hypothetical release
sequence X would head if it were a release operation, and one of the following hold:
- A is a release lightweight-fence and B is an acquire heavyweight-fence; or
- A is a release heavyweight-fence and B is an acquire lightweight-fence
then any evaluation sequenced before A strongly happens before any evaluation that B is
sequenced before.
void asymmetric_thread_fence_heavy(memory_order order) noexcept;
- Effects: Depending on the value of order, this operation:
- has no effects, if order == memory_order::relaxed;
- is an acquire heavyweight-fence, if order == memory_order::acquire or order ==
memory_order::consume;- is a release heavyweight-fence, if order == memory_order::release;
- is both an acquire heavyweight-fence and a release heavyweight-fence, if order ==
memory_order::acq_rel;- is a sequentially consistent acquire and release heavyweight-fence, if order ==
memory_order::seq_cst.
void asymmetric_thread_fence_light(memory_order order) noexcept;
- Effects: Depending on the value of order, this operation:
- has no effects, if order == memory_order::relaxed;
- is an acquire lightweight-fence, if order == memory_order::acquire or order ==
memory_order::consume;- is a release lightweight-fence, if order == memory_order::release;
- is both an acquire lightweight-fence and a release lightweight-fence, if order ==
memory_order::acq_rel;- is a sequentially consistent acquire and release lightweight-fence, if order ==
memory_order::seq_cst.[ Note: Delegating both heavy and light fence functions to an atomic_thread_fence(order)
call is a valid implementation.