Why is the volatile qualifier used through out std::atomic?
From what I've read from Herb Sutter and others you would think that volatile
and concurrent programming were completely orthogonal concepts, at least as far as C/C++ are concerned.
However, in GCC implementation all of std::atomic
's member functions have the volatile
qualifier. The same is true in Anthony Williams's implementation of std::atomic
.
So what's deal, do my atomic<>
variables need be volatile
or not?
Solution 1:
To summarize what others have correctly written:
C/C++ volatile
is for hardware access and interrupts. C++11 atomic<>
is for inter-thread communication (e.g., in lock-free code). Those two concepts/uses are orthogonal, but they have overlapping requirements and that is why people have often confused the two.
The reason that atomic<>
has volatile-qualified functions is the same reason it has const-qualified functions, because it's possible in principle for an object be both atomic<>
and also const
and/or volatile
.
Of course, as my article pointed out, a further source of confusion is that C/C++ volatile
isn't the same as C#/Java volatile
(the latter is basically equivalent to C++11 atomic<>
).
Solution 2:
Why is the volatile
qualifier used throughout std::atomic
?
So that volatile objects can also be atomic. See here:
The relevant quote is
The functions and operations are defined to work with volatile objects, so that variables that should be volatile can also be atomic. The volatile qualifier, however, is not required for atomicity.
Do my atomic<>
variables need to be volatile
or not?
No, atomic objects don't have to be volatile.
Solution 3:
As const, volatile is transitive. If you declare a method as volatile
then you cannot call any non-volatile method on it or any of its member attributes. By having std::atomic
methods volatile
you allow calls from volatile
member methods in classes that contain the std::atomic
variables.
I am not having a good day... so confusing... maybe a little example helps:
struct element {
void op1() volatile;
void op2();
};
struct container {
void foo() volatile {
e.op1(); // correct
//e.op2(); // compile time error
}
element e;
};