Like for any scalar, volatile
applied to an atomic object guarantees that assembly code exactly follow high level C or C++ code.
(Yes, it works the same in C and C++; no, there is no language called C/C++; yes, the design of many basic blocks is by intent the same in C and C++, and that includes the semantics of atomics.)
It means that no optimization is ever applied to these operations on atomic objects, and their effect is "immediately" "visible"; note that immediately is essentially meaningless in MT programs as operations can be reordered, so there is no well defined global "clock", and visibility depends on the guarantees of the CPU. But the order of operations on volatile qualified objects, including volatile atomics, as expressed in assembly/binary code, is never changed by the compiler, and you can check in the asm output or the disassembly - but you can't check at runtime in another thread as nothing is implied by volatile qualification on how changes to objects are made visible by other threads.
A way to formalize that "no optimization" guarantee of volatile semantics is to use a stopping signal on a program or thread, and then use a debugger, or ptrace
like function, to examine particular objects; all volatile objects would have a state that is allowed by sequential execution of the paused thread, represented according to the selected ABI (when compiler flags allow for the selection of an ABI).
So volatile
only makes sense for a particular ABI; the fact that volatile
exists in C and C++ means that the part of the ABI that covers object representation is part of any standard discussion.
Calls to external, that is separately compiled functions (that means, by definition, compiled after all optimization, so that rules out global optimization), have the same level of guarantees of no optimization; they are done according to the calling convention of the ABI, and all objects that might be accessible by the called function are represented according to the object representation part of the ABI. (That's what I call an ABI boundary.) Compilers do not have to support separate compilation and a function calling ABI, but then if they don't, you can't even have both C and C++ as they are different languages based on absolutely distinct standards (with no shared references at the core language level) and the only way to mix them is at the ABI level. (There is no formal guarantee that extern "C"
actually supports linking with any C compiler currently in use; the only formal guarantee is that these "C" calling convention functions can be called from the same C++ program.)
Any time you have to interact with anything other that code compiled by the same compiler you need an ABI convention; the volatile
keyword was explicitly designed to allow interactions with a world even outside any compiler: the hardware external to the CPU world.
[Note: In C an C++ the volatile
keyword can also be used for purposes of internal communication of the program between the executing program or thread and a signal handlers; or more rarely for programs that use longjmp
which is obviously rare as it's essentially a (broken, leaking, unusable) exception system for C.]