Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this atomic implementation of fetch_mult correct?

This site explains C++11 atomics and gives an example implementation of an atomic fetch_mult operation that is not provided by the default std::atomic<T> types:

#include <atomic>
#include <iostream>

template <typename T>
T fetch_mult(std::atomic<T>& shared, T mult){
  T oldValue= shared.load();
  // 1
  while (!shared.compare_exchange_strong(oldValue, oldValue * mult));
  return oldValue;
}

int main(){
   std::atomic<int> myInt{5};
   std::cout << myInt << std::endl;          
   fetch_mult(myInt,5);
   std::cout << myInt << std::endl;         
}

I'm having trouble understanding this function. If fetch_mult is interrupted at point // 1 by another thread that also calls fetch_mult, wouldn't one of the threads deadlock since the compare_exchange_strong will never return true unless either mult==1 or another thread sets the value back to oldValue?

For example (T1 and T2 being the respective threads):

  • T1: oldValue = 5;
  • T2: oldValue = 5;
  • T2: compare_exchange_strong successfully sets the value to 25
  • T1: compare_exchange_strong never completes successfully since its oldValue is still 5 unless somebody else sets the value to 5 again

Is my understanding correct? If so, would this be a correct implementation of fetch_mult?

template <typename T>
T fetch_mult(std::atomic<T>& shared, T mult){
  while (true) {
    T oldValue = shared.load();
    if (shared.compare_exchange_strong(oldValue, oldValue * mult)) 
      return oldValue;
  }
}
like image 579
anderas Avatar asked Aug 30 '19 07:08

anderas


1 Answers

atomic::compare_exchange_* loads the current value into the 'expected' parameter if the comparison fails. In your example, in step 4 T1 would fail the compare-exchange and load 25 into oldValue, and then would succeed on the next iteration.

like image 112
James Picone Avatar answered Nov 07 '22 13:11

James Picone