Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does "strongly happens before" mean?

The phrase "strongly happens before" is used several times in the C++ draft standard.

For example: Termination [basic.start.term]/5

If the completion of the initialization of an object with static storage duration strongly happens before a call to std​::​atexit (see , [support.start.term]), the call to the function passed to std​::​atexit is sequenced before the call to the destructor for the object. If a call to std​::​atexit strongly happens before the completion of the initialization of an object with static storage duration, the call to the destructor for the object is sequenced before the call to the function passed to std​::​atexit. If a call to std​::​atexit strongly happens before another call to std​::​atexit, the call to the function passed to the second std​::​atexit call is sequenced before the call to the function passed to the first std​::​atexit call.

And defined in Data races [intro.races]/12

An evaluation A strongly happens before an evaluation D if, either

(12.1) A is sequenced before D, or

(12.2) A synchronizes with D, and both A and D are sequentially consistent atomic operations ([atomics.order]), or

(12.3) there are evaluations B and C such that A is sequenced before B, B simply happens before C, and C is sequenced before D, or

(12.4) there is an evaluation B such that A strongly happens before B, and B strongly happens before D.

[ Note: Informally, if A strongly happens before B, then A appears to be evaluated before B in all contexts. Strongly happens before excludes consume operations. — end note ]

Why was "strongly happens before" introduced? Intuitively, what's its difference and relation with "happens before"?

What does the "A appears to be evaluated before B in all contexts" in the note mean?

(Note: the motivation for this question are Peter Cordes's comments under this answer.)

Additional draft standard quote (thanks to Peter Cordes)

Order and consistency [atomics.order]/4

There is a single total order S on all memory_­order​::​seq_­cst operations, including fences, that satisfies the following constraints. First, if A and B are memory_­order​::​seq_­cst operations and A strongly happens before B, then A precedes B in S. Second, for every pair of atomic operations A and B on an object M, where A is coherence-ordered before B, the following four conditions are required to be satisfied by S:

(4.1) if A and B are both memory_­order​::​seq_­cst operations, then A precedes B in S; and

(4.2) if A is a memory_­order​::​seq_­cst operation and B happens before a memory_­order​::​seq_­cst fence Y, then A precedes Y in S; and

(4.3) if a memory_­order​::​seq_­cst fence X happens before A and B is a memory_­order​::​seq_­cst operation, then X precedes B in S; and

(4.4) if a memory_­order​::​seq_­cst fence X happens before A and B happens before a memory_­order​::​seq_­cst fence Y, then X precedes Y in S.

like image 727
curiousguy Avatar asked Nov 22 '19 01:11

curiousguy


People also ask

What is memory_ order_ seq_ cst?

Atomic operations tagged memory_order_seq_cst not only order memory the same way as release/acquire ordering (everything that happened-before a store in one thread becomes a visible side effect in the thread that did a load), but also establish a single total modification order of all atomic operations that are so ...

What is Memory_order_acquire?

memory_order_acquire: Syncs reading this atomic variable AND makes sure relaxed vars written before this are synced as well. (does this mean all atomic variables on all threads are synced?) memory_order_release: Pushes the atomic store to other threads (but only if they read the var with consume/acquire)


1 Answers

Why was "strongly happens before" introduced? Intuitively, what's its difference and relation with "happens before"?

Brace yourself for "simply happens-before" as well! Take a look at this current snapshot of cppref https://en.cppreference.com/w/cpp/atomic/memory_order

enter image description here

It seems "simply happens-before" is added in C++20.

Simply happens-before

Regardless of threads, evaluation A simply happens-before evaluation B if any of the following is true:

  1. A is sequenced-before B

  2. A synchronizes-with B

  3. A simply happens-before X, and X simply happens-before B

Note: without consume operations, simply happens-before and happens-before relations are the same.

So Simply-HB and HB are the same except for how they handle consume operations. See HB

Happens-before

Regardless of threads, evaluation A happens-before evaluation B if any of the following is true:

  1. A is sequenced-before B

  2. A inter-thread happens before B

The implementation is required to ensure that the happens-before relation is acyclic, by introducing additional synchronization if necessary (it can only be necessary if a consume operation is involved, see Batty et al)

How do they differ with regard to consume? See Inter-Thread-HB

Inter-thread happens-before

Between threads, evaluation A inter-thread happens before evaluation B if any of the following is true

  1. A synchronizes-with B

  2. A is dependency-ordered before B

  3. ...

...

An operation that is dependency ordered (i.e. uses release/consume) is HB but not necessarily Simply-HB.

Consume is more relaxed than acquire, so if I understand correctly, HB is more relaxed than Simply-HB.

Strongly happens-before

Regardless of threads, evaluation A strongly happens-before evaluation B if any of the following is true:

  1. A is sequenced-before B

  2. A synchronizes with B, and both A and B are sequentially consistent atomic operations

  3. A is sequenced-before X, X simply happens-before Y, and Y is sequenced-before B

  4. A strongly happens-before X, and X strongly happens-before B

Note: informally, if A strongly happens-before B, then A appears to be evaluated before B in all contexts.

Note: strongly happens-before excludes consume operations.

So a release/consume operation cannot be Strongly-HB.

Release/acquire can be HB and Simply-HB (because release/acquire synchronizes-with) but is not necessarily Strongly-HB. Because Strongly-HB specifically says that A must synchronize-with B AND be a Sequentially Consistent operation.

Is happens-before guaranteed?

HB Simply-HB Strongly-HB
relaxed no no no
release/consume yes no no
release/acquire yes yes no
S.C. yes yes yes

What does the "A appears to be evaluated before B in all contexts" in the note mean?

All contexts: All threads / all CPUs see (or "will eventually agree on") the same order. This is the guarantee of sequential consistency--a global total modification order of all variables. Acquire/release chains only guarantee perceived modification order for threads participating in the chain. Threads outside the chain are theoretically allowed to see a different order.

I do not know why Strongly-HB and Simply-HB were introduced. Maybe to help clarify how to operate around consume? Strongly-HB has a nice properties--if one thread observes A strongly-happens-before B, it knows all threads will observe the same thing.

The history of consume:

Paul E. McKenney is responsible for consume being in the C and C++ standards. Consume guarantees ordering between pointer assignment and the memory it points to. It was invented because of the DEC Alpha. The DEC Alpha could speculatively dereference a pointer, thus it also had a memory fence to prevent this. The DEC Alpha is no longer made and no processors today have this behavior. Consume is intended to be very relaxed.

like image 130
Humphrey Winnebago Avatar answered Oct 20 '22 17:10

Humphrey Winnebago