...
This non-compliant code example also violates SIG31-C. Do not access or modify shared objects in signal handlers.
...
Non-Compliant Coding Example (External finite state machine)
This compliant solution example moves the finite state machine out of the signal handler, making it the handler re-entrant.
Code Block | ||
---|---|---|
| ||
#include <signal.h> volatile sig_atomic_t sig1 = 0; volatile sig_atomic_t sig2 = 0; void handler(int signum) { if (signum == SIGUSR1) { sig1 = 1; } else if (signum == SIGUSR2) { sig2 = 1; } } int main(void) { int state = 0; signal(SIGUSR1, handler); signal(SIGUSR2, handler); while (state != 2) { /* do nothing or give up CPU for a while */ if (state == 0 && sig1) { state = 1; sig2 = 0; } if (state == 1 && sig2) { state = 2; } } /* ... */ return 0; } |
There is still a race condition in this code where a SIGUSR2 sent immediately after a SIGUSR1 gets ignored. This is because the SIGUSR2 gets processed before the while loop sets the state to 1 and sig2 to 0, which erases the evidence of SIGUSR2. To completely eliminate this race condition, the OS must queue subsequent signals while one signal is being handled, and the finite state machine must be handled by the signal handler.
Compliant Solution (POSIX)
...
Code Block | ||
---|---|---|
| ||
#include <signal.h> #include <stdio.h> volatile sig_atomic_t sig1 = 0; volatile sig_atomic_t sig2 = 0; void handler(int signum) { if (signum == SIGUSR1) { sig1 = 1; } else if (sig1) { sig2 = 1; } } int main(void) { struct sigaction act; act.sa_handler = &handler; act.sa_flags = 0; if (sigfillsetsigemptyset( &act.sa_mask) != 0) { /* handle error */ } if (sigdelsetsigaddset( &act.sa_mask, SIGUSR1)) { /* handle error */ } if (sigdelsetsigaddset( &act.sa_mask, SIGUSR2)) { /* handle error */ } if (sigaction(SIGUSR1, &act, NULL) != 0) { /* handle error */ } if (sigaction(SIGUSR2, &act, NULL) != 0) { /* handle error */ } while (sig2 == 0) { /* do nothing or give up CPU for a while */ } /* ... */ return 0; } |
...