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;
};