Signal-handler cleanup.
[bpt/emacs.git] / src / process.c
index 7f6f6ba..0cc9bc3 100644 (file)
@@ -23,7 +23,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #define PROCESS_INLINE EXTERN_INLINE
 
-#include <signal.h>
 #include <stdio.h>
 #include <errno.h>
 #include <setjmp.h>
@@ -116,11 +115,20 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "gnutls.h"
 #endif
 
+#ifdef HAVE_WINDOW_SYSTEM
+#include TERM_HEADER
+#endif /* HAVE_WINDOW_SYSTEM */
+
 #if defined (USE_GTK) || defined (HAVE_GCONF) || defined (HAVE_GSETTINGS)
 #include "xgselect.h"
 #endif
-#ifdef HAVE_NS
-#include "nsterm.h"
+
+#ifndef WNOHANG
+# undef waitpid
+# define waitpid(pid, status, options) wait (status)
+#endif
+#ifndef WUNTRACED
+# define WUNTRACED 0
 #endif
 
 /* Work around GCC 4.7.0 bug with strict overflow checking; see
@@ -164,16 +172,6 @@ static Lisp_Object QClocal, QCremote, QCcoding;
 static Lisp_Object QCserver, QCnowait, QCnoquery, QCstop;
 static Lisp_Object QCsentinel, QClog, QCoptions, QCplist;
 static Lisp_Object Qlast_nonmenu_event;
-/* QCfamily is declared and initialized in xfaces.c,
-   QCfilter in keyboard.c.  */
-extern Lisp_Object QCfamily, QCfilter;
-
-/* Qexit is declared and initialized in eval.c.  */
-
-/* QCfamily is defined in xfaces.c.  */
-extern Lisp_Object QCfamily;
-/* QCfilter is defined in keyboard.c.  */
-extern Lisp_Object QCfilter;
 
 #define NETCONN_P(p) (EQ (XPROCESS (p)->type, Qnetwork))
 #define NETCONN1_P(p) (EQ (p->type, Qnetwork))
@@ -810,7 +808,7 @@ get_process (register Lisp_Object name)
 #ifdef SIGCHLD
 /* Fdelete_process promises to immediately forget about the process, but in
    reality, Emacs needs to remember those processes until they have been
-   treated by sigchld_handler; otherwise this handler would consider the
+   treated by the SIGCHLD handler; otherwise this handler would consider the
    process as being synchronous and say that the synchronous process is
    dead.  */
 static Lisp_Object deleted_pid_list;
@@ -858,7 +856,8 @@ nil, indicating the current buffer's process.  */)
 #endif
        {
          Fkill_process (process, Qnil);
-         /* Do this now, since remove_process will make sigchld_handler do nothing.  */
+         /* Do this now, since remove_process will make the
+            SIGCHLD handler do nothing.  */
          pset_status (p, Fcons (Qsignal, Fcons (make_number (SIGKILL), Qnil)));
          p->tick = ++process_tick;
          status_notify (p);
@@ -1586,7 +1585,7 @@ static Lisp_Object
 start_process_unwind (Lisp_Object proc)
 {
   if (!PROCESSP (proc))
-    abort ();
+    emacs_abort ();
 
   /* Was PROC started successfully?
      -2 is used for a pty with no process, eg for gdb.  */
@@ -1612,8 +1611,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
 #if !defined (WINDOWSNT) && defined (FD_CLOEXEC)
   int wait_child_setup[2];
 #endif
-  sigset_t procmask;
-  sigset_t blocked;
+  sigset_t blocked, procmask;
   struct sigaction sigint_action;
   struct sigaction sigquit_action;
   struct sigaction sigpipe_action;
@@ -1737,7 +1735,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
   if (inchannel > max_process_desc)
     max_process_desc = inchannel;
 
-  /* Until we store the proper pid, enable sigchld_handler
+  /* Until we store the proper pid, enable the SIGCHLD handler
      to recognize an unknown pid as standing for this process.
      It is very important not to let this `marker' value stay
      in the table after this function has returned; if it does
@@ -1765,12 +1763,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
        int xforkin = forkin;
        int xforkout = forkout;
 
-#if 0 /* This was probably a mistake--it duplicates code later on,
-        but fails to handle all the cases.  */
-       /* Make sure SIGCHLD is not blocked in the child.  */
-       sigsetmask (SIGEMPTYMASK);
-#endif
-
        /* Make the pty be the controlling terminal of the process.  */
 #ifdef HAVE_PTYS
        /* First, disconnect its current controlling terminal.  */
@@ -2559,7 +2551,7 @@ static Lisp_Object
 make_serial_process_unwind (Lisp_Object proc)
 {
   if (!PROCESSP (proc))
-    abort ();
+    emacs_abort ();
   remove_process (proc);
   return Qnil;
 }
@@ -3396,7 +3388,7 @@ usage: (make-network-process &rest ARGS)  */)
       if (socktype == SOCK_DGRAM)
        {
          if (datagram_address[s].sa)
-           abort ();
+           emacs_abort ();
          datagram_address[s].sa = xmalloc (lres->ai_addrlen);
          datagram_address[s].len = lres->ai_addrlen;
          if (is_server)
@@ -3982,7 +3974,7 @@ deactivate_process (Lisp_Object proc)
          FD_CLR (inchannel, &connect_wait_mask);
          FD_CLR (inchannel, &write_mask);
          if (--num_pending_connects < 0)
-           abort ();
+           emacs_abort ();
        }
 #endif
       if (inchannel == max_process_desc)
@@ -4761,7 +4753,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
                 Cleanup occurs c/o status_notify after SIGCLD. */
              no_avail = 1; /* Cannot depend on values returned */
 #else
-             abort ();
+             emacs_abort ();
 #endif
            }
          else
