Simplify and avoid signal-handling races.
[bpt/emacs.git] / src / process.c
index c654369..ab21576 100644 (file)
@@ -255,11 +255,12 @@ static int keyboard_bit_set (SELECT_TYPE *);
 static void deactivate_process (Lisp_Object);
 static void status_notify (struct Lisp_Process *);
 static int read_process_output (Lisp_Object, int);
+static void handle_child_signal (int);
 static void create_pty (Lisp_Object);
 
 /* If we support a window system, turn on the code to poll periodically
    to detect C-g.  It isn't actually used when doing interrupt input.  */
-#if defined (HAVE_WINDOW_SYSTEM) && !defined (USE_ASYNC_EVENTS)
+#ifdef HAVE_WINDOW_SYSTEM
 #define POLL_FOR_INPUT
 #endif
 
@@ -1610,11 +1611,16 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
   int wait_child_setup[2];
 #endif
 #ifdef SIGCHLD
-  sigset_t blocked, procmask;
+  sigset_t blocked;
 #endif
   /* Use volatile to protect variables from being clobbered by vfork.  */
   volatile int forkin, forkout;
   volatile int pty_flag = 0;
+  volatile Lisp_Object lisp_pty_name = Qnil;
+  volatile Lisp_Object encoded_current_dir;
+#if HAVE_WORKING_VFORK
+  char **volatile save_environ;
+#endif
 
   inchannel = outchannel = -1;
 
@@ -1640,6 +1646,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
       forkin = forkout = -1;
 #endif /* not USG, or USG_SUBTTY_WORKS */
       pty_flag = 1;
+      lisp_pty_name = build_string (pty_name);
     }
   else
 #endif /* HAVE_PTYS */
@@ -1704,14 +1711,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
   XPROCESS (process)->pty_flag = pty_flag;
   pset_status (XPROCESS (process), Qrun);
 
-#ifdef SIGCHLD
-  /* Delay interrupts until we have a chance to store
-     the new fork's pid in its process structure */
-  sigemptyset (&blocked);
-  sigaddset (&blocked, SIGCHLD);
-  pthread_sigmask (SIG_BLOCK, &blocked, &procmask);
-#endif
-
   FD_SET (inchannel, &input_wait_mask);
   FD_SET (inchannel, &non_keyboard_wait_mask);
   if (inchannel > max_process_desc)
@@ -1729,89 +1728,99 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
      error. */
   setup_process_coding_systems (process);
 
-  BLOCK_INPUT;
+  encoded_current_dir = ENCODE_FILE (current_dir);
 
