74

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?

VLL
  • 9,634
  • 1
  • 29
  • 54
deft_code
  • 57,255
  • 29
  • 141
  • 224
  • +1 Mr. Williams is here on SO, maybe he can show up and give an answer :) – Khaled Alshaya Mar 19 '10 at 16:55
  • 1
    I've seen a question on comp.std.c++ about that. Remember that `volatile` guarants that reads and writes in a single thread are done in order and that a volatile object cannot have any non-volatile member functions being called on it (just like const). But further than that, i have no clue about threads in C++. Everytime i try to read about it in the Standard, i'm starting to give up, not being able to grasp the sheer amount of indirections and logics in the text xD – Johannes Schaub - litb Mar 19 '10 at 17:08

3 Answers3

81

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<>).

KayEss
  • 2,290
  • 15
  • 31
Herb Sutter
  • 2,599
  • 1
  • 15
  • 6
  • 2
    I am going to abuse the fact that you are here to ask your opinion about an article by Alexandrescu on using the `volatile` flag to produce compile-time errors on thread-unsafe code (using `volatile` instances to lock the use of the interface and `const_cast` to remove the volatile when a mutex is acquire). Could it make sense adding a type qualifier 'threadsafe' or the like for this purpose in the language (I am just thinking out loud) The article is here: http://www.drdobbs.com/cpp/184403766;jsessionid=OEWBPI10M2IQLQE1GHPCKHWATMY32JVN – David Rodríguez - dribeas Mar 22 '10 at 10:27
  • 10
    In some of Andrei's articles, what he was really doing was to hijack (er, I mean, "reuse") the volatile keyword as a handy mostly-unused tag in the type system he could use as a hook to overload and get other effects, which was a little confusing because it wasn't stated quite that way. – Herb Sutter Mar 23 '10 at 14:10
  • 1
    See also Dr. Alexandrescu's (I'm not on a first name basis) follow-up comments and not-quite-retractions on that volatile article (as it were): http://www.drdobbs.com/generic-min-and-max-redivivus/184403774 – metal Jan 14 '13 at 14:33
59

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.

stephan
  • 10,104
  • 1
  • 51
  • 64
  • Note: This is technically same as platform specific http://stackoverflow.com/questions/3708160/what-is-the-effect-of-interlockedincrement-argument-declared-as-volatile – Suma Nov 22 '10 at 10:54
  • The volatile qualifier is used to prevent reordering. That's what it does. – Michaël Roy Aug 31 '17 at 04:25
  • 4
    @MichaëlRoy: `volatile` operations are only ordered wrt. other `volatile` accesses. `atomic` release, acquire, and seq_cst ops are ordered wrt. plain non-atomic variables, so rolling your own atomics with *just* `volatile` can't give you the same acq/rel semantics without barriers. `volatile atomic` may / will be useful in the future [when compilers take advantage of the as-if rule to optimize atomics](https://stackoverflow.com/questions/45960387/why-dont-compilers-merge-redundant-stdatomic-writes), e.g. to prevent merging "redundant" writes to a progress-counter. – Peter Cordes Jul 18 '18 at 06:49
  • @PeterCordes I agree. Using the bare volatile keyword is only safe in a single threaded environment, with interrupts. It does prevent reordering of operations for optimization by the compiler, _not_ by the CPU. I should have specified. edit: the bare volatile keyword can be used safely on single core CPUs in a multithreaded environment. But we're seeing fewer and fewer of this kind of setup. – Michaël Roy Jul 27 '18 at 15:27
  • 1
    @MichaëlRoy: it's technically still UB except for `volatile sig_atomic_t`, but most real implementations have `int` that's naturally atomic for loads/stores (defining the behaviour). (I recently wrote a big answer about this: [MCU programming - C++ O2 optimization breaks while loop](https://electronics.stackexchange.com/q/387181)) Still, if you want any ordering wrt. non-atomic variables (e.g. interrupt handler writes a normal buffer then sets an atomic flag), you can use a relaxed atomic load and `atomic_signal_fence(memory_order_acquire)` in the main code to prevent compile-time reordering. – Peter Cordes Jul 27 '18 at 15:51
  • @PeterCordes The volatile keyword only affects code generated by the compiler. It defeats read and write optimizations that would use registers instead of the actual value stored in the data segment. There are no special instruction generated to the cpu. It was never intended for multiple CPU cores. It works very well on single core CPUs, though. As intended when it was added to the language, sometime in the 70's. You should notice that I did not talk about fencing, a concept that does not exist in single core CPUs. – Michaël Roy Jul 27 '18 at 18:46
  • 1
    @MichaëlRoy: Yeah, I know exactly what `volatile` does on real compilers. But the set of things you can safely do for interaction between a signal or interrupt handler is *very* limited, and it doesn't help for types too wide to be naturally atomic. But as long as you stick to that, yes it works. As I showed in the link in my previous comment, you can use C++11 relaxed atomics with `signal_fence` to do everything that `volatile` can for that use-case exactly as efficiently, and with the option of release/acquire ordering using `signalf_fence`. – Peter Cordes Jul 27 '18 at 18:52
15

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;
};
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489