9
votes

Where does the standard define that a volatile variable can change undetected?

I've found two normative text which are about volatile:

intro.execution/7:

Reading an object designated by a volatile glvalue ([basic.lval]), 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.

Is this paragraph about the undetected change? Could the side effect mean this?


Or there is dcl.type.cv/5:

The semantics of an access through a volatile glvalue are implementation-defined. If an attempt is made to access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, the behavior is undefined.

Is this paragraph about my question? What does "The semantics of an access through a volatile glvalue are implementation-defined" mean exactly? Can you give an example of different "semantics of an access"?


And there is dcl.type.cv/6, which is about my question, but it is just a note:

[ Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object. See [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. — end note ]

2
Are you asking just out of curiosity? Or is there some underlying reasons? If the latter, what is the real and actual problem you have? Perhaps you should ask more directly about that instead? - Some programmer dude
@Someprogrammerdude: I'd like to give an answer to my own previous question, and I'd like to understand this issue better. So no real underlying programming problem, but out of curiosity, and to understand volatiles better: how and when can a volatile variable change "undetected". - geza
I'm pretty sure this and the previous question was extensively discussed on SO already. - Passer By
"The semantics of an access through a volatile glvalue are implementation-defined" is exceptionally unclear. But I think it is to be read regarding "The semantic descriptions in this document define a parameterized nondeterministic abstract machine.". Therefore I take it to mean that reading one volatile variable can change anything. Even the value of nonvolatile variables. As long as the behavior is exactly documented. - Johannes Schaub - litb
This might be relevant. - Passer By

2 Answers

7
votes

The key here is "changes in the state of the execution environment."

The execution environment is what's outside your program. That might include an OS, filesystem, screen, etcetera. It is generally unpredictable. You cannot assume that if you write a 0 to a file, that the file will not be overwritten by another process with a 1.

volatile variables are logically part of that execution environment. As far as C++ is concerned, the environment can enumerate them, read them and write them, just like files. And this can happen without your program knowing.

On the flip side, your implementation realizes the link between your program and its execution environment, so it does have some idea about what might happen. If it has some kind of private RAM disk implementation, then it might know that certain filenames are not externally visible in the OS filesystem. And it might know that volatile int i lives in a CPU register, so that it can't be accessed via memory mappings. That's all allowed by the C++ standard. It just talks about the execution environment in general terms, the implementation must be more precise. That is what "implementation-defined semantics" means.

1
votes

volatile is just a request to the compiler asking it to reload the variable from memory for each access. It is intended for 2 common use cases:

  • a variable can be changed by a different thread (or even a different program which could be given write access to that memory zone), or by kernel mode code (for example by a special driver)
  • this variable represents a physical memory register, mainly used in kernel mode programming, or on OS having no notion of user/kernel mode like good old MS/DOS.

Once you know that, the different quotes from the standard all make sense.

Reading an object designated by a volatile glvalue ([basic.lval]), ... are all side effects, which are changes in the state of the execution environment.

Reading a hardware register might have an impact on the underlying system, that is the reason why it is said to be an observable side effect.

The semantics of an access through a volatile glvalue are implementation-defined. If an attempt is made to access an object defined with a volatile-qualified type through the use of a non-volatile glvalue, the behavior is undefined.

If you use a non volatile pointer to access a volatile hardware register, the compiler could cache the previous value and not execute the physical access.

[ Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object. See [intro.execution] for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. — end note ]

Some implementation could reserve a memory zone for special low level io ports operations. In that case, the combination of the volatile specifier and of that special memory zone addresses could be required to validate the transformation or normal memory access operation with special io operations.