POSIX defines a function for exactly your purpose: sigtimedwait()
. It will suspend execution of the calling process until either one of the signals in a specified set becomes pending, or a specified timeout expires. Its return value indicates which of those occurred.
Within that period, if the first process provides data and sends a signal to the second, everything is OK. Otherwise, if it doesn't receive any signal from the first within that period, it will do another thing.
How can I make the second process respond to that need ?
The second process sets up the arguments for sigtimedwait
. It then blocks the expected signal (via sigprocmask
), so that the default disposition does not occur when that signal is received, and afterward calls sigtimedwait
. The return value of the latter indicates whether one of the specified signals was received (and if so, which), or whether the function returned for some other reason -- typically, because the timeout expired. In the latter case, the errno
variable can be examined to determine whether indeed it was a timeout. The manual page linked above provides some additional usage notes.
Specifically, you need two main arguments:
a sigset_t
defining the signals to block and to wait upon. The "See also" section at the end of the manual page refers you to the documentation for related and similar functions, including sigsetops(3)
, which tells you about how to manipulate such objects. For example,
#include <signal.h>
// ...
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGUSER2);
a struct timespec
, whose definition is provided inline in the manual page. That definition should be sufficient for you to come up with something like
struct timespec timeout = { .tv_sec = 10 };
Per the docs, you may specify the second argument of sigtimedwait
as NULL
if you do not need the information that otherwise could be conveyed back to you by that means, which you don't.
You can use the signal set already prepared as described above for setting up signal blocking, for example:
sigprocmask(SIG_BLOCK, &sigs, NULL);
This may seem a bit counterintuitive, since you do want to receive the signal, but the effect is to block whatever disposition has been established for the signal from occurring, which is a different path from handling it via sigtimedwait
.
At this point, then, you are ready to make the call to sigtimedwait()
:
int sig_num = sigtimedwait(&sigs, NULL, &timeout);
The result will be a signal number if one of the specified signals (only SIGUSER2
in this example) is received within the specified time, or -1 otherwise. Therefore,
if (sig_num == SIGUSER2) {
// I received the signal
// do something ...
} else {
// assert(sig_num == -1);
// I did not receive the signal within the alotted time
// do something else ...
}
You might choose to assume that a timeout occurred in the case where the signal is not received, but the most robust code would check errno
to verify that:
if (errno == EAGAIN) {
// it was a timeout
} else if (errno = EINTR) {
// a signal outside the set was received
// ... might want to calculate how much wait time is left and try again
// (left as an exercise)
} else {
// some other error occurred
// ... aborting with an error might be best:
perror("signal wait failed unexpectedly");
exit(1);
}