In the following code, I have not made the variable quit has volatile sig_atomic_t. I have left it as a plain int.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#define UNUSED(x) (void) (x)
int quit;
void sigusr1_handler(int sig)
{
UNUSED(sig);
write(1, "handler\n", 8);
quit = 1;
}
int main()
{
struct sigaction sa;
sa.sa_handler = sigusr1_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("sigaction");
return 1;
}
quit = 0;
while (!quit) {
printf("Working ...\n");
sleep(1);
}
printf("Exiting ...\n");
return 0;
}
Since the quit variable is not specified as volatile, I was expecting that the compiler's optimizer would optimize the while-loop in the code to:
while (1) {
printf("Working ...\n");
sleep(1);
}
But I don't see this happening. On one terminal, I run the following.
$ gcc -O3 foo.c && ./a.out
Working ...
Working ...
On another terminal, I send SIGUSR1 to my program.
$ pkill -USR1 a.out
On the first terminal, the output shows that the program's signal handler is invoked and the while-loop quits.
Working ...
Working ...
handler
Exiting ...
$
How can I demonstrate the optimization of the loop due to quit not being volatile?
It can be difficult to force the compiler to optimize the load of
quitout from the while conditional. I was able to do it by removing the body of the while loop:When compiling for x86-64 with
-O3on gcc version 5.4 this results in an infinite loop which doesn't check thequitvariable. Notice the jump-to-self at.L5:Changing the definition to
volatile int quit;causes the correct behavior. See themov,test, andjeinstructions at.L6:You can play with both examples here: without
volatileand withvolatile.