-  {
-    /* child_setup must clobber environ on systems with true vfork.
-       Protect it from permanent change.  */
-    char **save_environ = environ;
-    volatile Lisp_Object encoded_current_dir = ENCODE_FILE (current_dir);
+  block_input ();
+
+#ifdef SIGCHLD
+  /* Block SIGCHLD until we have a chance to store the new fork's
+     pid in its process structure.  */
+  sigemptyset (&blocked);
+  sigaddset (&blocked, SIGCHLD);
+  pthread_sigmask (SIG_BLOCK, &blocked, 0);
+#endif
+
+#if HAVE_WORKING_VFORK
+  /* child_setup must clobber environ on systems with true vfork.
+     Protect it from permanent change.  */
+  save_environ = environ;
+#endif
 
 #ifndef WINDOWSNT
-    pid = vfork ();
-    if (pid == 0)
+  pid = vfork ();
+  if (pid == 0)
 #endif /* not WINDOWSNT */
-      {
-       int xforkin = forkin;
-       int xforkout = forkout;
+    {
+      int xforkin = forkin;
+      int xforkout = forkout;
 
-       /* Make the pty be the controlling terminal of the process.  */
+      /* Make the pty be the controlling terminal of the process.  */
 #ifdef HAVE_PTYS
-       /* First, disconnect its current controlling terminal.  */
+      /* First, disconnect its current controlling terminal.  */
 #ifdef HAVE_SETSID
-       /* We tried doing setsid only if pty_flag, but it caused
-          process_set_signal to fail on SGI when using a pipe.  */
-       setsid ();
-       /* Make the pty's terminal the controlling terminal.  */
-       if (pty_flag && xforkin >= 0)
-         {
+      /* We tried doing setsid only if pty_flag, but it caused
+        process_set_signal to fail on SGI when using a pipe.  */
+      setsid ();
+      /* Make the pty's terminal the controlling terminal.  */
+      if (pty_flag && xforkin >= 0)
+       {
 #ifdef TIOCSCTTY
-           /* We ignore the return value
-              because faith@cs.unc.edu says that is necessary on Linux.  */
-           ioctl (xforkin, TIOCSCTTY, 0);
+         /* We ignore the return value
+            because faith@cs.unc.edu says that is necessary on Linux.  */
+         ioctl (xforkin, TIOCSCTTY, 0);
 #endif
-         }
+       }
 #else /* not HAVE_SETSID */
 #ifdef USG
-       /* It's very important to call setpgrp here and no time
-          afterwards.  Otherwise, we lose our controlling tty which
-          is set when we open the pty. */
-       setpgrp ();
+      /* It's very important to call setpgrp here and no time
+        afterwards.  Otherwise, we lose our controlling tty which
+        is set when we open the pty. */
+      setpgrp ();
 #endif /* USG */
 #endif /* not HAVE_SETSID */
 #if defined (LDISC1)
-       if (pty_flag && xforkin >= 0)
-         {
-           struct termios t;
-           tcgetattr (xforkin, &t);
-           t.c_lflag = LDISC1;
-           if (tcsetattr (xforkin, TCSANOW, &t) < 0)
-             emacs_write (1, "create_process/tcsetattr LDISC1 failed\n", 39);
-         }
+      if (pty_flag && xforkin >= 0)
+       {
+         struct termios t;
+         tcgetattr (xforkin, &t);
+         t.c_lflag = LDISC1;
+         if (tcsetattr (xforkin, TCSANOW, &t) < 0)
+           emacs_write (1, "create_process/tcsetattr LDISC1 failed\n", 39);
+       }
 #else
 #if defined (NTTYDISC) && defined (TIOCSETD)
-       if (pty_flag && xforkin >= 0)
-         {
-           /* Use new line discipline.  */
-           int ldisc = NTTYDISC;
-           ioctl (xforkin, TIOCSETD, &ldisc);
-         }
+      if (pty_flag && xforkin >= 0)
+       {
+         /* Use new line discipline.  */
+         int ldisc = NTTYDISC;
+         ioctl (xforkin, TIOCSETD, &ldisc);
+       }
 #endif
 #endif
 #ifdef TIOCNOTTY
-       /* In 4.3BSD, the TIOCSPGRP bug has been fixed, and now you
-          can do TIOCSPGRP only to the process's controlling tty.  */
-       if (pty_flag)
-         {
-           /* I wonder: would just ioctl (0, TIOCNOTTY, 0) work here?
-              I can't test it since I don't have 4.3.  */
-           int j = emacs_open ("/dev/tty", O_RDWR, 0);
-           if (j >= 0)
-             {
-               ioctl (j, TIOCNOTTY, 0);
-               emacs_close (j);
-             }
+      /* In 4.3BSD, the TIOCSPGRP bug has been fixed, and now you
+        can do TIOCSPGRP only to the process's controlling tty.  */
+      if (pty_flag)
+       {
+         /* I wonder: would just ioctl (0, TIOCNOTTY, 0) work here?
+            I can't test it since I don't have 4.3.  */
+         int j = emacs_open ("/dev/tty", O_RDWR, 0);
+         if (j >= 0)
+           {
+             ioctl (j, TIOCNOTTY, 0);
+             emacs_close (j);
+           }
 #ifndef USG
-           /* In order to get a controlling terminal on some versions
-              of BSD, it is necessary to put the process in pgrp 0
-              before it opens the terminal.  */
+         /* In order to get a controlling terminal on some versions
+            of BSD, it is necessary to put the process in pgrp 0
+            before it opens the terminal.  */
 #ifdef HAVE_SETPGID
-           setpgid (0, 0);
+         setpgid (0, 0);
 #else
-           setpgrp (0, 0);
+         setpgrp (0, 0);
 #endif
 #endif
-         }
+       }
 #endif /* TIOCNOTTY */
 
 #if !defined (DONT_REOPEN_PTY)
@@ -1823,70 +1832,79 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
      both HAVE_SETSID and TIOCSCTTY are defined.  */
        /* Now close the pty (if we had it open) and reopen it.
           This makes the pty the controlling terminal of the subprocess.  */
-       if (pty_flag)
-         {
+      if (pty_flag)
+       {
 
-           /* I wonder if emacs_close (emacs_open (pty_name, ...))
-              would work?  */
-           if (xforkin >= 0)
-             emacs_close (xforkin);
-           xforkout = xforkin = emacs_open (pty_name, O_RDWR, 0);
+         /* I wonder if emacs_close (emacs_open (pty_name, ...))
+            would work?  */
+         if (xforkin >= 0)
+           emacs_close (xforkin);
+         xforkout = xforkin = emacs_open (pty_name, O_RDWR, 0);
 
-           if (xforkin < 0)
-             {
-               emacs_write (1, "Couldn't open the pty terminal ", 31);
-               emacs_write (1, pty_name, strlen (pty_name));
-               emacs_write (1, "\n", 1);
-               _exit (1);
-             }
+         if (xforkin < 0)
+           {
+             emacs_write (1, "Couldn't open the pty terminal ", 31);
+             emacs_write (1, pty_name, strlen (pty_name));
+             emacs_write (1, "\n", 1);
+             _exit (1);
+           }
 
-         }
+       }
 #endif /* not DONT_REOPEN_PTY */
 
 #ifdef SETUP_SLAVE_PTY
-       if (pty_flag)
-         {
-           SETUP_SLAVE_PTY;
-         }
+      if (pty_flag)
+       {
+         SETUP_SLAVE_PTY;
+       }
 #endif /* SETUP_SLAVE_PTY */
 #ifdef AIX
-       /* On AIX, we've disabled SIGHUP above once we start a child on a pty.
-          Now reenable it in the child, so it will die when we want it to.  */
-       if (pty_flag)
-         signal (SIGHUP, SIG_DFL);
+      /* On AIX, we've disabled SIGHUP above once we start a child on a pty.
+        Now reenable it in the child, so it will die when we want it to.  */
+      if (pty_flag)
+       signal (SIGHUP, SIG_DFL);
 #endif
 #endif /* HAVE_PTYS */
 
-       signal (SIGINT, SIG_DFL);
-       signal (SIGQUIT, SIG_DFL);
-       /* GConf causes us to ignore SIGPIPE, make sure it is restored
-          in the child.  */
-       signal (SIGPIPE, SIG_DFL);
+      signal (SIGINT, SIG_DFL);
+      signal (SIGQUIT, SIG_DFL);
+
+      /* Emacs ignores SIGPIPE, but the child should not.  */
+      signal (SIGPIPE, SIG_DFL);
 
 #ifdef SIGCHLD
        /* Stop blocking signals in the child.  */
-       pthread_sigmask (SIG_SETMASK, &procmask, 0);
+      pthread_sigmask (SIG_SETMASK, &empty_mask, 0);
 #endif
 
-       if (pty_flag)
-         child_setup_tty (xforkout);
+      if (pty_flag)
+       child_setup_tty (xforkout);
 #ifdef WINDOWSNT
-       pid = child_setup (xforkin, xforkout, xforkout,
-                          new_argv, 1, encoded_current_dir);
+      pid = child_setup (xforkin, xforkout, xforkout,
+                        new_argv, 1, encoded_current_dir);
 #else  /* not WINDOWSNT */
 #ifdef FD_CLOEXEC
-       emacs_close (wait_child_setup[0]);
+      emacs_close (wait_child_setup[0]);
 #endif
-       child_setup (xforkin, xforkout, xforkout,
-                    new_argv, 1, encoded_current_dir);
+      child_setup (xforkin, xforkout, xforkout,
+                  new_argv, 1, encoded_current_dir);
 #endif /* not WINDOWSNT */
-      }
-    environ = save_environ;
-  }
+    }
+
+  /* Back in the parent process.  */
+
+#if HAVE_WORKING_VFORK
+  environ = save_environ;
+#endif
+
+  XPROCESS (process)->pid = pid;
 
