Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is `volatile` required for shared memory accessed via access function?

Tags:

[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.

like image 362
Clifford Avatar asked Jun 30 '10 10:06

Clifford


People also ask

When should volatile be used?

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.

What is volatile access?

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.

What will happen if the switches are not declared as a volatile?

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).

What is volatile storage specifier where is it used?

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.


1 Answers

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.

like image 194
Dummy00001 Avatar answered Oct 03 '22 05:10

Dummy00001