dimanche 29 janvier 2017

What should be memory order for sequential load-store atomics when certain errors are acceptable

Assume that a user is twisting a knob on a MIDI controller, and the values are being sent to my program as increments and decrements to a stored value. Twisting the knob one way will send a series of decrements, their value depending upon the speed of rotation; twisting the other way increments. I want to keep the stored value (and the value emitted by the following function) to between 0 and 100. If one or a few of the messages get dropped, that's no big deal, but I don't want unexpected major changes in the value emitted by the OffsetResult_ function.

My question then is--do the following memory order directives look correct? The clearest one for me is the compare_exchange_strong. The program is using that as a store that can fail, so it seems that release memory ordering applies.

Can I even go to std::memory_order_relaxed since the major concern is just the atomaticity of the changes to currentV, rather than remembering each change to currentV?

Is there a general way to look at combined load/store functions to see whether it should be acquire, release, or sequentially consistent?

class ChannelModel {
    ChannelModel():currentV{0}{};
    int OffsetResult_(int diff) noexcept;
  private:
    atomic<int> currentV;
};

int ChannelModel::OffsetResult_(int diff) noexcept {
  int currentV = storedV.fetch_add(diff, std::memory_order_acquire) + diff;
  if (currentV < 0) {//fix storedV unless another thread has already altered it
    storedV.compare_exchange_strong(currentV, 0, std::memory_order_release, std::memory_order_relaxed);
    return 0;
  }
  if (currentV > 100) {//fix storedV unless another thread has already altered it
    storedV.compare_exchange_strong(cv, 100, std::memory_order_release, std::memory_order_relaxed);
    return 100;
  }
  return cv;
}

Note that the actual code is much more complex, and it is reasonable to believe that the response to each message from the controller will take long enough that this function will, on occasion, be called by two threads at nearly the same time.

Aucun commentaire:

Enregistrer un commentaire