title: Why does an exiting child process cause the parent's sigsuspend call to return on macOS?
body: <p>Consider the following C code for a POSIX system:</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;sys/types.h&gt;
  #include &lt;signal.h&gt;
  #include &lt;unistd.h&gt;
  
  #define CONTINUE_SIGNAL SIGINT
  
  void continue_handler(int sig, siginfo_t *info, void *context)
  {
      printf("[P] Continuing process with pid = %d...\n", getpid());
  }
  
  int main(void)
  {
      struct sigaction act;
      sigset_t mask;
      pid_t pid;
  
      // Block the CONTINUE_SIGNAL for now.
      sigemptyset(&amp;mask);
      sigaddset(&amp;mask, CONTINUE_SIGNAL);
      sigprocmask(SIG_BLOCK, &amp;mask, NULL);
  
      if ((pid = fork()) == 0)  {
          printf("[C] This is the child (pid %d).\n", getpid());
          return 0;
      }
  
      printf("[P] Parent (pid %d) has spawned child (pid %d).\n", getpid(), pid);
  
      // Call the 'continue_handler' when CONTINUE_SIGNAL is received.
      act.sa_sigaction = continue_handler;
      act.sa_flags = SA_SIGINFO;
      sigaction(CONTINUE_SIGNAL, &amp;act, NULL);
      printf("[P] Waiting for CONTINUE_SIGNAL...\n");
  
      // Block all signals except CONTINUE_SIGNAL and wait for it to occur.
      sigfillset(&amp;mask);
      sigdelset(&amp;mask, CONTINUE_SIGNAL);
      sigsuspend(&amp;mask);
      printf("[P] Signal received. Exiting...\n");
  
      return 0;
  }
  </code></pre>
  
  <p>Note that I have removed all error checking to be able to represent it in a more compact form. However, I have verified that all the functions return successfully in both of the following cases.</p>
  
  <p>On a Linux system, the code does exactly what I expect it to do: The parent process spawns a child process and waits for <code>SIGINT</code> before it continues to execute <code>continue_handler</code>.</p>
  
  <pre><code>[P] Parent (pid 804) has spawned child (pid 805).
  [P] Waiting for CONTINUE_SIGNAL...
  [C] This is the child (pid 805).
    &lt;press Ctrl+C&gt;
  [P] Continuing process with pid = 804...
  [P] Signal received. Exiting...
  </code></pre>
  
  <p>On macOS, however, I obtain the following output (without any interaction):</p>
  
  <pre><code>[P] Parent (pid 6024) has spawned child (pid 6025).
  [P] Waiting for CONTINUE_SIGNAL...
  [C] This is the child (pid 6025).
  [P] Signal received. Exiting...
  </code></pre>
  
  <p>Obviously, the <code>sigsuspend</code> call of the parent process (pid 6024) returns immediately after the child process exits. Although <code>SIGINT</code> does not seem to be triggered, <code>sigsuspend</code> returns an <code>errno</code> of <code>EINTR</code>, i.e., reports a successful termination by one of the non-masked signals. Note that if I prevent the child process from exiting, <code>sigsuspend</code> does <em>not</em> return by itself and instead waits for <code>SIGINT</code> to be delivered.</p>
  
  <p>Is there an issue with the way I am using the API? Or is there maybe a certain degree of flexibility in the POSIX specification that makes both of these behaviors expected?</p>