29 Atomic operations library [atomics]

29.6 Operations on atomic types [atomics.types.operations]

29.6.5 Requirements for operations on atomic types [atomics.types.operations.req]

There are only a few kinds of operations on atomic types, though there are many instances on those kinds. This section specifies each general kind. The specific instances are defined in [atomics.types.generic], [atomics.types.operations.general], [atomics.types.operations.arith], and [atomics.types.operations.pointer].

In the following operation definitions:

  • an A refers to one of the atomic types.

  • a C refers to its corresponding non-atomic type. The atomic_address atomic type corresponds to the void* non-atomic type.

  • an M refers to type of the other argument for arithmetic operations. For integral atomic types, M is C. For atomic address types, M is std::ptrdiff_t.

  • the free functions not ending in _explicit have the semantics of their corresponding _explicit with memory_order arguments of memory_order_seq_cst.

Note: Many operations are volatile-qualified. The “volatile as device register” semantics have not changed in the standard. This qualification means that volatility is preserved when applying these operations to volatile objects. It does not mean that operations on non-volatile objects become volatile. Thus, volatile qualified operations on non-volatile objects may be merged under some conditions.  — end note ]

A::A() noexcept = default;

Effects: leaves the atomic object in an uninitialized state. [ Note: These semantics ensure compatibility with C.  — end note ]

constexpr A::A(C desired) noexcept;

Effects: Initializes the object with the value desired. Initialization is not an atomic operation ([intro.multithread]). [ Note: it is possible to have an access to an atomic object A race with its construction, for example by communicating the address of the just-constructed object A to another thread via memory_order_relaxed operations on a suitable atomic pointer variable, and then immediately accessing A in the receiving thread. This results in undefined behavior.  — end note ]

#define ATOMIC_VAR_INIT(value) see below

The macro expands to a token sequence suitable for constant initialization of an atomic variable of static storage duration of a type that is initialization-compatible with value. [ Note: This operation may need to initialize locks.  — end note ] Concurrent access to the variable being initialized, even via an atomic operation, constitutes a data race. [ Example:

atomic<int> v = ATOMIC_VAR_INIT(5);

 — end example ]

bool atomic_is_lock_free(const volatile A *object) noexcept; bool atomic_is_lock_free(const A *object) noexcept; bool A::is_lock_free() const volatile noexcept; bool A::is_lock_free() const noexcept;

Returns: True if the object's operations are lock-free, false otherwise.

void atomic_init(volatile A *object, C desired) noexcept; void atomic_init(A *object, C desired) noexcept;

Effects: Non-atomically initializes *object with value desired. This function shall only be applied to objects that have been default constructed, and then only once. [ Note: These semantics ensure compatibility with C.  — end note ] [ Note: Concurrent access from another thread, even via an atomic operation, constitutes a data race.  — end note ]

void atomic_store(volatile A* object, C desired) noexcept; void atomic_store(A* object, C desired) noexcept; void atomic_store_explicit(volatile A *object, C desired, memory_order order) noexcept; void atomic_store_explicit(A* object, C desired, memory_order order) noexcept; void A::store(C desired, memory_order order = memory_order_seq_cst) volatile noexcept; void A::store(C desired, memory_order order = memory_order_seq_cst) noexcept;

Requires: The order argument shall not be memory_order_consume, memory_order_acquire, nor memory_order_acq_rel.

Effects: Atomically replaces the value pointed to by object or by this with the value of desired. Memory is affected according to the value of order.

C A::operator=(C desired) volatile noexcept; C A::operator=(C desired) noexcept;

Effects: store(desired)

Returns: desired

C atomic_load(const volatile A* object) noexcept; C atomic_load(const A* object) noexcept; C atomic_load_explicit(const volatile A* object, memory_order) noexcept; C atomic_load_explicit(const A* object, memory_order) noexcept; C A::load(memory_order order = memory_order_seq_cst) const volatile noexcept; C A::load(memory_order order = memory_order_seq_cst) const noexcept;

Requires: The order argument shall not be memory_order_release nor memory_order_acq_rel.

Effects: Memory is affected according to the value of order.

Returns: Atomically returns the value pointed to by object or by this.

A::operator C() const volatile noexcept; A::operator C() const noexcept;

Effects: load()

Returns: The result of load().

C atomic_exchange(volatile A* object, C desired) noexcept; C atomic_exchange(A* object, C desired) noexcept; C atomic_exchange_explicit(volatile A* object, C desired, memory_order) noexcept; C atomic_exchange_explicit(A* object, C desired, memory_order) noexcept; C A::exchange(C desired, memory_order order = memory_order_seq_cst) volatile noexcept; C A::exchange(C desired, memory_order order = memory_order_seq_cst) noexcept;

Effects: Atomically replaces the value pointed to by object or by this with desired. Memory is affected according to the value of order. These operations are atomic read-modify-write operations ([intro.multithread]).

Returns: Atomically returns the value pointed to by object or by this immediately before the effects.