-  UNBLOCK_INPUT;
+  /* Stop blocking signals in the parent.  */
+#ifdef SIGCHLD
+  pthread_sigmask (SIG_SETMASK, &empty_mask, 0);
+#endif
+  unblock_input ();
 
-  /* This runs in the Emacs process.  */
   if (pid < 0)
     {
       if (forkin >= 0)
@@ -1897,7 +1915,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
   else
     {
       /* vfork succeeded.  */
-      XPROCESS (process)->pid = pid;
 
 #ifdef WINDOWSNT
       register_child (pid, inchannel);
@@ -1923,12 +1940,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
       if (forkin != forkout && forkout >= 0)
        emacs_close (forkout);
 
-#ifdef HAVE_PTYS
-      if (pty_flag)
-       pset_tty_name (XPROCESS (process), build_string (pty_name));
-      else
-#endif
-       pset_tty_name (XPROCESS (process), Qnil);
+      pset_tty_name (XPROCESS (process), lisp_pty_name);
 
 #if !defined (WINDOWSNT) && defined (FD_CLOEXEC)
       /* Wait for child_setup to complete in case that vfork is
@@ -1945,11 +1957,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
 #endif
     }
 
-#ifdef SIGCHLD
-  /* Stop blocking signals in the parent.  */
-  pthread_sigmask (SIG_SETMASK, &procmask, 0);
-#endif
-
   /* Now generate the error if vfork failed.  */
   if (pid < 0)
     report_file_error ("Doing vfork", Qnil);
@@ -3402,9 +3409,9 @@ usage: (make-network-process &rest ARGS)  */)
 #ifdef HAVE_GETADDRINFO
   if (res != &ai)
     {
-      BLOCK_INPUT;
+      block_input ();
       freeaddrinfo (res);
-      UNBLOCK_INPUT;
+      unblock_input ();
     }
 #endif
 
@@ -4372,7 +4379,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
         Otherwise, do pending quit if requested.  */
       if (read_kbd >= 0)
        QUIT;
-      else
+      else if (pending_signals)
        process_pending_signals ();
 
       /* Exit now if the cell we're waiting for became non-nil.  */
@@ -4739,21 +4746,6 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
          check_write = 0;
        }
 
