C++98: is volatile needed for access to shared global objects in a multithreaded application

Viewed 91

Sadly I'm stuck with C++98, which I'm using in an embedded application.

My question is: I have a multithreaded application, with various global shared variables (evil, I know). I do protect every access to them using mutexes. Do I also need to declare these global variables as volatile, in order to prevent the compiler from optimizing accesses to them?

Searching online it seems that volatile is absolutely useless for multithreading, but a lot of articles are related to C++11, which did introduce a memory model which recognizes threads, but I'm in C++98 land. I also found some resources that indicate that volatile is instead useful in my case, such as this Barr Group's article.

Let me emphasize the fact that I don't want to get rid of the mutexes at all, or try lock free programming. The mutexes are absolutely staying, I just want to understand if the volatile keyword is needed.

2 Answers

Do I also need to declare these global variables as volatile, in order to prevent the compiler from optimizing accesses to them?

No. And if you did, you would still be in trouble because volatile is not sufficient -- things other than the compiler (such as the CPU, posting buffers, and memory controllers) can also optimize accesses.

As I'm sure you've read elsewhere, volatile has no defined multi-threading semantics in C++98. So unless it does in your particular threading standard (which you don't specify), then it's completely useless to you.

Presumably, your code uses mutexes properly. No optimization is allowed to break code that only relies on guarantees provided by the relevant standards or implementation. So if you're using the mutexes correctly, then your code is guaranteed to work.

What does keyword volatile mean ?

You enforce your program always to read/write your variable to memory (like cache miss every time). Always (CPU->L1->L2->L3->Bus->Memoryand Memory->Bus->L3->L2->L1->CPU) Actually it slows your program, so you should not use it until exact need.

You may know that compiler may do some optimizations, but these are designed not to affect/change your program logic.

Example 1:

int d = 5; 
int b = 10;
for (int i=0; i < 1e9; i++){
    cout << i;
    b++;
}
cout << d; // d may be created only here, or not created at all. compiler may just cout << 5;
      // var b - seems to be skipped at all due to being unused

Example 2:

int a = 0;
for (int i = 0; i < 10; i++){
    a++;
    sleep(1000);
}
cout << a;

// this code could be compiled like the following one
sleep(10000); 
cout << 10;

Keyword volatile prevents your var from such optimizations. i.e. vars a, b will be created and incremented, also cache missed always.

Here is one of the most common use cases for using volatile:

You have a third party sensor or scoreboard, that is attached to var address, and its value is always read and shown on the scoreboard. so in this case you need volatile, to prevent compiler optimizations on it, so your score board can show correct value.

I guess now you can decide whether you need volatile or not

Related