[edit] For background reading, and to be clear, this is what I am talking about: Introduction to the volatile keyword
When reviewing embedded systems code, one of the most common errors I see is the omission of volatile for thread/interrupt shared data. However my question is whether it is 'safe' not to use volatile
when a variable is accessed via an access function or member function?
A simple example; in the following code...
volatile bool flag = false ;
void ThreadA()
{
...
while (!flag)
{
// Wait
}
...
}
interrupt void InterruptB()
{
flag = true ;
}
... the variable flag
must be volatile to ensure that the read in ThreadA is not optimised out, however if the flag were read via a function thus...
volatile bool flag = false ;
bool ReadFlag() { return flag }
void ThreadA()
{
...
while ( !ReadFlag() )
{
// Wait
}
...
}
... does flag
still need to be volatile? I realise that there is no harm in it being volatile, but my concern is for when it is omitted and the omission is not spotted; will this be safe?
The above example is trivial; in the real case (and the reason for my asking), I have a class library that wraps an RTOS such that there is an abstract class cTask that task objects are derived from. Such "active" objects typically have member functions that access data than may be modified in the object's task context but accessed from other contexts; is it critical then that such data is declared volatile?
I am really interested in what is guaranteed about such data rather than what a practical compiler might do. I may test a number of compilers and find that they never optimise out a read through an accessor, but then one day find a compiler or a compiler setting that makes this assumption untrue. I could imagine for example that if the function were in-lined, such an optimisation would be trivial for a compiler because it would be no different than a direct read.
A variable should be declared volatile whenever its value could change unexpectedly. In practice, only three types of variables could change: Memory-mapped peripheral registers. Global variables modified by an interrupt service routine.
In computer programming, particularly in the C, C++, C#, and Java programming languages, the volatile keyword indicates that a value may change between different accesses, even if it does not appear to be modified.
Failing to declare the variable as volatile will result in the compiler optimizing the code in such a way that it will read the port only once and keep using the same value in a temporary register to speed up the program (speed optimization).
The volatile is used in different places. For memory mapped peripheral registers, some global variables, that are accessed by some other functions or interrupt service routines, or in some multi-threaded applications, the volatile can be used.
My reading of C99 is that unless you specify volatile
, how and when the variable is actually accessed is implementation defined. If you specify volatile
qualifier then code must work according to the rules of an abstract machine.
Relevant parts in the standard are: 6.7.3 Type qualifiers
(volatile description) and 5.1.2.3 Program execution
(the abstract machine definition).
For some time now I know that many compilers actually have heuristics to detect cases when a variable should be reread again and when it is okay to use a cached copy. Volatile makes it clear to the compiler that every access to the variable should be actually an access to the memory. Without volatile it seems compiler is free to never reread the variable.
And BTW wrapping the access in a function doesn't change that since a function even without inline
might be still inlined by the compiler within the current compilation unit.
P.S. For C++ probably it is worth checking the C89 which the former is based on. I do not have the C89 at hand.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With