50

In serial code, updating a maximum could be accomplished simply by

template<typename T>
void update_maximum(T& maximum_value, T const& value) noexcept
{
  if(value > maximum_value) maximum_value = value;
}

However, how should this be done for an atomic<T> variable holding the maximum value:

template<typename T>
void update_maximum(std::atomic<T>& maximum_value, T const& value) noexcept
{
   // want a lock-free, thread-safe implementation
}

Obviously, the code from the serial version doesn't work, as another thread may alter maximum_value between the load and the store. Can one use the compare_exchange (which compares == rather than >) to implement this? how?

Note that explicit locks are not allowed (the only lock allowed is that which may come with the implementation of std::atomic<T>).

Walter
  • 44,150
  • 20
  • 113
  • 196

1 Answers1

54

It doesn't seem to be possible in a single operation, but you can make a loop, that tries to do this, until it finally succeeds or value in atomic variable becomes bigger than value:

template<typename T>
void update_maximum(std::atomic<T>& maximum_value, T const& value) noexcept
{
    T prev_value = maximum_value;
    while(prev_value < value &&
            !maximum_value.compare_exchange_weak(prev_value, value))
        {}
}
Naszta
  • 7,560
  • 2
  • 33
  • 49
zch
  • 14,931
  • 2
  • 41
  • 49
  • 5
    @zch: shouldn't you update `prev_value` inside the loop? Otherwise if the first `compare_exchange_weak` fails, it will always be comparing to a _potentially_ "old" value and may never leave the loop. Am I right? – André Neves Apr 24 '13 at 11:45
  • 4
    @Walter, I think it is, but I didn't stress test it. @André Neves, `compare_exchange_weak` does the updating when it fails - that's why first argument is by non-const reference. – zch Apr 24 '13 at 11:50
  • 7
    Article about CAS loops in C++11: http://herbsutter.com/2012/08/31/reader-qa-how-to-write-a-cas-loop-using-stdatomics/ – zch Apr 24 '13 at 12:18
  • 5
    @AndréNeves - `compare_exchange_weak` updates `prev_value` if the comparison fails. – Pete Becker Apr 24 '13 at 13:02
  • 1
    @zch, PeteBecker: You are both right. Thanks for the correction! – André Neves Apr 24 '13 at 13:09