bool atomic_compare_exchange_weak(volatile A* object, C* expected, C desired) noexcept; bool atomic_compare_exchange_weak(A* object, C* expected, C desired) noexcept; bool atomic_compare_exchange_strong(volatile A* object, C* expected, C desired) noexcept; bool atomic_compare_exchange_strong(A* object, C* expected, C desired) noexcept; bool atomic_compare_exchange_weak_explicit(volatile A* object, C* expected, C desired, memory_order success, memory_order failure) noexcept; bool atomic_compare_exchange_weak_explicit(A* object, C* expected, C desired, memory_order success, memory_order failure) noexcept; bool atomic_compare_exchange_strong_explicit(volatile A* object, C* expected, C desired, memory_order success, memory_order failure) noexcept; bool atomic_compare_exchange_strong_explicit(A* object, C* expected, C desired, memory_order success, memory_order failure) noexcept; bool A::compare_exchange_weak(C& expected, C desired, memory_order success, memory_order failure) volatile noexcept; bool A::compare_exchange_weak(C& expected, C desired, memory_order success, memory_order failure) noexcept; bool A::compare_exchange_strong(C& expected, C desired, memory_order success, memory_order failure) volatile noexcept; bool A::compare_exchange_strong(C& expected, C desired, memory_order success, memory_order failure) noexcept; bool A::compare_exchange_weak(C& expected, C desired, memory_order order = memory_order_seq_cst) volatile noexcept; bool A::compare_exchange_weak(C& expected, C desired, memory_order order = memory_order_seq_cst) noexcept; bool A::compare_exchange_strong(C& expected, C desired, memory_order order = memory_order_seq_cst) volatile noexcept; bool A::compare_exchange_strong(C& expected, C desired, memory_order order = memory_order_seq_cst) noexcept;

Requires: The failure argument shall not be memory_order_release nor memory_order_acq_rel. The failure argument shall be no stronger than the success argument.

Effects: Atomically, compares the contents of the memory pointed to by object or by this for equality with that in expected, and if true, replaces the contents of the memory pointed to by object or by this with that in desired, and if false, updates the contents of the memory in expected with the contents of the memory pointed to by object or by this. Further, if the comparison is true, memory is affected according to the value of success, and if the comparison is false, memory is affected according to the value of failure. When only one memory_order argument is supplied, the value of success is order, and the value of failure is order except that a value of memory_order_acq_rel shall be replaced by the value memory_order_acquire and a value of memory_order_release shall be replaced by the value memory_order_relaxed. If the operation returns true, these operations are atomic read-modify-write operations ([intro.multithread]). Otherwise, these operations are atomic load operations.

Returns: The result of the comparison.

Note: For example, the effect of atomic_compare_exchange_strong is

if (memcmp(object, expected, sizeof(*object)) == 0)
  memcpy(object, &desired, sizeof(*object));
else
  memcpy(expected, object, sizeof(*object));

 — end note ] [ Example: the expected use of the compare-and-exchange operations is as follows. The compare-and-exchange operations will update expected when another iteration of the loop is needed.

expected = current.load();
do {
  desired = function(expected);
} while (!current.compare_exchange_weak(expected, desired));

 — end example ]

Implementations should ensure that weak compare-and-exchange operations do not consistently return false unless either the atomic object has value different from expected or there are concurrent modifications to the atomic object.

Remark: A weak compare-and-exchange operation may fail spuriously. That is, even when the contents of memory referred to by expected and object are equal, it may return false and store back to expected the same memory contents that were originally there. [ Note: This spurious failure enables implementation of compare-and-exchange on a broader class of machines, e.g., load-locked store-conditional machines. A consequence of spurious failure is that nearly all uses of weak compare-and-exchange will be in a loop.

When a compare-and-exchange is in a loop, the weak version will yield better performance on some platforms. When a weak compare-and-exchange would require a loop and a strong one would not, the strong one is preferable.  — end note ]

Note: The memcpy and memcmp semantics of the compare-and-exchange operations may result in failed comparisons for values that compare equal with operator== if the underlying type has padding bits, trap bits, or alternate representations of the same value. Thus, compare_exchange_strong should be used with extreme care. On the other hand, compare_exchange_weak should converge rapidly.  — end note ]

The following operations perform arithmetic computations. The key, operator, and computation correspondence is:

Table 147 — Atomic arithmetic computations
Key Op Computation Key Op Computation
add + addition sub - subtraction
or | bitwise inclusive or xor ^ bitwise exclusive or
and & bitwise and

C atomic_fetch_key(volatile A *object, M operand) noexcept; C atomic_fetch_key(A* object, M operand) noexcept; C atomic_fetch_key_explicit(volatile A *object, M operand, memory_order order) noexcept; C atomic_fetch_key_explicit(A* object, M operand, memory_order order) noexcept; C A::fetch_key(M operand, memory_order order = memory_order_seq_cst) volatile noexcept; C A::fetch_key(M operand, memory_order order = memory_order_seq_cst) noexcept;

Effects: Atomically replaces the value pointed to by object or by this with the result of the computation applied to the value pointed to by object or by this and the given operand. Memory is affected according to the value of order. These operations are atomic read-modify-write operations ([intro.multithread]).

Returns: Atomically, the value pointed to by object or by this immediately before the effects.

Remark: For signed integer types, arithmetic is defined to use two's complement representation. There are no undefined results. For address types, the result may be an undefined address, but the operations otherwise have no undefined behavior.

C A::operator op=(M operand) volatile noexcept; C A::operator op=(M operand) noexcept;

Effects: fetch_key(operand)

Returns: fetch_key(operand) op operand

C A::operator++(int) volatile noexcept; C A::operator++(int) noexcept;

Returns: fetch_add(1)

C A::operator--(int) volatile noexcept; C A::operator--(int) noexcept;

Returns: fetch_sub(1)

C A::operator++() volatile noexcept; C A::operator++() noexcept;

Effects: fetch_add(1)

Returns: fetch_add(1) + 1

C A::operator--() volatile noexcept; C A::operator--() noexcept;

Effects: fetch_sub(1)

Returns: fetch_sub(1) - 1