| > Unfortunately "allows" here is telling us about the programmers not the hardware. The programmers see this and figure eh, I don't really understand this but somebody wrote "volatile" so I guess they knew what they were doing. So... you're saying that whether a piece of code is correct is something that's more complicated than just noticing that it has the word "volatile" written somewhere? This is super obvious. > C++ 98 doesn't provide standardized atomics, so you would need to find out on each target what (if anything) you're required to do to get atomic behaviour. Yes. But you don't need all of your operations to be atomic in order to get atomic behavior for an operation. It turns out that for a common ring buffer, with one producer and one consumer, you only need the read/write position to be read/written atomically, and operations on buffer data must be ordered wrt. operations on the read/write positions. This is commonly achieved with "volatile" on single-core systems. > This keyword does not mean "I want a Sequentially Consistent memory model across all my code, except somehow still very fast". That's not a thing. You get ordering of the volatile operations with respect to other volatile operations, from the perspective of one CPU core. That is sometimes all you need. The point that "some people don't understand what volatile means" is not germane. > Volatile accesses for things that are clearly just RAM are a code smell. As a result the volatile keyword in C and C++ is usually a code smell. This is a quite extreme viewpoint. I can't agree with it. Volatile is, yes, overused and abused. Or at least it was. However, if you want to write a ring buffer and use it on a single-core processor in an embedded environment, you can write the whole thing in old-school C90 or C++98, and the only real question you have about your environment is whether the operations on read/write pointers will tear. It is rare, at the very least, to find a CPU where reads and writes to an int will tear. |
But again, putting a volatile modifier on the read/write position did not achieve this.
Most of what you thought you wanted "volatile" for here is either just always the behaviour for aligned primitive types anyway (on platforms like x86 with a relatively strong consistency guarantee at a low level) or still unsafe even after you sprinkled volatile around (on ARM platforms with relaxed memory access rules, unless your compiler specifically gives different behaviour for volatile)
> This is a quite extreme viewpoint. I can't agree with it.
It's the conventional viewpoint by now, even the C++ Core Guidelines explicitly tell you not to do this. e.g. CP200
But if you want an extreme viewpoint not yet widely accepted by say the C++ Standards Committee, try this:
Neither volatile nor atomic should exist as type qualifiers. A "volatile 32-bit integer" isn't a thing, and neither is an "atomic 32-bit integer". The operations exist and they're important in low-level programming, but the types are a fiction‡.
Rust's model here is much closer. You can do volatile memory access in Rust, but you can't have "volatile" types, they're not a thing. You must explicitly call an (inlined) function to read whatever primitive data you need out of a memory address or write it to the address. As a result, programmers who need volatile memory access are far more likely to remain clear about what they're doing and why, not just sprinkling "volatile" keywords and hoping it'll do what they meant.
‡ If you want some real C++ horror, why is volatile constexpr a thing? Literally the committee's excuse is maybe this could be useful. They don't have a concrete example of use, but hey, why not throw things we don't need into the standard, it's not as though C++ is a bloated language with far too many edge cases already...