Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should std::atomic_compare_exchange_strong be used?

There are two atomic CAS operations in C++11: atomic_compare_exchange_weak and atomic_compare_exchange_strong.

According to cppreference:

The weak forms of the functions are allowed to fail spuriously, that is, act as if *obj != *expected even if they are equal. 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.

The following is an example for using the weak version, I think:

do {
    expected = current.value();
    desired = f(expected);
} while (!current.atomic_compare_exchange_weak(expected, desired));

Could someone give an example where the compare-and-exchange is not in a loop so that the strong version is preferable?

like image 498
user571470 Avatar asked Jul 29 '13 01:07

user571470


1 Answers

The atomic_compare_exchange_XXX functions update their "expected" argument with the observed value, so your loop is the same as:

expected = current;
do {
    desired = f(expected);
} while (!current.atomic_compare_exchange_weak(expected, desired));

If the desired value is independent of the expected value, this loop becomes:

desired = ...;
expected = current;
while (current.atomic_compare_exchange_weak(expected, desired))
  ;

Let's add some semantics. Let's say that several threads are running this simultaneously. In each case desired is a non-zero ID for the current thread, and current is used to provide mutual exclusion to ensure that some thread performs a cleanup task. We don't really care which one, but we want to be sure that some thread gets access (and maybe other threads can observe the winner by reading it's ID from current).

We can achieve the desired semantics with:

expected = 0;
if (current.atomic_compare_exchange_strong(expected, this_thread)) {
  // I'm the winner
  do_some_cleanup_thing();
  current = 0;
} else {
  std::cout << expected << " is the winner\n";
}

This is a case where atomic_compare_exchange_weak would require a loop to accomplish the same effect as atomic_compare_exchange_strong, since spurious failures are possible:

expected = 0;
while(!current.atomic_compare_exchange_weak(expected, this_thread)
       && expected == 0))
  ;
if (expected == this_thread) {
  do_some_cleanup_thing();
  current = 0;
} else {
  std::cout << expected << " is the winner\n";
}

The standard suggests that implementations may provide more efficient code in this case for atomic_compare_exchange_strong than looping with ..._weak (§29.6.5/25 [atomics.types.operations.req]).

like image 150
Casey Avatar answered Dec 05 '22 21:12

Casey