@@ -4870,15 +4862,13 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
       for (channel = 0; channel <= max_input_desc; ++channel)
         {
           struct fd_callback_data *d = &fd_callback_info[channel];
-          if (FD_ISSET (channel, &Available)
-              && d->func != 0
-              && (d->condition & FOR_READ) != 0)
-            d->func (channel, d->data, 1);
-          if (FD_ISSET (channel, &write_mask)
-              && d->func != 0
-              && (d->condition & FOR_WRITE) != 0)
-            d->func (channel, d->data, 0);
-          }
+          if (d->func
+             && ((d->condition & FOR_READ
+                  && FD_ISSET (channel, &Available))
+                 || (d->condition & FOR_WRITE
+                     && FD_ISSET (channel, &write_mask))))
+            d->func (channel, d->data);
+       }
 
       for (channel = 0; channel <= max_process_desc; channel++)
        {
@@ -4967,8 +4957,8 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 
                  if (p->pid == -2)
                    {
-                     /* If the EIO occurs on a pty, sigchld_handler's
-                        waitpid() will not find the process object to
+                     /* If the EIO occurs on a pty, the SIGCHLD handler's
+                        waitpid call will not find the process object to
                         delete.  Do it here.  */
                      p->tick = ++process_tick;
                      pset_status (p, Qfailed);
@@ -5004,7 +4994,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
              FD_CLR (channel, &connect_wait_mask);
               FD_CLR (channel, &write_mask);
              if (--num_pending_connects < 0)
-               abort ();
+               emacs_abort ();
 
              proc = chan_process[channel];
              if (NILP (proc))
@@ -5198,14 +5188,14 @@ read_process_output (Lisp_Object proc, register int channel)
   /* There's no good reason to let process filters change the current
      buffer, and many callers of accept-process-output, sit-for, and
      friends don't expect current-buffer to be changed from under them.  */
-  record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
+  record_unwind_current_buffer ();
 
   /* Read and dispose of the process output.  */
   outstream = p->filter;
   if (!NILP (outstream))
     {
       Lisp_Object text;
-      int outer_running_asynch_code = running_asynch_code;
+      bool outer_running_asynch_code = running_asynch_code;
       int waiting = waiting_for_user_input_p;
 
       /* No need to gcpro these, because all we do with them later
@@ -5433,16 +5423,20 @@ read_process_output (Lisp_Object proc, register int channel)
 static jmp_buf send_process_frame;
 static Lisp_Object process_sent_to;
 
-#ifndef FORWARD_SIGNAL_TO_MAIN_THREAD
-static _Noreturn void send_process_trap (int);
-#endif
+static _Noreturn void
+handle_pipe_signal (int sig)
+{
+  sigset_t unblocked;
+  sigemptyset (&unblocked);
+  sigaddset (&unblocked, SIGPIPE);
+  pthread_sigmask (SIG_UNBLOCK, &unblocked, 0);
+  _longjmp (send_process_frame, 1);
+}
 
 static void
-send_process_trap (int ignore)
+deliver_pipe_signal (int sig)
 {
-  SIGNAL_THREAD_CHECK (SIGPIPE);
-  sigunblock (sigmask (SIGPIPE));
-  longjmp (send_process_frame, 1);
+  handle_on_main_thread (sig, handle_pipe_signal);
 }
 
 /* In send_process, when a write fails temporarily,
@@ -5535,7 +5529,7 @@ send_process (volatile Lisp_Object proc, const char *volatile buf,
   struct Lisp_Process *p = XPROCESS (proc);
   ssize_t rv;
   struct coding_system *coding;
-  void (*volatile old_sigpipe) (int);
+  struct sigaction old_sigpipe_action;
 
   if (p->raw_status_new)
     update_status (p);
@@ -5645,7 +5639,7 @@ send_process (volatile Lisp_Object proc, const char *volatile buf,
   /* 2000-09-21: Emacs 20.7, sparc-sun-solaris-2.6, GCC 2.95.2,
      CFLAGS="-g -O": The value of the parameter `proc' is clobbered
      when returning with longjmp despite being declared volatile.  */
-  if (!setjmp (send_process_frame))
+  if (!_setjmp (send_process_frame))
     {
       p = XPROCESS (proc);  /* Repair any setjmp clobbering.  */
       process_sent_to = proc;
@@ -5674,7 +5668,9 @@ send_process (volatile Lisp_Object proc, const char *volatile buf,
              /* Send this batch, using one or more write calls.  */
              ptrdiff_t written = 0;
              int outfd = p->outfd;
-             old_sigpipe = (void (*) (int)) signal (SIGPIPE, send_process_trap);
+             struct sigaction action;
+             emacs_sigaction_init (&action, deliver_pipe_signal);
+             sigaction (SIGPIPE, &action, &old_sigpipe_action);
 #ifdef DATAGRAM_SOCKETS
              if (DATAGRAM_CHAN_P (outfd))
                {
@@ -5685,7 +5681,7 @@ send_process (volatile Lisp_Object proc, const char *volatile buf,
                    written = rv;
                  else if (errno == EMSGSIZE)
                    {
-                     signal (SIGPIPE, old_sigpipe);
+                     sigaction (SIGPIPE, &old_sigpipe_action, 0);
                      report_file_error ("sending datagram",
                                         Fcons (proc, Qnil));
                    }
@@ -5710,7 +5706,7 @@ send_process (volatile Lisp_Object proc, const char *volatile buf,
                    }
 #endif
                }
-             signal (SIGPIPE, old_sigpipe);
+             sigaction (SIGPIPE, &old_sigpipe_action, 0);
 
              if (rv < 0)
                {
@@ -5770,7 +5766,7 @@ send_process (volatile Lisp_Object proc, const char *volatile buf,
     }
   else
     {
-      signal (SIGPIPE, old_sigpipe);
+      sigaction (SIGPIPE, &old_sigpipe_action, 0);
       proc = process_sent_to;
       p = XPROCESS (proc);
       p->raw_status_new = 0;
@@ -6365,7 +6361,7 @@ process has been transmitted to the serial port.  */)
 #endif /* not HAVE_SHUTDOWN */
       new_outfd = emacs_open (NULL_DEVICE, O_WRONLY, 0);
       if (new_outfd < 0)
-       abort ();
+       emacs_abort ();
       old_outfd = XPROCESS (proc)->outfd;
 
       if (!proc_encode_coding_system[new_outfd])
@@ -6408,143 +6404,135 @@ process has been transmitted to the serial port.  */)
    indirectly; if it does, that is a bug  */
 
 #ifdef SIGCHLD
-static void
-sigchld_handler (int signo)
+
+/* Record one child's changed status.  Return true if a child was found.  */
+static bool
+record_child_status_change (void)
 {
-  int old_errno = errno;
   Lisp_Object proc;
   struct Lisp_Process *p;
+  pid_t pid;
+  int w;
+  Lisp_Object tail;
 
-  SIGNAL_THREAD_CHECK (signo);
-
-  while (1)
-    {
-      pid_t pid;
-      int w;
-      Lisp_Object tail;
-
-#ifdef WNOHANG
-#ifndef WUNTRACED
-#define WUNTRACED 0
-#endif /* no WUNTRACED */
-      /* Keep trying to get a status until we get a definitive result.  */
-      do
-       {
-         errno = 0;
-         pid = waitpid (-1, &w, WNOHANG | WUNTRACED);
-       }
-      while (pid < 0 && errno == EINTR);
-
-      if (pid <= 0)
-       {
-         /* PID == 0 means no processes found, PID == -1 means a real
-            failure.  We have done all our job, so return.  */
+  do
+    pid = waitpid (-1, &w, WNOHANG | WUNTRACED);
+  while (pid < 0 && errno == EINTR);
 
-         errno = old_errno;
-         return;
-       }
-#else
-      pid = wait (&w);
-#endif /* no WNOHANG */
+  /* PID == 0 means no processes found, PID == -1 means a real failure.
+     Either way, we have done all our job.  */
+  if (pid <= 0)
+    return false;
 
-      /* Find the process that signaled us, and record its status.  */
+  /* Find the process that signaled us, and record its status.  */
 
-      /* The process can have been deleted by Fdelete_process.  */
-      for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail))
+  /* The process can have been deleted by Fdelete_process.  */
+  for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail))
+    {
+      Lisp_Object xpid = XCAR (tail);
+      if ((INTEGERP (xpid) && pid == XINT (xpid))
+         || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid)))
        {
-         Lisp_Object xpid = XCAR (tail);
-         if ((INTEGERP (xpid) && pid == XINT (xpid))
-             || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid)))
-           {
-             XSETCAR (tail, Qnil);
-             goto sigchld_end_of_loop;
-           }
+         XSETCAR (tail, Qnil);
+         return true;
        }
+    }
 
