The code below sets an short-term alarm, fork()s a shorter-lived child, and then calls sigsuspend awaiting only a SIGALRM. I would expect it to die by SIGALRM.
Instead, on OS X, the sigsuspend appears to be interrupted by the child's termination, even though SIGCHLD is masked during the call. That is contrary to my expectation of POSIX behavior and my read of the OS X man page, and contrary to observed behavior on at least Linux and FreeBSD (reported on Stackoverflow).
Catching SIGCHLD or leaving it at SIG_DFL makes no difference, and, indeed, a user-defined signal handler never appears to run in any case. Only "natural" child termination appears to cause this. kill -CHLD $mypid won't do it.
What is going on?
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#define ALARM_TIMEOUT 5
#define CHILD_SLEEP 2
static volatile sig_atomic_t saw_chld = 0;
static
void noop(int s, siginfo_t *si, void *c) {
saw_chld = 1;
}
int
main(int argc, char **argv) {
alarm(ALARM_TIMEOUT);
if (argc > 1) {
printf("parent catching CHLD\n");
struct sigaction sa = { 0 };
sa.sa_sigaction = noop;
sa.sa_flags = SA_SIGINFO;
sigfillset(&sa.sa_mask);
(void)sigaction(SIGCHLD, &sa, NULL);
}
if (fork() == (pid_t)0) { // FIXME - error checking
printf("child sleeping\n");
sleep(CHILD_SLEEP);
printf("child exit\n");
exit(0);
}
sigset_t mask;
sigfillset(&mask);
sigdelset(&mask, SIGALRM);
printf("parent suspending\n");
errno = 0;
int r = sigsuspend(&mask);
printf("parent sigsuspend() => %d (errno %d)\n", r, errno);
if (argc > 1) printf("parent saw SIGCHLD? %d\n", saw_chld);
exit(0);
}When I run the above on OS X, I see:
$ ./demo
parent suspending
child sleeping
child exit
parent sigsuspend() => -1 (errno 4)
$ ./demo catch
parent catching CHLD
parent suspending
child sleeping
child exit
parent sigsuspend() => -1 (errno 4)
parent saw SIGCHLD? 0