Why does volatile int64 not data race?


#1

In the Juce audio playback demo there is a Component with a timerCallback() which loop-updates a playcursor:

void updateCursorPosition()
{
    ...
    transportSource.getCurrentPosition())
    ... (draws the play cursor in message thread)
}

transportSource is actually a AudioFormatReaderSource, which holds the current playcursor like this:

class JUCE_API  AudioFormatReaderSource  : public PositionableAudioSource
{
...
int64 volatile nextPlayPos;
...
}

So we have the audio thread and gui thread accessing nextPlayPos concurrently, with both threads modifying and reading. volatile only prevents some optimizations but does not eliminate data races.

To my knowledge 64 bit primitive operations (on double, int64, etc.) are atomic on x86_64, but only if the data is aligned.
Is this already the explanation to why it does not data race? If yes: What guarantees can we expect from compilers regarding alignment?


#2

I can see how volatile might make something work that didn’t work before, but I’m not sure it’s the right solution. It could theoretically data race.

Using std::atomic with relaxed memory ordering would be appropriate. This would ensure that on the 32-bit build things are sound. There’s a danger you’ll get two separate MOVs otherwise. It’s probably not seen as an issue because it rarely gets large enough to need the additional 32-bits, and when it does there’s only going to be one instance in a blue moon where the split MOVL instructions could possibly be hit as a data race.

PS. There is zero overhead from using std::atomic on the 64bit system, it comes to a single MOV instruction as long as you are using the relaxed/acquire or release orderings.

(I think you’ll find that the int64 is always properly aligned though I don’t know where that’s specified as a requirement…and anyway specifying the no-overhead std::atomic will ensure the right thing is done whatever, and makes it super clear this is a shared variable)


#3

The JUCE classes like AudioFormatReaderSource don not use std::atomic for the int64 nextPlayPosition, but in the Demo there is concurrent access from GUI and audio thread.

This means that either Jules et. al. do not see it as an issue because it almost never data races (like you mentioned), or they did not fix it yet (unlikely, I would say).
Or there is sth. I missed.


#4

bazrush is totally right about this - we really should change it to be atomic in the AudioFormatReaderSource class, although in practice it’ll probably never actually cause a problem, and even it the data did tear, the worst possible outcome is likely to just be a flicker in the play cursor or something trivial.

In demo code, we’re much less strict about this though, as beginners find it enough of a struggle to understand what’s going on without us boggling their mind with “atomic” things!


#5

Not sure if I agree with you Jules, on your last remark.
I think it is good for beginners to get things right from the beginning. For me that includes topics such as “atomics”.
Therefore I think you could use atomic also in the demo code.


#6

Fair point. Normally when this subject comes up, we just direct people to Timur’s conference talks on lock-free audio threading…