Reputation: 663
I've read https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
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).
So as I understand the code like
bool expected=true;
extern atomic<bool> b = false;
void foo ()
{
//
while(!b.compare_exchange_weak(expected, false));
//
}
after the loop will run once(ignoring spurious failure) it will fail, and will write to expected false
, so on the second iteration compare_exchange_weak will return success, though b
hasn't been changes to true. But what's the point of all this? I though I could use this as a lock for synchronization, waiting for other thread to change b
, but now I can't think of a usage of this.
Example from the cppreference also shows that after two calls compare_exchange_strong will succeed.
#include <atomic>
#include <iostream>
std::atomic<int> ai;
int tst_val= 4;
int new_val= 5;
bool exchanged= false;
void valsout()
{
std::cout << "ai= " << ai
<< " tst_val= " << tst_val
<< " new_val= " << new_val
<< " exchanged= " << std::boolalpha << exchanged
<< "\n";
}
int main()
{
ai= 3;
valsout();
// tst_val != ai ==> tst_val is modified
exchanged= ai.compare_exchange_strong( tst_val, new_val );
valsout();
// tst_val == ai ==> ai is modified
exchanged= ai.compare_exchange_strong( tst_val, new_val );
valsout();
}
Result:
ai= 3 tst_val= 4 new_val= 5 exchanged= false
ai= 3 tst_val= 3 new_val= 5 exchanged= false
ai= 5 tst_val= 3 new_val= 5 exchanged= true
Upvotes: 6
Views: 1407
Reputation: 63704
std::atomic::compare_exchange_weak
performs this English-language task, in a thread-aware way:
Because the variable holds
expected
, it should now holddesired
.
As a trivial task, imagine that the value of your std::atomic<int> x
should be squared. But other threads may be modifying it, so you can't simply read the value, square it and write the new value back. The value may have changed after you read it!
Here is a thread-safe way to perform this task. You are guaranteed that the stored value will be replaced with its square.
int expected = x;
while( !x.compare_exchange_weak(expected, expected*expected) ) {}
This code will atomically replace expected
with its square unless the value has changed.
If the value has changed, expected
is now updated with the new value and the code tries again.
Upvotes: 8
Reputation: 37512
I will give an example where I did used that, since it is very simple one.
I had atomic which describes available size of something. There was a danger of integer overflow, so I had do checking first before subtracting a value.
Not exact copy paste of my production code:
class LockFreeCuncuretSizeLimit {
public:
explicit LockFreeCuncuretSizeLimit(size_t available) : mSize{available}
{}
bool tryAcuqire(size_t s) {
size_t lastSize = mSize;
size_t newSize;
do
{
if (lastSize >= s)
{
newSize = lastSize - s;
}
else
{
return false;
}
}
while (!mSize.compare_exchange_weak(lastSize, newSize));
return true;
}
void release(size_t s) {
mSize += s; // here I do not have worry about integer overflow
}
private:
std::atomic<size_t> mSize;
};
Now try image do that without compare_exchange_strong
and not having a race condition.
There is a change that condition is meet, but when subtraction is done on atomic some other thread already subtracted a value so when I do actual subtraction it is possible to overflow integer. So this cant be done without compare_exchange_strong
.
Now difference between compare_exchange_strong
and compare_exchange_weak
is hard to explain. Even Herb Sutter on some cppcon talk give up explaining that and provided simple rule: "if you need loop use compare_exchange_weak
otherwise use compare_exchange_strong
".
Upvotes: 4