This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Immediate status.

4552. compare_exchange_weak writes a value on spurious failure, not memory contents

Section: 32.5.7.2 [atomics.ref.ops], 32.5.8.2 [atomics.types.operations] Status: Immediate Submitter: Jonathan Wakely Opened: 2026-03-24 Last modified: 2026-03-27

Priority: 3

View other active issues in [atomics.ref.ops].

View all other issues in [atomics.ref.ops].

View all issues with Immediate status.

Discussion:

[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.

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

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

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

Proposed resolution:

This wording is relative to N5032.

  1. Modify 32.5.7.2 [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 (6.10.2.2 [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 32.5.8.2 [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 (6.10.2 [intro.multithread] 6.10.2.2 [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 (6.9.1 [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]