When is a memory_order_seq_cst fence useful?
Asked Answered
B

1

6

C++ supported atomic thread fences, that is fences guaranteeing properties for thread that use std::atomic<> operations, with the function atomic_thread_fence. It takes a memory order parameter to adjust the "strength" of the fence.

I understand that fences are useful when not all atomic operations are done with a "strong" order:

  • when not all atomic reads (1) in a thread are acquire operations, you may find a use for an acquire fence;
  • when not all atomic modifications (1) in a thread are release operations, you may find a use for a release fence.

(1) that includes RMW operations

So the usefulness of all these (acquire, release and acq_rel fences) is obvious: they allow threads that use atomic operations weaker than acq/rel (respectively) to synchronize properly.

But I don't understand where memory_order_seq_cst could be specifically needed as a fence:

  • What's the implication of using weaker than memory_order_seq_cst atomic operations and a memory_order_seq_cst fence?

  • What would specifically be guaranteed (in term of possible ordering of atomic operations) by a memory_order_seq_cst fence that wouldn't be guaranteed by memory_order_acq_rel?

Banderole answered 13/12, 2019 at 4:44 Comment(2)
If I remember it correctly, atomic_thread_fence should also affect regular (i.e. not through atomic<>) read and writes.Pericarp
Presumably, such program would involve thread A doing normal writes (non atomic operations) and thread B observing these writes? Would these threads be able to run concurrently without a data race? In C++ data race => UB.Banderole
T
3

No, a seq-cst-fence is not only both a release and an acquire-fence, but also provides some additional properties (see Working Draft, Standard for Programming Language C++, 32.4.4-32.4.8). A seq-cst fence is also part of the single total order of all sequentially consistent operations, enforcing the following observations:

  • For an atomic operation B that reads the value of an atomic object M, if there is a memory_order_seq_cst fence X sequenced before B, then B observes either the last memory_order_seq_cst modification of M preceding X in the total order S or a later modification of M in its modification order.
  • For atomic operations A and B on an atomic object M, where A modifies M and B takes its value, if there is a memory_order_seq_cst fence X such that A is sequenced before X and B follows X in S, then B observes either the effects of A or a later modification of M in its modification order.
  • For atomic operations A and B on an atomic object M, where A modifies M and B takes its value, if there are memory_order_seq_cst fences X and Y such that A is sequenced before X, Y is sequenced before B, and X precedes Y in S, then B observes either the effects of A or a later modification of M in its modification order.

For example, I am using seq-cst fences in my hazard pointer implementation: https://github.com/mpoeter/xenium/blob/master/xenium/reclamation/impl/hazard_pointer.hpp
The thread acquiring a safe reference to some object uses seq-cst fence after storing the hazard pointer, but before re-reading the pointer to the object. The thread trying to reclaim some objects uses a seq-cst fence before gathering the active hazard pointers from all threads. Based on the rules above this ensures that either the thread trying to reclaim the object sees that some other thread has a HP for this object (i.e., the object is used), or the reload of thread trying to acquire the safe reference to the object returns a different pointer, indicating to that thread that the object has been removed and it has to perform a retry.

Timekeeper answered 3/2, 2020 at 10:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.