First: the code you cite does not compile. Please post valid code;
this increases your chances to get an answer.
I'll take the freedom to reformulate your question to something like,
How to I terminate a program gracefully?
One should not reformulate questions, I know. But in the interest of sanity I have
to spread the word that asynchronous signal delivery is a mistake
that was made when UNIX was
invented. Read on below for
a little explanation of why, and some alternatives. But first,
TL;DR: do not use asynchronous signal delivery. It is very hard to get
asynchronous signal delivery right - there are many traps (thanks
@Shawn for the comment). Rather, synchronously wait for the signal
to arrive. Here's how that would look like,
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
int error;
// setup set of signals that are meant to terminate us
sigset_t termination_signals;
sigemptyset(&termination_signals);
sigaddset(&termination_signals, SIGTERM);
sigaddset(&termination_signals, SIGINT);
sigaddset(&termination_signals, SIGQUIT);
// block asynchronous delivery for those
error = sigprocmask(SIG_BLOCK, &termination_signals, NULL);
if (error) {
perror("sigprocmask(SIGTERM|SIGINT|SIGQUIT)");
exit(1);
}
// wait for one of these signals to arrive. EINTR handling is
// always good to have in larger programs. for example, libraries
// might make use of signals in their own weird way - thereby
// disturbing their users most impolitely by interrupting every
// operation they synchronously wait for.
while (1) {
int sig;
error = sigwait(&termination_signals, &sig);
if (error && errno == EINTR) {
perror("sigwait");
continue;
}
printf("received termination signal %d\n", sig);
break;
}
return 0;
}
Asynchronous Signal Delivery
Signals are a poor man's notification. It's like throwing integer
values (with predefined meanings) at processes. Originally, as they
were inventing UNIX, they had to come up with a notification mechnism
to use between processes. They did this in a way that is an analogy to
a then-popular notification mechnism: interrupts. (While this might
sound cynical, I'd bet that I'm not far off.)
Questions to ask yourself before you really decide to go that route:
- Do I really know that I need it?
- What are the consequences?
- Are there alternatives?
While I cannot help with 1., here some information for 2. and 3.
Consequences of Asynchronous Signal Handling
Asynchronous signal delivery is causing your program to enter a
certain form of parallelism: the signal handler interrupts normal
program flow. Such interruptions might not happen at times where the
program is prepared for them.
The interruption mechnism is comparable to what you might know from
multithreading, at a highest level. A common denominator of those two
might be that, "if you don't take care you are dead, but you might
not even know". Race conditions.
Asynchronous signal delivery has nothing in common with
multithreading though, apart from increasing mortality rates.
As if that's not enough, there's the concept of interrupted system
calls.
Rationale goes like this (taken from a hypothetical conversation between Ken Thompson and Dennis Ritchie, right before The Epoch),
Q: now we have defined a notification mechnism between processes
(asynchronous signal delivery), and jumped through hoops to call a
user supplied function to handle delivery.
Now what if the target process sleeps? Waits for something to
happen? Timer? Data from a serial port maybe?
A: let's wake him up!
The effect of this is that blocking system calls (like reading from a
TCP connection - these haven't been there at that time - , or from
serial ports) are interrupted. When you read from a socket, or
otherwise block until some event occurs, the call return non-zero,
with the global errno
variable (another artifact of the early
seventies) being set to EINTR
.
It is still unclear to me what the intended behavior is when the
target process has multiple threads where each thread blocks on
something. I'd expect that every such blocking call is interrupted.
Unfortunately behavior is not consistent, not even on Linuxen, let
alone other UNIXen that I haven't used for a long time. I'm not a
standards lawyer, but this alone make me run away from this arcane
mechanism.
That said, if you are still not running away, please inform yourself
about the exact definitions. The best place to start, in my opinion,
is to read the man 7 signal
manual
page. Not an easy
read though, but exact.
Next, to know what can be done in an interrupt service routine, err
signal handler function, read through the man 7 signal-safety
manual
page. You'll
see that, for example, none of the following are safe:
- No
printf()
. You use that in the signal handler, so please
don't. None of printf()
's siblings, such a fprintf()
are safe
either. None of <stdio.h>
.
<pthread.h>
is not safe. You cannot modify thread-safe data
structures because you'd inadvertently lock a mutex, or signal (pun
intended) a condition variable, or use some other threading
mechanism.
Alternatives
Synchronously await signals. The code that I cite above does that.
Event driven programming. The following is Linux specific.
At the basis of that is an event loop of some form. GUI toolkits
work this way, so if your code is part of such a larger picture, you
can hook file descriptor based signal notifications into something
that's already there.
Event sources, at their basis, are file descriptors. Sockets work this way. See man
signalfd
for
how to create a file descriptor to spit out signal notification.
Event loops, at their basis, use one of the following system calls
(order from simplest to most powerful),
Self pipe trick. See here; this is commonly used when your program is event based, but you cannot used the Linux-specific signalfd()
above.