volatile and global variables

should you add "volatile" for each and every global variables?

as per C99 section 5.1.2.3, quote:

7. More stringent correspondences between abstract and actual semantics may be defined by each implementation.

8. EXAMPLE 1 An implementation might define a one-to-one correspondence between abstract and actual semantics: at every sequence point, the values of the actual objects would agree with those specified by the abstract semantics. The keyword volatile would then be redundant.

9. Alternatively, an implementation might perform various optimizations within each translation unit, such that the actual semantics would agree with the abstract semantics only when making function calls across

translation unit boundaries. In such an implementation, at the time of each function entry and function return where the calling function and the called function are in different translation units, the values of all externally linked objects and of all objects accessible via pointers therein would agree with the abstract semantics. Furthermore, at the time of each such function entry the values of the parameters of the called function and of all objects accessible via pointers therein would agree with the abstract semantics. In this type of implementation, objects referred to by interrupt service routines activated by the signal function would require explicit specification of volatile storage, as well as other implementation-defined restrictions."

Above basically say, if an API does not change the value of a global variable, a highly optimized compiler can compile the API such that it reads the value of the global variable once into a CPU register at the entry of the API and keep on using that register without re-reading the memory ( even the memory content might be changed by, say, a signal handler ).

Following is an example:

// file name is test.c

#include <stdio.h>

#include <stdlib.h>

#include <signal.h>

#include <string.h>

int aaa = 0xbeef;

void handler (int sig)

{

printf("inside handler\n"); // printf is not signal safe, oh whatever.

aaa = 0xabcd;

}

void main(void)

{

struct sigaction act;

memset (&act, 0, sizeof(act));

act.sa_handler = handler;

if (sigaction(SIGTERM, &act, 0)) {

printf("sigaction error\n");

return;

}

int bbb = aaa;

if(bbb==0x1234) {

printf(" b is 0\n");

}

int ccc = aaa;

if(ccc==0x5678) {

printf(" c is 0\n");

}

}

compile the above code via:

gcc -g test.c

objdump -S -D a.out > out.txt

grep -e aaa -e cmpl out.txt

int bbb = aaa;

4005c1: 8b 05 61 0a 20 00 mov 0x200a61(%rip),%eax # 601028 <aaa>

4005ca: 81 7d f8 34 12 00 00 cmpl $0x1234,-0x8(%rbp)

int ccc = aaa;

4005dd: 8b 05 45 0a 20 00 mov 0x200a45(%rip),%eax # 601028 <aaa>

4005e6: 81 7d fc 78 56 00 00 cmpl $0x5678,-0x4(%rbp)

Above is enough proof that each and every time we access variable "aaa", the code read from the memory. This is default gcc behavior: no optimization.

compile the code with optimization:

gcc -g -O3 test.c

objdump -S -D a.out > out.txt

grep -e aaa -e cmp out.txt

int bbb = aaa;

40048f: 8b 05 9b 0b 20 00 mov 0x200b9b(%rip),%eax # 601030 <aaa>

400495: 3d 34 12 00 00 cmp $0x1234,%eax

int ccc = aaa;

40049c: 3d 78 56 00 00 cmp $0x5678,%eax

Above is enough proof that inside main(), we just read from the memory where "aaa" is stored into register $eax, and using $eax throughout the code without re-read it from the memory, even that memory content could be possibly changed by the signal handler.

In the optimized case, the global variable value might become stale ( signal handler changed its value between first use and second use ). Is that a desirable behavior or not? I would argue that should be case by case. E.g, if the global variable is a configuration flag, one certainly would not want to handle the value change in each and every API that uses the global variable.

However, using stale value in the second half of the API may not be a desirable scenario either in most cases. A mutex seems necessary as such, even in the optimized case.

the following link is a nice reading, however, it did not discuss the scenario where we access the same global variable at different locations inside the same API.

http://www.embedded.com/electronics-blogs/beginner-s-corner/4023801/Introduction-to-the-Volatile-Keyword

Summary: if we are using a global variable inside an API multiple times, either inside a while() loop, or at different code locations of the API, we should consider the following:

1. is a global variable really needed?

2. what are the compiler optimization flags in use?

3. should I use a mutex to protect it, regardless optimization, to avoid using stale value inside the same API?

4. should I add volatile keyword to force compiler to not optimize at all?