-      /* Otherwise, if it is asynchronous, it is in Vprocess_alist.  */
+  /* Otherwise, if it is asynchronous, it is in Vprocess_alist.  */
+  p = 0;
+  for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
+    {
+      proc = XCDR (XCAR (tail));
+      p = XPROCESS (proc);
+      if (EQ (p->type, Qreal) && p->pid == pid)
+       break;
       p = 0;
-      for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
-       {
-         proc = XCDR (XCAR (tail));
-         p = XPROCESS (proc);
-         if (EQ (p->type, Qreal) && p->pid == pid)
-           break;
-         p = 0;
-       }
-
-      /* Look for an asynchronous process whose pid hasn't been filled
-        in yet.  */
-      if (p == 0)
-       for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
-         {
-           proc = XCDR (XCAR (tail));
-           p = XPROCESS (proc);
-           if (p->pid == -1)
-             break;
-           p = 0;
-         }
-
-      /* Change the status of the process that was found.  */
-      if (p != 0)
-       {
-         int clear_desc_flag = 0;
+    }
 
-         p->tick = ++process_tick;
-         p->raw_status = w;
-         p->raw_status_new = 1;
+  /* Look for an asynchronous process whose pid hasn't been filled
+     in yet.  */
+  if (! p)
+    for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
+      {
+       proc = XCDR (XCAR (tail));
+       p = XPROCESS (proc);
+       if (p->pid == -1)
+         break;
+       p = 0;
+      }
 
-         /* If process has terminated, stop waiting for its output.  */
-         if ((WIFSIGNALED (w) || WIFEXITED (w))
-             && p->infd >= 0)
-           clear_desc_flag = 1;
+  /* Change the status of the process that was found.  */
+  if (p)
+    {
+      int clear_desc_flag = 0;
 
-         /* We use clear_desc_flag to avoid a compiler bug in Microsoft C.  */
-         if (clear_desc_flag)
-           {
-             FD_CLR (p->infd, &input_wait_mask);
-             FD_CLR (p->infd, &non_keyboard_wait_mask);
-           }
+      p->tick = ++process_tick;
+      p->raw_status = w;
+      p->raw_status_new = 1;
 
-         /* Tell wait_reading_process_output that it needs to wake up and
-            look around.  */
-         if (input_available_clear_time)
-           *input_available_clear_time = make_emacs_time (0, 0);
-       }
+      /* If process has terminated, stop waiting for its output.  */
+      if ((WIFSIGNALED (w) || WIFEXITED (w))
+         && p->infd >= 0)
+       clear_desc_flag = 1;
 
-      /* There was no asynchronous process found for that pid: we have
-        a synchronous process.  */
-      else
+      /* We use clear_desc_flag to avoid a compiler bug in Microsoft C.  */
+      if (clear_desc_flag)
        {
-         synch_process_alive = 0;
-
-         /* Report the status of the synchronous process.  */
-         if (WIFEXITED (w))
-           synch_process_retcode = WEXITSTATUS (w);
-         else if (WIFSIGNALED (w))
-           synch_process_termsig = WTERMSIG (w);
-
-         /* Tell wait_reading_process_output that it needs to wake up and
-            look around.  */
-         if (input_available_clear_time)
-           *input_available_clear_time = make_emacs_time (0, 0);
+         FD_CLR (p->infd, &input_wait_mask);
+         FD_CLR (p->infd, &non_keyboard_wait_mask);
        }
 
-    sigchld_end_of_loop:
-      ;
+      /* Tell wait_reading_process_output that it needs to wake up and
+        look around.  */
+      if (input_available_clear_time)
+       *input_available_clear_time = make_emacs_time (0, 0);
+    }
+  /* There was no asynchronous process found for that pid: we have
+     a synchronous process.  */
+  else
+    {
+      synch_process_alive = 0;
+
+      /* Report the status of the synchronous process.  */
+      if (WIFEXITED (w))
+       synch_process_retcode = WEXITSTATUS (w);
+      else if (WIFSIGNALED (w))
+       synch_process_termsig = WTERMSIG (w);
+
+      /* Tell wait_reading_process_output that it needs to wake up and
+        look around.  */
+      if (input_available_clear_time)
+       *input_available_clear_time = make_emacs_time (0, 0);
+    }
 
