Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C 'Volatile' keyword in ISR and multithreaded program?

I read about usage of C volatile keyword in memory-mapped hardware register, ISR, and multithreaded program.

1) register

uint8_t volatile * pReg;
while (*pReg == 0) { // do sth } // pReg point to status register 

2) ISR

int volatile flag = 0;
int main() 
{
    while(!flag) { // do sth }
}
interrupt void rx_isr(void)
{
    //change flag
}

3) multithread

int volatile var = 0;
int task1()
{
    while (var == 0) { // do sth }
}
int task2()
{
    var++;
}

I can see why compiler can mistakenly optimize the while in case 1) if volatile is not there, 'cause variable change is made from hardware, compiler may not see any change of the variable made from code.

But for case 2) and 3), why is volatile ever needed? In both cases variable is declared global, and compiler can see it's used in more than one place. So why would compiler optimize the while loop if the variable is not volatile?

Is it because a compiler by-design has no idea of "asynchronous call" (in case of ISR), or multithreading? But this can't be, right?

In addition, case 3) looks like a common program in multithreading without the volatile keyword. Let's say I add some locking to the global variable (no volatile keyword):

int var = 0;
int task1()
{
    lock();   // some mutex
    while (var == 0) { do sth }
    release()
}
int task2()
{
    lock();
    var++;
    release();
}

It looks normal enough to me. So do I really need volatile in multithreading? How come I've never seen volatile qualifier added to variable to avoid optimization in multithread program before?

like image 939
user1559625 Avatar asked Oct 05 '12 02:10

user1559625


People also ask

How is volatile keyword used in multithreading?

Volatile keyword is used to modify the value of a variable by different threads. It is also used to make classes thread safe. It means that multiple threads can use a method and instance of the classes at the same time without any problem. The volatile keyword can be used either with primitive type or objects.

What is the volatile keyword in C?

C's volatile keyword is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of the variable may change at any time--without any action being taken by the code the compiler finds nearby.

What is volatile variable in C with example?

The volatile keyword is intended to prevent the compiler from applying any optimizations on objects that can change in ways that cannot be determined by the compiler. Objects declared as volatile are omitted from optimization because their values can be changed by code outside the scope of current code at any time.

How can volatile keywords affect thread performance?

Using volatile is yet another way (like synchronized, atomic wrapper) of making class thread-safe. Thread-safe means that a method or class instance can be used by multiple threads at the same time without any problem.


3 Answers

Is it because a compiler by-design has no idea of "asynchronous call" (in case of ISR), or multithreading? But this can't be, right?

Yes, it is that way.

In C the compiler has no notion of concurrency, so it is allowed to reorder and cache memory accesses, as long as the view from a single thread can't notice the difference.

That's why you need volatile (block this kind of optimizations for a variable), memory barriers (block it at a single point of the program for all variables) or other forms of synchronization such as locking (which typically act as memory barriers).

like image 178
starblue Avatar answered Oct 17 '22 13:10

starblue


The main point of using volatile keyword is to prevent compiler from generating a code that uses CPU registers as faster ways to represent variables. This forces compiled code to access the exact memory location in RAM on every access to the variable to get the latest value of it which may have been changed by another entity. By adding volatile we make sure that our code is aware of any change made to a variable by anyone else like hardware or ISR and no coherency issue happens.

In absence of volatile keyword, compiler tries to generate faster code by reading the content of variable from RAM into a CPU register once and use that cached value in a loop or function. Accessing RAM could be tens of times slower than accessing the CPU register.

I've had the experience on item 1 and 2 but I don't think you need to define a variable as volatile in a multi threded environment. Adding the lock/unlock mechanism is necessary to solve synchronization problem and is not related the what volatile is about.

like image 31
Kamyar Souri Avatar answered Oct 17 '22 13:10

Kamyar Souri


The compiler is indeed to allow that nothing else changes your variables unless some every specific conditions are met. One of them is volatile access; others are certain compiler barriers.

The naive way to program multithreaded code which you may have in mind is indeed prone to errors and would be considered undefined behaviour. If you have correct multithreaded code, then either an optimisation is still legal (like in your final task1, where the loop is still UB and may be thrown out), or the synchronisation primitives will contain the necessary barriers (usually in the guts of some atomic variables).

To round things up, here's a corrected version of the multithreaded example:

 for (;;)
 {
     lock();
     if (var != 0) { unlock(); break; }
     unlock();
 }

The implementation of the unlock() function introduces a compiler barrier which ensures that the loop cannot be optimized away.

like image 2
Kerrek SB Avatar answered Oct 17 '22 13:10

Kerrek SB