I have an atomic type where I need to atomically compare it with a value, and if the two values are not equal then exchange the value of the atomic.
Put another way, where compare_exchange_strong
essentially does this operation atomically:
if (atomic_value == expected)
atomic_value = desired;
...I'm looking for a way to do this:
if (atomic_value != expected)
atomic_value = desired;
(Yes, I know compare_exchange_strong
compares using bitwise equality, not the ==
operator. And I know the value of expected
gets assigned when the comparison fails. This was just for illustration purposes. In my use case I don't need the value of the atomic regardless of the result of the comparison.)
Is there any way to do this without having to fall back on using a lock instead of std::atomic
?
CodePudding user response:
auto observed = atomic_value.load();
for (;;)
{
if (observed == expected){
break; // no exchange
}
if (atomic_value.compare_exchange_weak(observed, desired)) {
break; // successfully exchanged observed with desired
}
}
Sure it is suboptimal on architectures where HW has LL/SC, as C does not expose it. With LL/SC can have arbitrary condition.
CodePudding user response:
Just use the loop one ordinarily uses with compare-and-exchange, but instead of looping until the (new) expected value matches, loop either until it matches (and the store happens) or it equals the value in your != expected
condition, since that’s the case in which you needn’t do anything. (Obviously don’t make the initial value be that “unless” value so that you can’t “succeed” the first time.)
CodePudding user response:
You could use something like this:
#include <atomic>
#include <random>
std::atomic<int> atomVal;
int store_if_not_equal(int desired)
{
while (true) // or maxloop....
{
int expected = desired;
if (atomVal.compare_exchange_strong(expected, desired))
{
// values matched - do nothing
return 0;
}
else
{
//expected now contains the "current value"
// another thread could have sneaked in and changed it,
// so only replace it if it still matches
if (atomVal.compare_exchange_strong(expected, desired))
{
// success
return 1;
}
}
// if we arrive here, retry
}
}
int main()
{
atomVal.store(rand());
return store_if_not_equal(2);
}