Friday 1 December 2017

c++ - Should i specify volatile keyword for every object that shares its memory between different threads

itemprop="text">

I just read href="https://www.securecoding.cert.org/confluence/display/cplusplus/CON01-CPP.+Do+not+use+volatile+as+a+synchronization+primitive"
rel="nofollow">Do not use volatile as a synchronization primitive article
on CERT site and noticed that a compiler can theoretically optimize the following code
in the way that it'll store a flag variable in the registers
instead of modifying actual memory shared between different
threads:



bool flag = false;//Not
declaring as {{volatile}} is wrong. But even by declaring {{volatile}} this code is
still erroneous
void test() {
while (!flag) {

Sleep(1000); // sleeps for 1000 milliseconds
}
}
void
Wakeup() {
flag = true;
}
void debit(int
amount){
test();
account_balance -= amount;//We think it is safe
to go inside the critical
section
}


Am
I right?



Is it true that I need to use
volatile keyword for every object in my program that shares its
memory between different threads? Not because it does some kind of synchronization for
me (I need to use mutexes or any other synchronization primitives to accomplish such
task anyway) but just because of the fact that a compiler can possibly optimize my code
and store all shared variables in the registers so other threads will never get updated
values?



Answer




It is actually very simple, but confusing at
the same time. On a high level, there are two optimization entities at play when you
write C++ code - compiler and CPU. And within compiler, there are two major optimization
techniue in regards to variable access - omitting variable access even if written in the
code and moving other instructions around this particular variable
access.



In particular, following example
demonstrates those two techniques:



int
k;
bool flag;



void foo()
{
flag = true;
int i = k;
k++;
k =
i;
flag =
false;
}


In
the code provided, compiler is free to skip first modification of flag - leaving only
final assignment to false; and completely remove any modifications to k. If you make k
volatile, you will require compiler to preserve all access to k = it will be
incremented, and than original value put back. If you make flag volatile as well, both
assignments first to true, than two false will remain in the code. However, reordering
would still be possible, and the effective code might look
like



void foo() {
flag
= true;
flag = false;
int i = k;
k++;
k =
i;
}


This
will have unpleasant effect if another thread would be expecting flag to indicate if k
is being modified now.



One of the way to achive
the desired effect would be to define both variables as atomic.
This would prevent compiler from both optimizations, ensuring code executed will be the
same as code written. Note that atomic is, in effect, a volatile+ - it does all the
volatile does + more.



Another thing to notice
is that compiler optimizations are, indeed, a very powerful and desired tool. One should
not impede them just for the fun of it, so atomicity should be used only when it is
required.


No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...