Here is a simple toy program that uses volatile sig_atomic_t
.
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#define UNUSED(x) (void) (x)
volatile sig_atomic_t 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("Exiting ...\n");
return 0;
}
I think I know why volatile sig_atomic_t
is necessary for the quit
variable in this particular program.
- Without
volatile
, the compiler may optimizewhile (!quit) ;
to an infinite loop. It does not find the loop modifyingquit
, so it assumes thatquit
always remains0
. - An update to
quit
or a read ofquit
should happen in a single machine instruction. If it takes multiple machine instructions to update or readquit
, then if a signal handler is invoked while an update was going on, a read in the signal handler may see inconsistent value inquit
.
Am I correct so far? If not, please correct me in your answer.
Now I want to learn a generalized rule for when sig_atomic_t
is necessary in the context of signal handling. Jonathan Leffler has explained in the comment that it is not easy to provide a generalization.
Can you provide a list of known scenarios where a variable needs to be defined as sig_atomic_t
from C standard perspective? It need not be an exhaustive list. It could be a list a less experienced developer can refer to while writing C software with signal handling code.