Title
`compare_exchange_weak` writes a value on spurious failure, not memory contents
Status
immediate
Section
[atomics.ref.ops] [atomics.types.operations]
Submitter
Jonathan Wakely

Created on 2026-03-24.00:00:00 last changed 4 days ago

Messages

Date: 2026-03-27.11:35:15

Proposed resolution:

This wording is relative to N5032.

  1. Modify [atomics.ref.ops] as indicated:

    constexpr bool compare_exchange_weak(value_type& expected, value_type desired,
                                         memory_order order = memory_order::seq_cst) const noexcept;
    
    constexpr bool compare_exchange_strong(value_type& expected, value_type desired,
                                           memory_order order = memory_order::seq_cst) const noexcept;
    

    -23- Constraints: is_const_v<T> is `false`.

    -24- Preconditions: `failure` is `memory_order::relaxed`, `memory_order::acquire`, or `memory_order::seq_cst`.

    -25- Effects: Retrieves the value in `expected`. It then atomically compares the value representation of the value referenced by `*ptr` for equality with that previously retrieved from `expected`, and if `true`, replaces the value referenced by `*ptr` with that in `desired`. If and only 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 and only if the comparison is `false` then, after the atomic operation, the value in `expected` is replaced by the value read from the value referenced by `*ptr` during the atomic comparison. If the operation returns `true`, these operations are atomic read-modify-write operations ([intro.races]) on the value referenced by `*ptr`. Otherwise, these operations are atomic load operations on that memory.

    -26- Returns: The result of the comparison.

    -27- Remarks: A weak compare-and-exchange operation may fail spuriously. That is, even when the contents of memory value representations referred to by `expected` and `ptr` are compare equal, it may return `false` and store back to `expected` the same memory contents that were value representation that was originally there.

    [Note 2: 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]

  2. Modify [atomics.types.operations] as indicated:

    bool compare_exchange_weak(T& expected, T desired,
                               memory_order success, memory_order failure) volatile noexcept;
    constexpr bool compare_exchange_weak(T& expected, T desired,
                               memory_order success, memory_order failure) noexcept;
    bool compare_exchange_strong(T& expected, T desired,
                                 memory_order success, memory_order failure) volatile noexcept;
    constexpr bool compare_exchange_strong(T& expected, T desired,
                                 memory_order success, memory_order failure) noexcept;
    bool compare_exchange_weak(T& expected, T desired,
                               memory_order order = memory_order::seq_cst) volatile noexcept;
    constexpr bool compare_exchange_weak(T& expected, T desired,
                               memory_order order = memory_order::seq_cst) noexcept;
    bool compare_exchange_strong(T& expected, T desired,
                                 memory_order order = memory_order::seq_cst) volatile noexcept;
    constexpr bool compare_exchange_strong(T& expected, T desired,
                                 memory_order order = memory_order::seq_cst) noexcept;
    

    -21- Constraints: For the `volatile` overload of this function, `is_always_lock_free` is `true`.

    -22- Preconditions: `failure` is `memory_order::relaxed`, `memory_order::acquire`, or `memory_order::seq_cst`.

    -25- Effects: Retrieves the value in `expected`. It then atomically compares the value representation of the value pointed to by `this` for equality with that previously retrieved from `expected`, and if `true`, replaces the value pointed to by `this` with that in `desired`. If and only 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 and only if the comparison is `false` then, after the atomic operation, the value in `expected` is replaced by the value read from the value pointed to by `this` during the atomic comparison. If the operation returns `true`, these operations are atomic read-modify-write operations ([intro.multithread] [intro.races]) on the value pointed to by `this`. Otherwise, these operations are atomic load operations on that memory.

    -24- Returns: The result of the comparison.

    -25- [Note 4: For example, the effect of `compare_exchange_strong` on objects without padding bits ([basic.types.general]) is

    if (memcmp(this, &expected, sizeof(*this)) == 0)
      memcpy(this, &desired, sizeof(*this));
    else
      memcpy(&expected, this, sizeof(*this));
    
    end note]

    [Example 1: 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]

    [Example 2: Because the expected value is updated only on failure, code releasing the memory containing the expected value on success will work. For example, list head insertion will act atomically and would not introduce a data race in the following code:

    do {
      p->next = head;                                   // make new list node point to the current head
    } while (!head.compare_exchange_weak(p->next, p));  // try to insert
    
    end example]

    -26- Recommended practice: 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.

    -27- Remarks: A weak compare-and-exchange operation may fail spuriously. That is, even when the contents of memory value representations referred to by `expected` and `this` are compare equal, it may return `false` and store back to `expected` the same memory contents that were value representation that was originally there.

    [Note 2: 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]

Date: 2026-03-27.11:35:15

[ Croydon 2026-03-27; move to Immediate. ]

Date: 2026-03-27.11:35:15

[ Croydon 2026-03-27; SG1 approved the change. ]

Date: 2026-03-24.14:32:06

[ Croydon 2026-03-24; Set priority to 2 and send to SG1. ]

Date: 2026-03-24.00:00:00

[atomic.ref.ops] says:

-27- Remarks: A weak compare-and-exchange operation may fail spuriously. That is, even when the contents of memory referred to by `expected` and `ptr` are equal, it may return `false` and store back to `expected` the same memory contents that were originally there.
I think this is missing an update from p0528r3 which should have changed "the same memory contents" to something like "the same value". When the comparison succeeds but the weak CAS fails anyway, the bits of `exchange` are replaced by the bits copied from `*ptr`. Those bits might differ from the original bits of `expected` in their padding.

History
Date User Action Args
2026-03-27 11:35:15adminsetmessages: + msg16115
2026-03-27 11:35:15adminsetmessages: + msg16114
2026-03-27 11:35:15adminsetstatus: open -> immediate
2026-03-24 14:32:06adminsetmessages: + msg16050
2026-03-24 14:32:06adminsetstatus: new -> open
2026-03-24 13:15:12adminsetmessages: + msg16048
2026-03-24 00:00:00admincreate