Search code examples
csignalsforkposixwait

wait() does not return after child received sigstop


Just as the title says, one process forks waits for its child to receive a SIGSTOP (self sent by the child) but it does not wake up after as if it didn't receive any SIGCHLD (but it actually did, tested with strace).

Anyone any ideas?

int main() {
  if (fork()) {
    wait(NULL);
    write(1, "Bye\n", 4);
  } else {
    kill(getpid(), SIGSTOP);
    write(1, "Hello\n", 6);
  }
return 0;
}

They both hang, whereas only the child should.


Solution

  • The specification for wait says that it reports only children that have exited, not children that have stopped. You should use

    waitpid(-1, 0, WUNTRACED);
    

    instead.

    The flag name WUNTRACED is a little cryptic - why isn't it WSTOPPED? This is a point where the standardized wait* API brushes up against the not-standardized ptrace API. Compare POSIX's definition of WUNTRACED

    WUNTRACED
    The status of any child processes specified by pid that are stopped, and whose status has not yet been reported since they stopped, shall also be reported to the requesting process.

    … with its documentation in the Linux manpage

    WUNTRACED
    also return if a child has stopped (but not traced via ptrace(2)). Status for traced children which have stopped is provided even if this option is not specified.

    So basically the default behavior of wait* has a special case embedded in it for the convenience of people writing debuggers (people writing debuggers will take every convenience they can get) and that special case has been around for so long that it affected the names chosen for waitpid's flags. (I don't know one way or the other, but I would not be surprised to learn that ptrace stops are older than job control stops.)