4004. The load and store operation in §[atomics.order] p1 is ambiguous

Section: 32.5.4 [atomics.order] Status: SG1 Submitter: jim x Opened: 2023-10-30 Last modified: 2024-05-19

Priority: 3

View other active issues in [atomics.order].

View all other issues in [atomics.order].

View all issues with SG1 status.

Discussion:

32.5.4 [atomics.order] p1 says:

  1. (1.2) — memory_order::release, memory_order::acq_rel, and memory_order::seq_cst: a store operation performs a release operation on the affected memory location.

  2. (1.3) — memory_order::consume: a load operation performs a consume operation on the affected memory location. […]

  3. (1.4) — memory_order::acquire, memory_order::acq_rel, and memory_order::seq_cst: a load operation performs an acquire operation on the affected memory location.

What do the store and load operations intend to mean in this context? If there is no extra specification, it is easy to consider them as the operations performed by the non-static member functions "store" and "load" defined in the atomic class (template).

32.5.4 [atomics.order] p2 says

An atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M and takes its value from any side effect in the release sequence headed by A.

According to the above interpretation, A is an operation performed by the non-static member function store, however, I think the following example can establish the synchronization relationship

std::atomic<int> x{0};

Thread 1:
int expected = 0;
x.compare_exchange_strong(expected, 1, memory_order::release, memory_order::relaxed); // #1

Thread 2:
int expected = 1;
while(x.compare_exchange_strong(expected, 2, memory_order::acquire, memory_order::relaxed)){} // #2

Assuming the RMW operations are successful in the two threads, I think #1 intends to perform a release operation while #2 performs an acquire operation, and hence they can establish the synchronization relationship, however, they both are RMW operations.

It should be clearly defined which are store operations and which are load operations.

[2024-03-11; Reflector poll]

Set priority to 3 after reflector poll in November 2023. Ask SG1 to look.

Jonathan: "Interpreting this to only mean the store and load member functions wouldn't even be self-consistent. Could be clearer though, 6.9.2 [intro.multithread] talks about reads and writes (and RMW ops) and only uses "store" and "load" informally. Maybe just add something saying "reads are also called loads and writes are also called stores".

[2024-05-15; jim x comments and expands the discussion]

This is an addition to this issue, consider this example:

std::atomic<int> x{0};

Thread 1:
int expected = 0;
x.compare_exchange_strong(expected, 1, memory_order::acq_rel, memory_order::relaxed); // #1

Thread 2:
int expected = 1;
while(x. compare_exchange_strong(expected, 2, memory_order::acq_rel, memory_order::relaxed)){} // #2

memory_order::acq_rel performs a release operation when the operation is a store and is an acquire operation when the operation is a load. It is unclear what operations #1 and #2 are considered when they succeed, as pointed out in the original issue, we still don't specify whether RMW is classified as load or store operation or can be both.

We should specify how the success order affects the RMW when it is viewed as a load or store operation.

Proposed resolution: