Ehhhhhh…
> The semantics of volatile were never clearly defined…
Well, the problem is that volatile was defined, more or less. It means that you get load and store instructions in machine code that correspond to the appearance of loads and stores in the source code. Importantly, this does not imply anything about the memory model on multiprocessor systems.
So "volatile" was never supposed to be used for threading. What you can use it for is:
- Memory-mapped I/O
- Values that are changed in interrupts
- Values changed on other threads on x86 if you just don’t give a damn
- longjmp/setjmp
- Values changed on other threads on Itanium
What happens when you don’t use “volatile” is that the compiler will do dead store elimination and optimize out loads. For example,
// Global;
int x = 0;
void wait_for_x(void) { while (x == 0) {} }
If `x` is modified by an interrupt, it should be marked `volatile` because otherwise the compiler will helpfully optimize out the test in the loop.What people actually did for lock-free programming in the pre-C++-11 days was to use those non-portable intrinsics or inline assembler. If someone used “volatile”, it was usually out of ignorance.