views:

277

answers:

5

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

+1  A: 

Of course, in the second example, writing/modifying variable 'flag' is omitted. If it is never written to, there is no need for it being volatile.

Concerning the main question

The variable has still to be flagged volatile even if every thread accesses/modifies it through the same function.

A function can be "active" simultaneously in several threads. Imagine that the function code is just a blueprint that gets taken by a thread and executed. If thread B interrupts the execution of ReadFlag in thread A, it simply executes a different copy of ReadFlag (with a different context, e.g. a different stack, different register contents). And by doing so, it could mess up the execution of ReadFlag in thread A.

ziggystar
`flag` is written to by the interrupt; the second code example was intended as a fragment and to only show the changes, the interrupt handler remains the same. Sorry if that was not clear.
Clifford
`volatile` is NOT a thread synchronization primitive, using it as such is bad and wrong, see my answer for why.
Spudd86
+3  A: 

Yes it is critical.
Like you said volatile prevents code breaking optimization on shared memory [C++98 7.1.5p8].
Since you never know what kind of optimization a given compiler may do now or in the future, you should explicitly specify that your variable is volatile.

Ugo
-1 for incorrect answer
Spudd86
@spudd86 -1 for incorrect comment (damn it doesn't work ^^)
Ugo
+9  A: 

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.

Dummy00001
actually the CORRECT way to deal with the synchronization issues is with locks, volatile does NOT make this safe since on systems like some multi-processor arm systems this will STILL break because the caches aren't coherent...
Spudd86
Thanks, in the normal case the 'data owner' task and the 'data accessing' task would be in separate compilation units, but I do not intend to rely on that, nor that 'linker optimisation' might not have the same effect.
Clifford
@spudd86: Synchronisation is a different issue (I probably should not have mentioned non-atomic access in the preamble); I am only concerned with the compiler optimising out a read. Also in my case we can assume that targeting multi-processor devices is not required. Currently the target is a Cortex-M3.
Clifford
To prevent the function from being inlined, use the 'volatile' keyword. As I understand it, volatile int foo (void) { return 32; } will not get inlined (at least with any compiler I've tried). It can also be applied to statements. :) Fun stuff.
Sparky
A: 

Edit: I didn't read the code very closely and so I thought this was a question about thread synchronization, for which volatile should never be used, however this usage looks like it might be OK (Depending on how else the variable in question is used, and if the interrupt is always running such that it's view of memory is (cache-)coherent with the one that the thread sees. In the case of 'can you remove the volatile qualifier if you wrap it in a function call?' the accepted answer is correct, you cannot. I'm going to leave my original answer because it's important for people reading this question to know that volatile is almost useless outside of certain very special cases.

More Edit: Your RTOS use case may require additional protection above and beyond volatile, you may need to use memory barriers in some cases or make them atomic... I can't really tell you for sure, it's just something you need to be careful of (I'd suggest looking at the Linux kernel documentation link I have below though, Linux doesn't use volatile for that kind of thing, very probably with a good reason). Part of when you do and do not need volatile depends very strongly on the memory model of the CPU you're running on, and often volatile is not good enough.

volatile is the WRONG way to do this, it does NOT guarantee that this code will work, it wasn't meant for this kind of use.

volatile was intended for reading/writing to memory mapped device registers, and as such it is sufficient for that purpose, however it DOES NOT help when you're talking about stuff going between threads. (In particular the compiler is still aloud to re-order some reads and writes, as is the CPU while it's executing (this one's REALLY important since volatile doesn't tell the CPU to do anything special (sometimes it means bypass cache, but that's compiler/CPU dependent))

see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html, Intel developer article, CERT, Linux kernel documentation

Short version of those articles, volatile used the way you want to is both BAD and WRONG. Bad because it will make your code slower, wrong because it doesn't actually do what you want.

In practice, on x86 your code will function correctly with or without volatile, however it will be non-portable.

EDIT: Note to self actually read the code... this is the sort of thing volatile is meant to do.

Spudd86
In my case cache coherency etc. are not an issue. The target is a Cortex-M3. I do not intend to weigh down the code with mechanisms to make the code portable to targets it will never run on. I applaud your letting this stand despite addressing a different issue, because the issue is important.
Clifford
volatile can actually be exactly what you want when programming for small embedded devices like 8bit MCUs.
ziggystar
+2  A: 

In C, the volatile keyword is not required here (in the general sense).

From the ANSI C spec (C89), section A8.2 "Type Specifiers":

There are no implementation-independent semantics for volatile objects.

Kernighan and Ritchie comment on this section (referring to the const and volatile specifiers):

Except that it should diagnose explicit attempts to change const objects, a compiler may ignore these qualifiers.

Given these details, you can't be guaranteed how a particular compiler interprets the volatile keyword, or if it ignores it altogether. A keyword that is completely implementation dependent shouldn't be considered "required" in any situation.

That being said, K&R also state that:

The purpose of volatile is to force an implementation to suppress optimization that could otherwise occur.

In practice, this is how practically every compiler I have seen interprets volatile. Declare a variable as volatile and the compiler will not attempt to optimize accesses to it in any way.

Most of the time, modern compilers are pretty good about judging whether or not a variable can be safely cached or not. If you find that your particular compiler is optimizing away something that it shouldn't, then adding a volatile keyword might be appropriate. Be aware, though, that this can limit the amount of optimization that the compiler can do on the rest of the code in the function that uses the volatile variable. Some compilers are better about this than others; one embedded C compiler I used would turn off all optimizations for a function that accesses a volatile, but others like gcc seem to be able to still perform some limited optimizations.

Accessing the variable through an accessor function should prevent the function from caching the value. Even if the function is auto-inlined, each call to the function should re-call the function and re-fetch a new value. I have never seen a compiler that would auto-inline the accessor function and then optimize away the data re-fetch. I'm not saying it can't happen (since this is implementation-dependent behavior), but I wouldn't write any code that expects that to happen. Your second example is essentially placing a wrapper API around the variable, and libraries do this without using volatile all the time.

All in all, the treatment of volatile objects in C is implementation-dependent. There is nothing "guaranteed" about them according to the ANSI C89 spec.

Your code is sharing the volatile object between a thread and an interrupt routine. No compiler implementation (that I have ever seen) gives volatile enough power to be sufficient for handling parallel access. You should use some sort of locking mechanism to guarantee that the two threads (in your first example) don't step on each other's toes (even though one is an interrupt handler, you can still have parallel access on a multi-CPU or multi-core system).

bta
Adding to the last paragraph: Make sure that whatever locking mechanism you use is safe to use inside interrupt context (i.e. accessing it shouldn't be a blocking operation).
bta