-#if 0 /* When polling is used, interrupt_input is 0,
-        so get_input_pending should read the input.
-        So this should not be needed.  */
-      /* If we are using polling for input,
-        and we see input available, make it get read now.
-        Otherwise it might not actually get read for a second.
-        And on hpux, since we turn off polling in wait_reading_process_output,
-        it might never get read at all if we don't spend much time
-        outside of wait_reading_process_output.  */
-      if (read_kbd && interrupt_input
-         && keyboard_bit_set (&Available)
-         && input_polling_used ())
-       kill (getpid (), SIGALRM);
-#endif
-
       /* Check for keyboard input */
       /* If there is any, return immediately
         to give it higher priority than subprocesses */
@@ -4817,7 +4809,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 
       if (read_kbd && interrupt_input
          && keyboard_bit_set (&Available) && ! noninteractive)
-       kill (getpid (), SIGIO);
+       handle_input_available_signal (SIGIO);
 #endif
 
       if (! wait_proc)
@@ -4937,7 +4929,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
                      pset_status (p, Qfailed);
                    }
                   else
-                   kill (getpid (), SIGCHLD);
+                   handle_child_signal (SIGCHLD);
                }
 #endif /* HAVE_PTYS */
              /* If we can detect process termination, don't consider the
@@ -5393,25 +5385,6 @@ read_process_output (Lisp_Object proc, register int channel)
 \f
 /* Sending data to subprocess */
 
-static sys_jmp_buf send_process_frame;
-static Lisp_Object process_sent_to;
-
-static _Noreturn void
-handle_pipe_signal (int sig)
-{
-  sigset_t unblocked;
-  sigemptyset (&unblocked);
-  sigaddset (&unblocked, SIGPIPE);
-  pthread_sigmask (SIG_UNBLOCK, &unblocked, 0);
-  sys_longjmp (send_process_frame, 1);
-}
-
-static void
-deliver_pipe_signal (int sig)
-{
-  handle_on_main_thread (sig, handle_pipe_signal);
-}
-
 /* In send_process, when a write fails temporarily,
    wait_reading_process_output is called.  It may execute user code,
    e.g. timers, that attempts to write new data to the same process.
@@ -5495,14 +5468,12 @@ write_queue_pop (struct Lisp_Process *p, Lisp_Object *obj,
    This function can evaluate Lisp code and can garbage collect.  */
 
 static void
