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