diff --git a/src/startup.c b/src/startup.c index 3d400a16..2fb1d689 100644 --- a/src/startup.c +++ b/src/startup.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #ifdef __FreeBSD__ @@ -381,35 +382,13 @@ buryChild(int foo) int status; /* R.I.P. */ - /* SIGCHLD's are blocked while we are in this signal handler, and if 2 or - * more kids exit while we handle the exit of one child in this handler - * (ie, about at the same time), we will miss the SIGCHLD signals the - * other kids will send and will be left with zombies. Using a while loop - * tries to avoid this (or at least minimize the probability), by calling - * waitpid until there are no more exited kids that respond. - * I think there is still a small probability that a a SIGCHLD signal can - * arrive after we finished the while loop, but before we return. - * Passing SA_NODEFER to the SIGCHLD handler doesn't seem to help at all, - * though tha man page sais that using this flag should allow receival of - * other SIGCHLD signals while processing one. Why is this? -Dan - * - * There seem to be SA_NOCLDWAIT, but I'm not sure its available on all - * platforms, and its possible that even if it will prevent zombie - * generation, it may also prevent us from receiving the exit status - * of our kids (I'm not sure, because the man page doesn't state this - * clearly). -Dan - * - * Maybe the best way would be to forget about SA_NODEFER and do it the - * old way: unblock SIGCHLD immediately after we entered this handler, - * then call waitpid in a while loop to get the exit status of all kids - * that exited while SIGCHLD was blocked and we missed the signal. - * This way there is no way we can miss any kid, because the while loop - * takes care of signals that were missed before we reenabled SIGCHLD - * and after enabling the signal, we we start getting them even if we - * are in the signal handler itself. Only need to check if it doesn't - * have any problem with reentrancy, but I don't think so. -Dan + /* If 2 or more kids exit in a small time window, before this handler gets + * the chance to get invoked, the SIGCHLD signals will be merged and only + * one SIGCHLD signal will be sent to us. We use a while loop to get all + * exited child status because we can't count on the number of SIGCHLD + * signals to know exactly how many kids have exited. -Dan */ - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + while ((pid=waitpid(-1, &status, WNOHANG))>0 || (pid<0 && errno==EINTR)) { NotifyDeadProcess(pid, WEXITSTATUS(status)); /* * Make sure that the kid will be buried even if there are @@ -418,7 +397,7 @@ buryChild(int foo) if (!WDelayedActionSet) { WDelayedActionSet = 1; WMAddIdleHandler(delayedAction, NULL); - } + } } } @@ -824,9 +803,8 @@ StartUp(Bool defaultScreenOnly) sigaction(SIGPIPE, &sig_action, NULL); /* handle dead children */ - /* Using SA_NODEFER doesn't seem to have any effect. Why? -Dan */ sig_action.sa_handler = buryChild; - sig_action.sa_flags = SA_NODEFER|SA_NOCLDSTOP|SA_RESTART; + sig_action.sa_flags = SA_NOCLDSTOP|SA_RESTART; sigaction(SIGCHLD, &sig_action, NULL); /* Now we unblock all signals, that may have been blocked by the parent