-send_process (volatile Lisp_Object proc, const char *volatile buf,
-             volatile ptrdiff_t len, volatile Lisp_Object object)
+send_process (Lisp_Object proc, const char *buf, ptrdiff_t len,
+             Lisp_Object object)
 {
-  /* Use volatile to protect variables from being clobbered by longjmp.  */
   struct Lisp_Process *p = XPROCESS (proc);
   ssize_t rv;
   struct coding_system *coding;
-  struct sigaction old_sigpipe_action;
 
   if (p->raw_status_new)
     update_status (p);
@@ -5609,145 +5580,126 @@ send_process (volatile Lisp_Object proc, const char *volatile buf,
       pty_max_bytes--;
     }
 
-  /* 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 (!sys_setjmp (send_process_frame))
-    {
-      p = XPROCESS (proc);  /* Repair any setjmp clobbering.  */
-      process_sent_to = proc;
+  /* If there is already data in the write_queue, put the new data
+     in the back of queue.  Otherwise, ignore it.  */
+  if (!NILP (p->write_queue))
+    write_queue_push (p, object, buf, len, 0);
 
-      /* If there is already data in the write_queue, put the new data
-         in the back of queue.  Otherwise, ignore it.  */
-      if (!NILP (p->write_queue))
-        write_queue_push (p, object, buf, len, 0);
+  do   /* while !NILP (p->write_queue) */
+    {
+      ptrdiff_t cur_len = -1;
+      const char *cur_buf;
+      Lisp_Object cur_object;
 
-      do   /* while !NILP (p->write_queue) */
+      /* If write_queue is empty, ignore it.  */
+      if (!write_queue_pop (p, &cur_object, &cur_buf, &cur_len))
        {
-         ptrdiff_t cur_len = -1;
-         const char *cur_buf;
-         Lisp_Object cur_object;
+         cur_len = len;
+         cur_buf = buf;
+         cur_object = object;
+       }
 
-         /* If write_queue is empty, ignore it.  */
-         if (!write_queue_pop (p, &cur_object, &cur_buf, &cur_len))
+      while (cur_len > 0)
+       {
+         /* Send this batch, using one or more write calls.  */
+         ptrdiff_t written = 0;
+         int outfd = p->outfd;
+#ifdef DATAGRAM_SOCKETS
+         if (DATAGRAM_CHAN_P (outfd))
            {
-             cur_len = len;
-             cur_buf = buf;
-             cur_object = object;
+             rv = sendto (outfd, cur_buf, cur_len,
+                          0, datagram_address[outfd].sa,
+                          datagram_address[outfd].len);
+             if (0 <= rv)
+               written = rv;
+             else if (errno == EMSGSIZE)
+               report_file_error ("sending datagram", Fcons (proc, Qnil));
            }
-
-         while (cur_len > 0)
-           {
-             /* Send this batch, using one or more write calls.  */
-             ptrdiff_t written = 0;
-             int outfd = p->outfd;
-             struct sigaction action;
-             emacs_sigaction_init (&action, deliver_pipe_signal);
-             sigaction (SIGPIPE, &action, &old_sigpipe_action);
-#ifdef DATAGRAM_SOCKETS
-             if (DATAGRAM_CHAN_P (outfd))
-               {
-                 rv = sendto (outfd, cur_buf, cur_len,
-                              0, datagram_address[outfd].sa,
-                              datagram_address[outfd].len);
-                 if (0 <= rv)
-                   written = rv;
-                 else if (errno == EMSGSIZE)
-                   {
-                     sigaction (SIGPIPE, &old_sigpipe_action, 0);
-                     report_file_error ("sending datagram",
-                                        Fcons (proc, Qnil));
-                   }
-               }
-             else
+         else
 #endif
-               {
+           {
 #ifdef HAVE_GNUTLS
-                 if (p->gnutls_p)
-                   written = emacs_gnutls_write (p, cur_buf, cur_len);
-                 else
+             if (p->gnutls_p)
+               written = emacs_gnutls_write (p, cur_buf, cur_len);
+             else
 #endif
-                   written = emacs_write (outfd, cur_buf, cur_len);
-                 rv = (written ? 0 : -1);
+               written = emacs_write (outfd, cur_buf, cur_len);
+             rv = (written ? 0 : -1);
 #ifdef ADAPTIVE_READ_BUFFERING
-                 if (p->read_output_delay > 0
-                     && p->adaptive_read_buffering == 1)
-                   {
-                     p->read_output_delay = 0;
-                     process_output_delay_count--;
-                     p->read_output_skip = 0;
-                   }
-#endif
+             if (p->read_output_delay > 0
+                 && p->adaptive_read_buffering == 1)
+               {
+                 p->read_output_delay = 0;
+                 process_output_delay_count--;
+                 p->read_output_skip = 0;
                }
-             sigaction (SIGPIPE, &old_sigpipe_action, 0);
+#endif
+           }
 
-             if (rv < 0)
-               {
-                 if (0
+         if (rv < 0)
+           {
+             if (0
 #ifdef EWOULDBLOCK
-                     || errno == EWOULDBLOCK
+                 || errno == EWOULDBLOCK
 #endif
 #ifdef EAGAIN
-                     || errno == EAGAIN
+                 || errno == EAGAIN
 #endif
-                     )
-                   /* Buffer is full.  Wait, accepting input;
-                      that may allow the program
-                      to finish doing output and read more.  */
-                   {
+                 )
+               /* Buffer is full.  Wait, accepting input;
+                  that may allow the program
+                  to finish doing output and read more.  */
+               {
 #ifdef BROKEN_PTY_READ_AFTER_EAGAIN
-                     /* A gross hack to work around a bug in FreeBSD.
-                        In the following sequence, read(2) returns
-                        bogus data:
-
-                        write(2)        1022 bytes
-                        write(2)   954 bytes, get EAGAIN
-                        read(2)   1024 bytes in process_read_output
-                        read(2)     11 bytes in process_read_output
-
-                        That is, read(2) returns more bytes than have
-                        ever been written successfully.  The 1033 bytes
-                        read are the 1022 bytes written successfully
-                        after processing (for example with CRs added if
-                        the terminal is set up that way which it is
-                        here).  The same bytes will be seen again in a
-                        later read(2), without the CRs.  */
-
-                     if (errno == EAGAIN)
-                       {
-                         int flags = FWRITE;
-                         ioctl (p->outfd, TIOCFLUSH, &flags);
-                       }
+                 /* A gross hack to work around a bug in FreeBSD.
+                    In the following sequence, read(2) returns
+                    bogus data:
+
+                    write(2)    1022 bytes
+                    write(2)   954 bytes, get EAGAIN
+                    read(2)   1024 bytes in process_read_output
+                    read(2)     11 bytes in process_read_output
+
+                    That is, read(2) returns more bytes than have
+                    ever been written successfully.  The 1033 bytes
+                    read are the 1022 bytes written successfully
+                    after processing (for example with CRs added if
+                    the terminal is set up that way which it is
+                    here).  The same bytes will be seen again in a
+                    later read(2), without the CRs.  */
+
+                 if (errno == EAGAIN)
+                   {
+                     int flags = FWRITE;
+                     ioctl (p->outfd, TIOCFLUSH, &flags);
+                   }
 #endif /* BROKEN_PTY_READ_AFTER_EAGAIN */
 
-                     /* Put what we should have written in wait_queue.  */
-                     write_queue_push (p, cur_object, cur_buf, cur_len, 1);
-                     wait_reading_process_output (0, 20 * 1000 * 1000,
-                                                  0, 0, Qnil, NULL, 0);
-                     /* Reread queue, to see what is left.  */
-                     break;
-                   }
-                 else
-                   /* This is a real error.  */
-                   report_file_error ("writing to process", Fcons (proc, Qnil));
+                 /* Put what we should have written in wait_queue.  */
+                 write_queue_push (p, cur_object, cur_buf, cur_len, 1);
+                 wait_reading_process_output (0, 20 * 1000 * 1000,
+                                              0, 0, Qnil, NULL, 0);
+                 /* Reread queue, to see what is left.  */
+                 break;
                }
-             cur_buf += written;
-             cur_len -= written;
+             else if (errno == EPIPE)
+               {
+                 p->raw_status_new = 0;
+                 pset_status (p, list2 (Qexit, make_number (256)));
+                 p->tick = ++process_tick;
+                 deactivate_process (proc);
+                 error ("process %s no longer connected to pipe; closed it",
+                        SDATA (p->name));
+               }
+             else
+               /* This is a real error.  */
+               report_file_error ("writing to process", Fcons (proc, Qnil));
            }
+         cur_buf += written;
+         cur_len -= written;
        }
-      while (!NILP (p->write_queue));
-    }
-  else
-    {
-      sigaction (SIGPIPE, &old_sigpipe_action, 0);
-      proc = process_sent_to;
-      p = XPROCESS (proc);
-      p->raw_status_new = 0;
-      pset_status (p, Fcons (Qexit, Fcons (make_number (256), Qnil)));
-      p->tick = ++process_tick;
-      deactivate_process (proc);
-      error ("SIGPIPE raised on process %s; closed it", SDATA (p->name));
     }
+  while (!NILP (p->write_queue));
 }
 
 DEFUN ("process-send-region", Fprocess_send_region, Sprocess_send_region,
@@ -6178,39 +6130,27 @@ SIGCODE may be an integer, or a symbol whose name is a signal name.  */)
 #ifdef SIGUSR2
       parse_signal ("usr2", SIGUSR2);
 #endif
-#ifdef SIGTERM
       parse_signal ("term", SIGTERM);
-#endif
 #ifdef SIGHUP
       parse_signal ("hup", SIGHUP);
 #endif
-#ifdef SIGINT
       parse_signal ("int", SIGINT);
-#endif
 #ifdef SIGQUIT
       parse_signal ("quit", SIGQUIT);
 #endif
-#ifdef SIGILL
       parse_signal ("ill", SIGILL);
-#endif
-#ifdef SIGABRT
       parse_signal ("abrt", SIGABRT);
-#endif
 #ifdef SIGEMT
       parse_signal ("emt", SIGEMT);
 #endif
 #ifdef SIGKILL
       parse_signal ("kill", SIGKILL);
 #endif
-#ifdef SIGFPE
       parse_signal ("fpe", SIGFPE);
-#endif
 #ifdef SIGBUS
       parse_signal ("bus", SIGBUS);
 #endif
-#ifdef SIGSEGV
       parse_signal ("segv", SIGSEGV);
-#endif
 #ifdef SIGSYS
       parse_signal ("sys", SIGSYS);
 #endif
@@ -6375,27 +6315,15 @@ process has been transmitted to the serial port.  */)
    ** Malloc WARNING: This should never call malloc either directly or
    indirectly; if it does, that is a bug  */
 
-#ifdef SIGCHLD
-
-/* Record one child's changed status.  Return true if a child was found.  */
-static bool
-record_child_status_change (void)
+/* Record the changed status of the child process PID with wait status W.  */
+void
+record_child_status_change (pid_t pid, int w)
 {
+#ifdef SIGCHLD
   Lisp_Object proc;
   struct Lisp_Process *p;
-  pid_t pid;
-  int w;
   Lisp_Object tail;
 
-  do
-    pid = waitpid (-1, &w, WNOHANG | WUNTRACED);
-  while (pid < 0 && errno == EINTR);
-
-  /* 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.  */
 
   /* The process can have been deleted by Fdelete_process.  */
@@ -6406,7 +6334,7 @@ record_child_status_change (void)
          || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid)))
        {
          XSETCAR (tail, Qnil);
-         return true;
+         return;
        }
     }
 
