quote from https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
bool compare_exchange_weak( T& expected, T desired, std::memory_order success, std::memory_order failure ) noexcept;
Atomically compares the object representation (until C 20)value representation (since C 20) of *this with that of expected, and if those are bitwise-equal, replaces the former with desired (performs read-modify-write operation). Otherwise, loads the actual value stored in *this into expected (performs load operation).
I'm having a hard time understanding the word Atomically mentioned above. Because expected
and desired
are all of type T, so read & write operation on these values are not atomic, if underlying atomic value and expected
are not equal, Is the operation of loading *this
to expected
also atomic? or does Atomically only applies to operation related to underlying atomic value?
CodePudding user response:
Naively, the
compares the [representation] of
*this
with that of expected
and
replaces the former with desired
could be two separate operations that happen sequentially. One could imagine that it is possible that another thread writes a value after compares but before replaces into *this
so that the second operation, replaces, overwrites the value that was just written by the other thread, which is now lost.
Atomically means that this cannot happen, i.e., that the two operations are actually only one uninterruptible operation.
CodePudding user response:
Because
expected
anddesired
are all of type T, so read & write operation on these values are not atomic
That's true. The load of expected
(and the store, if it happens) is not an atomic operation. Therefore, if this call to compare_exchange_weak
is potentially concurrent with any other operation that accesses expected
, the program has a race condition (unless both operations are only reads). The load of desired
is also not an atomic operation, although this doesn't matter because it's a local variable in the compare_exchange_weak
function (no other thread can see it).
However, the operation on *this
is atomic. This means that it will not race with any other potentially concurrent atomic operation on *this
; if there are two potentially concurrent atomic operations on *this
, the behaviour will be as if one thread or the other "gets there first", without causing any undefined behaviour. Furthermore, it will be either an atomic read-modify-write operation (on success) or an atomic load operation (on failure). An atomic read-modify-write operation has the additional constraint that the entry that it creates in the atomic variable's modification order immediately follows the entry from which the read took its value (no other modifications can "intervene").