tags:

views:

195

answers:

4

Hello guys,

In C++, volatile is treated the same way const is: passing a pointer to volatile data to a function that doesn't want the volatile modifier triggers a compile error.

int foo(int* bar) { /* snip */ }

int main()
{
    volatile int* baz;
    foo(baz); // error: invalid conversion from ‘volatile int*’ to ‘int*’
}

Why is it dangerous? It's obvious for the const modifier that removing it can break const correctness; but is there such a thing as "volatile correctness"? I can't figure out how passing a pointer to volatile data as a pointer to non-volatile data could cause problems.

EDIT Just so you guys know why I was using volatile in the first place: many of Mac OS X's OSAtomic family of functions (for atomic increments, decrements, additions, subtractions, compare and swap, etc.) takes volatile arguments.

A: 

Well, in foo() the compiler no longer knows that baz (or more strictly bar) is volatile and so may attempt to apply some inappropriate optimisations.

anon
Your answer is extremely vague. Could you give a few examples of situations in which the compiler could apply an optimization that will make the code dangerous?
zneak
@zneak What do you imagine the purpose of volatile is? It is precisely to prevent optimisation of, for example, caching a variable in a register and not reading it from memory on every access in the program source.
anon
@Neil Butterworth: Yeah, and it's pretty useful in certain circumstances, notably when sharing data structures across threads. But assuming all functions that need to not cache the variable take a pointer to `volatile` data, and those that don't need it won't take it, I don't see the problem it will cause.
zneak
@zneak It is not at all useful in threading. The classic use is in memory mapped I/O where the contents of a memory location may change outwith program control, and so must be read on every access. If the compiler optimised such accesses, changes could be missed.
anon
@Neil Butterworth: I think it `volatile` is useful for threading and data sharing amongst threads.
KLee1
@Klee1 Well, unless you expand on that, what can I say except you think wrong.
anon
@KLee1 You think wrong. `volatile` is only "useful" for threading if you are doing synchronization wrong, so it's not useful at all.
Tyler McHenry
@Tyler McHenry: Okay, maybe they are not as "useful" as other constructs for threading, but the _can_ be used for it (like a naive implementation of cross-thread signaling, busy waiting for instance). Also, thank you for insulting my intelligence!
KLee1
+7  A: 

Because the volatile modifier means that the compiler must take care to actually perform each read/write of the volatile data item exactly as the C standard's 'abstract machine' specifies.

When the volatile modifier is stripped away, the data accesses can be optimized away as long as the program behaves 'as if' the access occurred as far as the single-threaded viewpoint of the program flow of control is concerned. In other words, the compiler can treat a non-volatile piece of data as if the compiler and only the compiler can see and can modify the data item (which is the case in the vast majority of cases).

The volatile keyword tells the compiler that something else (hardware or another thread of execution) can modify or see that data item, so the compiler isn't permitted to optimize away accesses.

If you could pass a pointer to a volatile piece of data to a function that took a non-volatile pointer without warning, the function might not see a change in the data that might occur. If you don't care about that, you might be able to code up a nice, portable workaround (depending on what foo() does with the data):

int foo(int* bar) { /* snip */ }

int main()
{
    volatile int* baz;

    int tmp = *baz;
    foo(&tmp);
    *baz = tmp;
}
Michael Burr
So what kind of problem can it cause?
zneak
@zneak - that really depends on what `foo()` does and on what the volatile data item does (or why it's volatile). In general, since volatile data is used for very specific things (hardware registers or communication between threads), it usually doesn't make sense for something that's not specifically written to deal with the exact volatile 'thing' to do anything with it. Note that it's often the case that for the volatile thing to really work correct, some other platform specific work must be done (like flushing memory barriers).
Michael Burr
@zneak Suppose that `baz` pointed to a memory-mapped hardware register. Without the volatile qualifier on `bar`, within `foo`, the compiler would be within its rights to cache the value of `*bar` in a cpu register, which would miss asynchronous hardware-driven changes to the value of `*bar`, or optimize away writes to `*bar`, which would affect what signals are sent to the hardware. Concerns like this would be why `baz` is a volatile pointer in the first place, so stripping the volatile would re-introduce the potential problems that the volatile qualifier was supposed to avoid.
Tyler McHenry
A: 

The volatile keyword means that the value should be loaded and stored from/to memory every time.

Consider the code:

int foo(int* bar) { 
    while(*bar){ 
        //Do something...
    }
}

int main()
{
    volatile int num = 1;
    volatile int* baz = #
    //Start a seperate thread to change *baz evenutally... 
    foo(baz);
}

When the compiler sees the while loop, and knows that what is pointed to by bar is always going to be 1. Why should it have to check every time? It would be extremely wasteful to check every time. volatile makes sure that the compiler does do this check every time, so when the other thread changes the value, the while loop exits.

KLee1
Dude, your example is broken. Not using the `volatile` keyword when you _need it_ has different consequences than when you don't use it when you _don't need_ it.
zneak
The compiler doesn't know when you _don't need_ it. If you don't need it, then why are you using it?
KLee1
It's not because `foo` doesn't need it the rest of my code doesn't need it either. (Please specify @zneak somewhere in your comments so I get notified.)
zneak
@zneak If none of your code needs baz to be `volatile`, then why is it?
KLee1
@KLee1: _Some_ of my code needs it, and some doesn't. This is what I tried to say in my previous comment, sorry if it was unclear.
zneak
@zneak volatileness of a memory location is not something that comes and goes. The contents of a given location either can be changed externally, or they cannot. Writes to a location either have external side-effects, or they do not. This isn't a transient property. Keep in mind that the `volatile` qualification applies to the *pointed-to address* not to the pointer itself.
Tyler McHenry
KLee1
@zneak As you clearly don't understand what volatile means (otherwise you would not be asking this question), how do you know you need it?
anon
+4  A: 

Not only can the compiler optimize away access to non-volatile variables, it can update them predictively/speculatively, as long as the sequential execution of the program is unaffected.

If spurious writes to your volatile variable don't break your design, it probably didn't need to be volatile in any context.

Ben Voigt
Congratulations! You got it through my thick skull.
zneak