@@ -6476,10 +6404,11 @@ record_child_status_change (void)
       if (input_available_clear_time)
        *input_available_clear_time = make_emacs_time (0, 0);
     }
-
-  return true;
+#endif
 }
 
+#ifdef SIGCHLD
+
 /* 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
@@ -6495,14 +6424,29 @@ enum { CAN_HANDLE_MULTIPLE_CHILDREN = 1 };
 static void
 handle_child_signal (int sig)
 {
-  while (record_child_status_change () && CAN_HANDLE_MULTIPLE_CHILDREN)
-    continue;
+  do
+    {
+      pid_t pid;
+      int status;
+
+      do
+       pid = waitpid (-1, &status, WNOHANG | WUNTRACED);
+      while (pid < 0 && errno == EINTR);
+
+      /* PID == 0 means no processes found, PID == -1 means a real failure.
+        Either way, we have done all our job.  */
+      if (pid <= 0)
+       break;
+
+      record_child_status_change (pid, status);
+    }
+  while (CAN_HANDLE_MULTIPLE_CHILDREN);
 }
 
 static void
 deliver_child_signal (int sig)
 {
-  handle_on_main_thread (sig, handle_child_signal);
+  deliver_process_signal (sig, handle_child_signal);
 }
 
 #endif /* SIGCHLD */