-      /* On some systems, we must return right away.
-        If any more processes want to signal us, we will
-        get another signal.
-        Otherwise (on systems that have WNOHANG), loop around
-        to use up all the processes that have something to tell us.  */
+  return true;
+}
+
+/* On some systems, the SIGCHLD handler must return right away.  If
+   any more processes want to signal us, we will get another signal.
+   Otherwise, loop around to use up all the processes that have
+   something to tell us.  */
 #if (defined WINDOWSNT \
      || (defined USG && !defined GNU_LINUX \
         && !(defined HPUX && defined WNOHANG)))
-      errno = old_errno;
-      return;
-#endif /* USG, but not HPUX with WNOHANG */
-    }
+enum { CAN_HANDLE_MULTIPLE_CHILDREN = 0 };
+#else
+enum { CAN_HANDLE_MULTIPLE_CHILDREN = 1 };
+#endif
+
+static void
+handle_child_signal (int sig)
+{
+  while (record_child_status_change () && CAN_HANDLE_MULTIPLE_CHILDREN)
+    continue;
+}
+
+static void
+deliver_child_signal (int sig)
+{
+  handle_on_main_thread (sig, handle_child_signal);
 }
+
 #endif /* SIGCHLD */
 \f
 
@@ -6569,9 +6557,9 @@ static void
 exec_sentinel (Lisp_Object proc, Lisp_Object reason)
 {
   Lisp_Object sentinel, odeactivate;
-  register struct Lisp_Process *p = XPROCESS (proc);
+  struct Lisp_Process *p = XPROCESS (proc);
   ptrdiff_t count = SPECPDL_INDEX ();
-  int outer_running_asynch_code = running_asynch_code;
+  bool outer_running_asynch_code = running_asynch_code;
   int waiting = waiting_for_user_input_p;
 
   if (inhibit_sentinels)
@@ -6589,7 +6577,7 @@ exec_sentinel (Lisp_Object proc, Lisp_Object reason)
   /* There's no good reason to let sentinels change the current
      buffer, and many callers of accept-process-output, sit-for, and
      friends don't expect current-buffer to be changed from under them.  */
-  record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
+  record_unwind_current_buffer ();
 
   sentinel = p->sentinel;
   if (NILP (sentinel))
@@ -7398,7 +7386,11 @@ init_process_emacs (void)
 #ifndef CANNOT_DUMP
   if (! noninteractive || initialized)
 #endif
-    signal (SIGCHLD, sigchld_handler);
+    {
+      struct sigaction action;
+      emacs_sigaction_init (&action, deliver_child_signal);
+      sigaction (SIGCHLD, &action, 0);
+    }
 #endif
 
   FD_ZERO (&input_wait_mask);