On Mon, May 11, 2020 at 10:59 AM Paul Smith <psm...@gnu.org> wrote: > > As with all single-threaded applications, though, the problem is the > difficulty (in a portable way) of handling both signals and wait*(2) > reliably...
I do wonder if GNU make isn't making it worse for itself by blocking SIGCHLD. I wonder if you could just have three different file descriptors: - the "current token file descriptor" - a /dev/null file descriptor - the jobserver pipe file descriptor. This is left blocking. and then make the rule be that - the SIGCHLD handler just does dup2(nullfd, jobserverfd); This guarantees that any read() that was interrupted by a SIGCHLD will now reliably return immediately. But it also guarantees that if we raced, and the SIGCHLD happened just *before* we did the read(), the read() will also exit immediately. - the waiting code does check_child_status(); for (;;) { ret = read(jobserverfd, buffer, 1); if (ret == 1) { .. we got the token successfully, do whatever .. return; } /* The read might fail because of SIGCHLD - either we got interrupted, or the fd was replaced with /dev/null */ /* First, re-instate the pipe binding */ dup2(pipefd, jobserverfd); /* Then do the child status stuff again */ check_child_status(); /* Ok, we can restart, there's no races with SIGCHLD */ } which fundamentally doesn't have any races. Look, ma, no need for nonblocking reads, or pselect(). I don't know. Maybe I missed something. Linus