My understanding of the semantics of volatile
in C and C++ is that it turns memory access into (observable) side effects. Whenever reading or writing to a memory mapped file (or shared memory) I would expect the the pointer to be volatile qualified, to indicate that this is in fact I/O. (John Regehr wrote a very good article on the semantics of volatile
).
Furthermore, I would expect using functions like memcpy()
to access shared memory to be incorrect, since the signature suggests the volatile qualification is cast away, and the memory access not be treated as I/O.
In my mind, this is an argument in favor of std::copy()
, where the volatile qualifier won't be cast away, and memory accesses being correctly treated as I/O.
However, my experience of using pointers to volatile objects and std::copy()
to access memory mapped files is that it's orders of magnitude slower than just using memcpy()
. I am tempted to conclude that perhaps clang and GCC are overly conservative in their treatment of volatile
. Is that the case?
What guidance is there for accessing shared memory with regards to volatile
, if I want to follow the letter of the standard and have it back the semantics I rely on?
Relevant quote from the standard [intro.execution] §14:
Reading an object designated by a volatile glvalue, modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression (or a subexpression) in general includes both value computations (including determining the identity of an object for glvalue evaluation and fetching a value previously assigned to an object for prvalue evaluation) and initiation of side effects. When a call to a library I/O function returns or an access through a volatile glvalue is evaluated the side effect is considered complete, even though some external actions implied by the call (such as the I/O itself) or by the volatile access may not have completed yet.