Armen Tsirunyan
Armen Tsirunyan

Reputation: 132974

Is volatile required here?

EDITED and refined my question after Johannes's valuable answer

bool b = true;
volatile bool vb = true;    
void f1() { }
void f2() { b = false; }

void(* volatile pf)() = &f1; //a volatile pointer to function

int main()
{
   //different threads start here, some of which may change pf 
   while(b && vb)
   {
      pf();
   }
}

So, let's forget synchronization for a while. The question is whether b has to be declared volatile. I have read the standard and sort-of know the formal definition of volatile semantics (I even almost understand them, the word almost being the key). But let's be a bit informal here. If the compiler sees that in the loop there is no way for b to change then unless b is volatile, it can optimize it away and assume it is equivalent to while(vb). The question is, in this case pf is itself volatile, so is the compiler allowed to assume that b won't change in the loop even if b is not volatile?

Please refrain from comments and answers which address the style of this piece of code, this is not a real-world example, this is an experimental theoretical question. Comments and answers which, apart from answering my question, also address the semantics of volatile in greater detail which you think I have misunderstood are very much welcome.

I hope my question is clear. TIA

Editing once more:
what about this?

bool b = true;
volatile bool vb = true;
void f1() {}
void f2() {b = false;}
void (*pf) () = &f1;

#include <iosrteam>
int main()
{
   //threads here

   while(b && vb)
   {
      int x;
      std::cin >> x;
      if(x == 0)
         pf = &f1;
      else
         pf = &f2;    
      pf();
   } 
}

Is there a principal difference between the two programs. If yes, what is the difference?

Upvotes: 5

Views: 440

Answers (3)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506847

The question is, in this case pf is itself volatile, so is the compiler allowed to assume that b won't change in the loop even if b is not volatile?

It can't, because you say that pf might be changed by the other threads, and this indirectly changes b if pf is called then by the while loop. So while it is theoretically not required to read b normally, it in practice must read it to determine whether it should short circuit (when b becomes false it must not read vb another time).


Answer to the second part

In this case pf is not volatile anymore, so the compiler can get rid of it and see that f1 has an empty body and f2 sets b to false. It could optimize main as follows

int main()
{
   // threads here (which you say can only change "vb")

   while(vb)
   {
      int x;
      std::cin >> x;
      if(x != 0)
         break;    
   } 
}

Answer to older revision

One condition for the compiler to be allowed to optimize the loop away is that the loop does not access or modify any volatile object (See [stmt.iter]p5 in n3126). You do that here, so it can't optimize the loop away. In C++03 a compiler wasn't allowed to optimize even the non-volatile version of that loop away (but compilers did it anyway).

Note that another condition for being able to optimize it away is that the loop contains no synchronization or atomic operations. In a multithreaded program, such should be present anyway though. So even if you get rid of that volatile, if your program is properly coded I don't think the compiler can optimize it away entirely.

Upvotes: 3

Brooks Moses
Brooks Moses

Reputation: 9527

The exact requirements on volatile in the current C++ standard in a case like this are, as I understand it, not entirely well-defined by the standard, since the standard doesn't really deal with multi-threading. It's basically a compiler hint. So, instead, I'll address what happens in a typical compiler.

First, suppose the compiler is compiling your functions independently, and then linking them together. In either example, you have a loop in which you're checking a variable, and calling a function pointer. Within the context of that function, the compiler has no idea what the function behind that function pointer will do, and thus it must always re-load b from memory after calling it. Thus, volatile is irrelevant there.

Expanding that to your first actual case, and allowing the compiler to make whole-program optimizations, because pf is volatile the compiler still has no idea what it's going to be pointing at (it can't even assume it's either f1 or f2!), and thus likewise cannot make any assumptions about what will be unmodified across the function-pointer call -- and so volatile on b is still irrelevant.

Your second case is actually simpler -- vb in it is a red herring. If you eliminate that, you can see that even in completely single-threaded semantics, the function-pointer call may modify b. You're not doing anything with undefined behavior, and so the program must operate correctly without volatile -- remember that, if you aren't considering a situation with external thread tweaks, volatile is a no-op. Therefore, without vb in the picture, you cannot possibly need volatile, and it's pretty clear that adding vb changes nothing.

Thus, in summary: You don't need volatile in either case. The difference, insofar as there is one, is that in the first case if fp were not volatile, a sufficiently-advanced compiler could possibly optimize b away, whereas it cannot even without volatile anywhere in the program in the second case. In practice, I do not expect any compilers would actually make that optimization.

Upvotes: 2

Lou Franco
Lou Franco

Reputation: 89142

volatile only hurts you if you think you could have benefited from an optimization that can't be done or if it communicates something that isn't true.

In your case, you said that these variables can be changed by other threads. Reading code, that's my assumption when I see volatile, so from a maintainer's perspective, that's good -- it's giving me extra information (which is true).

I don't know whether the optimizations are worth trying to salvage since you said this isn't the real code, but if they aren't then there aren't any reasons to not use volatile.

Not using volatile when you are supposed to results in incorrect behavior, since the optimizations are changing the meaning of the code.

I worry about coding the minutia of the standard and behavior of your compilers because things like this can change and even if they don't, your code changes (which could effect the compiler) -- so, unless you are looking for micro-optimization improvements on this specific code, I'd just leave it volatile.

Upvotes: 0

Related Questions