(libc.info.gz) Merged Signals

Info Catalog (libc.info.gz) Signals in Handler (libc.info.gz) Defining Handlers (libc.info.gz) Nonreentrancy
 
 24.4.5 Signals Close Together Merge into One
 --------------------------------------------
 
 If multiple signals of the same type are delivered to your process
 before your signal handler has a chance to be invoked at all, the
 handler may only be invoked once, as if only a single signal had
 arrived.  In effect, the signals merge into one.  This situation can
 arise when the signal is blocked, or in a multiprocessing environment
 where the system is busy running some other processes while the signals
 are delivered.  This means, for example, that you cannot reliably use a
 signal handler to count signals.  The only distinction you can reliably
 make is whether at least one signal has arrived since a given time in
 the past.
 
    Here is an example of a handler for `SIGCHLD' that compensates for
 the fact that the number of signals received may not equal the number of
 child processes that generate them.  It assumes that the program keeps
 track of all the child processes with a chain of structures as follows:
 
      struct process
      {
        struct process *next;
        /* The process ID of this child.  */
        int pid;
        /* The descriptor of the pipe or pseudo terminal
           on which output comes from this child.  */
        int input_descriptor;
        /* Nonzero if this process has stopped or terminated.  */
        sig_atomic_t have_status;
        /* The status of this child; 0 if running,
           otherwise a status value from `waitpid'.  */
        int status;
      };
 
      struct process *process_list;
 
    This example also uses a flag to indicate whether signals have
 arrived since some time in the past--whenever the program last cleared
 it to zero.
 
      /* Nonzero means some child's status has changed
         so look at `process_list' for the details.  */
      int process_status_change;
 
    Here is the handler itself:
 
      void
      sigchld_handler (int signo)
      {
        int old_errno = errno;
 
        while (1) {
          register int pid;
          int w;
          struct process *p;
 
          /* Keep asking for a status until we get a definitive result.  */
          do
            {
              errno = 0;
              pid = waitpid (WAIT_ANY, &w, WNOHANG | WUNTRACED);
            }
          while (pid <= 0 && errno == EINTR);
 
          if (pid <= 0) {
            /* A real failure means there are no more
               stopped or terminated child processes, so return.  */
            errno = old_errno;
            return;
          }
 
          /* Find the process that signaled us, and record its status.  */
 
          for (p = process_list; p; p = p->next)
            if (p->pid == pid) {
              p->status = w;
              /* Indicate that the `status' field
                 has data to look at.  We do this only after storing it.  */
              p->have_status = 1;
 
              /* If process has terminated, stop waiting for its output.  */
              if (WIFSIGNALED (w) || WIFEXITED (w))
                if (p->input_descriptor)
                  FD_CLR (p->input_descriptor, &input_wait_mask);
 
              /* The program should check this flag from time to time
                 to see if there is any news in `process_list'.  */
              ++process_status_change;
            }
 
          /* Loop around to handle all the processes
             that have something to tell us.  */
        }
      }
 
    Here is the proper way to check the flag `process_status_change':
 
      if (process_status_change) {
        struct process *p;
        process_status_change = 0;
        for (p = process_list; p; p = p->next)
          if (p->have_status) {
            ... Examine `p->status' ...
          }
      }
 
 It is vital to clear the flag before examining the list; otherwise, if a
 signal were delivered just before the clearing of the flag, and after
 the appropriate element of the process list had been checked, the status
 change would go unnoticed until the next signal arrived to set the flag
 again.  You could, of course, avoid this problem by blocking the signal
 while scanning the list, but it is much more elegant to guarantee
 correctness by doing things in the right order.
 
    The loop which checks process status avoids examining `p->status'
 until it sees that status has been validly stored.  This is to make sure
 that the status cannot change in the middle of accessing it.  Once
 `p->have_status' is set, it means that the child process is stopped or
 terminated, and in either case, it cannot stop or terminate again until
 the program has taken notice.   Atomic Usage, for more
 information about coping with interruptions during accesses of a
 variable.
 
    Here is another way you can test whether the handler has run since
 the last time you checked.  This technique uses a counter which is never
 changed outside the handler.  Instead of clearing the count, the program
 remembers the previous value and sees whether it has changed since the
 previous check.  The advantage of this method is that different parts of
 the program can check independently, each part checking whether there
 has been a signal since that part last checked.
 
      sig_atomic_t process_status_change;
 
      sig_atomic_t last_process_status_change;
 
      ...
      {
        sig_atomic_t prev = last_process_status_change;
        last_process_status_change = process_status_change;
        if (last_process_status_change != prev) {
          struct process *p;
          for (p = process_list; p; p = p->next)
            if (p->have_status) {
              ... Examine `p->status' ...
            }
        }
      }
 
Info Catalog (libc.info.gz) Signals in Handler (libc.info.gz) Defining Handlers (libc.info.gz) Nonreentrancy
automatically generated by info2html