ISO/IEC JTC1 SC22 WG21 N2748 = 08-0258 - 2008-08-24
Lawrence Crowl, Lawrence@Crowl.org, crowl@google.com
The current C++ compare-and-exchange operation was designed for maximal efficiency in the typical case where it is used in a loop to achieve an atomic update based on the value in the atomic variable.
expected = current.load();
do desired = function(expected);
while (!current.compare_exchange(expected, desired));
To achieve good efficiency on load-locked-store-conditional machines,
the operation is defined to be spurious,
that is the operation may fail
even when the expected
value remains unchanged.
However, there are some cases, such as concurrently driving a state machine, where a compare-and-exchange without spurious failure would lead to simpler and more efficient code. Of course, one can implement a non-spurious compare-and-exchange with a spurious compare-and-exchange via a function with a loop. Unfortunately, this approach fails to take full advantage of machines that provide non-spurious compare-and-exchange in hardware.
The solution proposed is the consensus of the concurrency subgroup meeting in Sophia-Antipolis in June 2008. It is to provide both weak (spurious) and strong (non-spurious) compare-and-exchange.
We anticipate that most code that naturally wraps the compare-exchange in a loop will use the weak form while most code that naturally does not wrap the compare-exchange in a loop will use the strong form.
To make this choice clear to programmers, the concurrency subcommittee directed that neither form be syntactically simpler than the other.
The changes to the wording
involve replacing each of the existing compare_exchange
functions
with equivalent compare_exchange_weak
and compare_exchange_strong
functions.
The semantic description of the functions
changes to incorporate the distinction.
In the header <memory>
synopsis,
edit as follows.
...
template<class T> bool atomic_compare_exchange_weak(shared_ptr<T> *p, shared_ptr<T> *v, shared_ptr<T> w);
template<class T> bool atomic_compare_exchange_weak_explicit(shared_ptr<T> *p, shared_ptr<T> *v, shared_ptr<T> w, memory_order success, memory_order failure);
template<class T> bool atomic_compare_exchange_strong(shared_ptr<T> *p, shared_ptr<T> *v, shared_ptr<T> w);
template<class T> bool atomic_compare_exchange_strong_explicit(shared_ptr<T> *p, shared_ptr<T> *v, shared_ptr<T> w, memory_order success, memory_order failure);
...
Edit the description of atomic_compare_exchange
for shared_ptr
in paragraphs 17 through 22 as follows.
template<class T> bool atomic_compare_exchange_weak(shared_ptr<T> *p, shared_ptr<T> *v, shared_ptr<T> w);
Returns:
atomic_compare_exchange_weak_explicit(p, v, w, memory_order_seq_cst, memory_order_seq_cst)
.template<class T> bool atomic_compare_exchange_strong(shared_ptr<T> *p, shared_ptr<T> *v, shared_ptr<T> w);
Returns:
atomic_compare_exchange_strong_explicit(p, v, w, memory_order_seq_cst, memory_order_seq_cst)
.template<class T> bool atomic_compare_exchange_weak_explicit(shared_ptr<T> *p, shared_ptr<T> *v, shared_ptr<T> w, memory_order success, memory_order failure);
template<class T> bool atomic_compare_exchange_strong_explicit(shared_ptr<T> *p, shared_ptr<T> *v, shared_ptr<T> w, memory_order success, memory_order failure);
Requires:
failure
shall not bememory_order_release
,memory_order_acq_rel
, or stronger thansuccess
.Effects: If
*p
is equivalent to*v
, assignsw
to*p
and has synchronization semantics corresponding to to the value ofsuccess
, otherwise assigns*p
to*v
and has synchronization semantics corresponding to to the value offailure
.Returns:
true
if*p
was equivalent to*v
,false
otherwise.Throws: nothing.
Remarks: two
shared_ptr
objects are equivalent if they store the same pointer value and share ownership.Remarks: The weak forms may fail spuriously. See 29.4 [atomics.types.operations].
In the header <cstdattomic>
synopsis,
edit as follows.
...
bool atomic_compare_exchange_weak(volatile atomic_bool*, bool*, bool);
bool atomic_compare_exchange_weak_explicit(volatile atomic_bool*, bool*, bool, memory_order, memory_order);
bool atomic_compare_exchange_strong(volatile atomic_bool*, bool*, bool);
bool atomic_compare_exchange_strong_explicit(volatile atomic_bool*, bool*, bool, memory_order, memory_order);
...
bool atomic_compare_exchange_weak(volatile atomic_
itype*,
integral*,
integral);
bool atomic_compare_exchange_weak_explicit(volatile atomic_
itype*,
integral*,
integral, memory_order, memory_order);
bool atomic_compare_exchange_strong(volatile atomic_
itype*,
integral*,
integral);
bool atomic_compare_exchange_strong_explicit(volatile atomic_
itype*,
integral*,
integral, memory_order, memory_order);
...
bool atomic_compare_exchange_weak(volatile atomic_address*, void**, void*);
bool atomic_compare_exchange_weak_explicit(volatile atomic_address*, void**, void*, memory_order, memory_order);
bool atomic_compare_exchange_strong(volatile atomic_address*, void**, void*);
bool atomic_compare_exchange_strong_explicit(volatile atomic_address*, void**, void*, memory_order, memory_order);
...
In the synopsis, edit as follows.
...
bool compare_exchange_weak(bool&, bool, memory_order, memory_order) volatile;
bool compare_exchange_weak(bool&, bool, memory_order = memory_order_seq_cst) volatile;
bool compare_exchange_strong(bool&, bool, memory_order, memory_order) volatile;
bool compare_exchange_strong(bool&, bool, memory_order = memory_order_seq_cst) volatile;
...
bool atomic_compare_exchange_weak(volatile atomic_bool*, bool*, bool);
bool atomic_compare_exchange_weak_explicit(volatile atomic_bool*, bool*, bool, memory_order, memory_order);
bool atomic_compare_exchange_strong(volatile atomic_bool*, bool*, bool);
bool atomic_compare_exchange_strong_explicit(volatile atomic_bool*, bool*, bool, memory_order, memory_order);
...
bool compare_exchange_weak(
integral&,
integral, memory_order, memory_order);
bool compare_exchange_weak(
integral&,
integral, memory_order = memory_order_seq_cst);
bool compare_exchange_strong(
integral&,
integral, memory_order, memory_order);
bool compare_exchange_strong(
integral&,
integral, memory_order = memory_order_seq_cst);
...
bool atomic_compare_exchange_weak(volatile atomic_
itype*,
integral*,
integral);
bool atomic_compare_exchange_weak_explicit(volatile atomic_
itype*,
integral*,
integral, memory_order, memory_order);
bool atomic_compare_exchange_strong(volatile atomic_
itype*,
integral*,
integral);
bool atomic_compare_exchange_strong_explicit(volatile atomic_
itype*,
integral*,
integral, memory_order, memory_order);
...
In the synopsis, edit as follows.
...
bool compare_exchange_weak(void*&, void*, memory_order, memory_order) volatile;
bool compare_exchange_weak(void*&, void*, memory_order = memory_order_seq_cst) volatile;
bool compare_exchange_strong(void*&, void*, memory_order, memory_order) volatile;
bool compare_exchange_strong(void*&, void*, memory_order = memory_order_seq_cst) volatile;
...
bool atomic_compare_exchange_weak(volatile atomic_address*, void**, void*);
bool atomic_compare_exchange_weak_explicit(volatile atomic_address*, void**, void*, memory_order, memory_order);
bool atomic_compare_exchange_strong(volatile atomic_address*, void**, void*);
bool atomic_compare_exchange_strong_explicit(volatile atomic_address*, void**, void*, memory_order, memory_order);
...
In the synopsis, edit as follows.
...
bool compare_exchange_weak(T&, T, memory_order, memory_order) volatile;
bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst) volatile;
bool compare_exchange_strong(T&, T, memory_order, memory_order) volatile;
bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst) volatile;
...
bool compare_exchange_weak(T*&, T*, memory_order, memory_order) volatile;
bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst) volatile;
bool compare_exchange_strong(T*&, T*, memory_order, memory_order) volatile;
bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst) volatile;
...
Edit the compare_exchange
synopses
after paragraph 15 as follows:
bool atomic_compare_exchange_weak(volatile
A*object,
C*expected,
Cdesired);
bool atomic_compare_exchange_weak_explicit(volatile
A*object,
C*expected,
Cdesired, memory_order success, memory_order failure);
bool
A::compare_exchange_weak(
C& expected,
Cdesired, memory_order success, memory_order failure) volatile;
bool
A::compare_exchange_weak(
C& expected,
Cdesired, memory_order order = memory_order_seq_cst) volatile;
bool atomic_compare_exchange_strong(volatile
A*object,
C*expected,
Cdesired);
bool atomic_compare_exchange_strong_explicit(volatile
A*object,
C*expected,
Cdesired, memory_order success, memory_order failure);
bool
A::compare_exchange_strong(
C& expected,
Cdesired, memory_order success, memory_order failure) volatile;
bool
A::compare_exchange_strong(
C& expected,
Cdesired, memory_order order = memory_order_seq_cst) volatile;
Edit paragraph 20 as follows.
Remark: The weak compare-and-exchange operations may fail spuriously, that is, return
false
while leaving the value pointed to byexpected
unchanged. [Note: This spurious failure enables implementation of compare-and-exchange on a broader class of machines, e.g. load-locked store-conditional machines. —end note] [Example: A consequence of spurious failure is that nearly all uses of weak compare-and-exchange will be in a loop.expected = current.load(); do desired = function(expected); while (!current.compare_exchange_weak(expected, desired));
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 example]