Avoid vfork-related deadlock more cleanly.
authorPaul Eggert <eggert@cs.ucla.edu>
Sun, 21 Jul 2013 06:53:47 +0000 (23:53 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Sun, 21 Jul 2013 06:53:47 +0000 (23:53 -0700)
* callproc.c (child_setup): When the child's exec fails, output
the program name, as that's more useful.  Use O_NONBLOCK to avoid
deadlock.
* process.c (create_process_1): Remove; no longer needed.
(create_process): Remove timer hack; no longer needed, now that
the child avoids deadlock.

src/ChangeLog
src/callproc.c
src/process.c

index 24738f4..e97e07a 100644 (file)
@@ -1,3 +1,13 @@
+2013-07-21  Paul Eggert  <eggert@cs.ucla.edu>
+
+       Avoid vfork-related deadlock more cleanly.
+       * callproc.c (child_setup): When the child's exec fails, output
+       the program name, as that's more useful.  Use O_NONBLOCK to avoid
+       deadlock.
+       * process.c (create_process_1): Remove; no longer needed.
+       (create_process): Remove timer hack; no longer needed, now that
+       the child avoids deadlock.
+
 2013-07-20  Glenn Morris  <rgm@gnu.org>
 
        * image.c (Fimage_flush): Fix doc typo.
index e0040ad..282a080 100644 (file)
@@ -1193,6 +1193,7 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp,
 {
   char **env;
   char *pwd_var;
+  int exec_errno;
 #ifdef WINDOWSNT
   int cpid;
   HANDLE handles[3];
@@ -1368,13 +1369,16 @@ child_setup (int in, int out, int err, char **new_argv, bool set_pgrp,
   tcsetpgrp (0, pid);
 
   execve (new_argv[0], new_argv, env);
+  exec_errno = errno;
 
-  /* Don't output the program name here, as it can be arbitrarily long,
-     and a long write from a vforked child to its parent can cause a
-     deadlock.  */
-  emacs_perror ("child process");
+  /* Avoid deadlock if the child's perror writes to a full pipe; the
+     pipe's reader is the parent, but with vfork the parent can't
+     run until the child exits.  Truncate the diagnostic instead.  */
+  fcntl (STDERR_FILENO, F_SETFL, O_NONBLOCK);
 
-  _exit (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
+  errno = exec_errno;
+  emacs_perror (new_argv[0]);
+  _exit (exec_errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
 
 #else /* MSDOS */
   pid = run_msdos_command (new_argv, pwd_var + 4, in, out, err, env);
index 12035da..34783fa 100644 (file)
@@ -1599,12 +1599,6 @@ start_process_unwind (Lisp_Object proc)
     remove_process (proc);
 }
 
-static void
-create_process_1 (struct atimer *timer)
-{
-  /* Nothing to do.  */
-}
-
 
 static void
 create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
@@ -1841,14 +1835,13 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
   unblock_child_signal ();
   unblock_input ();
 
+  if (forkin >= 0)
+    emacs_close (forkin);
+  if (forkin != forkout && forkout >= 0)
+    emacs_close (forkout);
+
   if (pid < 0)
-    {
-      if (forkin >= 0)
-       emacs_close (forkin);
-      if (forkin != forkout && forkout >= 0)
-       emacs_close (forkout);
-      report_file_errno ("Doing vfork", Qnil, vfork_errno);
-    }
+    report_file_errno ("Doing vfork", Qnil, vfork_errno);
   else
     {
       /* vfork succeeded.  */
@@ -1857,26 +1850,6 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
       register_child (pid, inchannel);
 #endif /* WINDOWSNT */
 
-      /* If the subfork execv fails, and it exits,
-        this close hangs.  I don't know why.
-        So have an interrupt jar it loose.  */
-      {
-       struct atimer *timer;
-       EMACS_TIME offset = make_emacs_time (1, 0);
-
-       stop_polling ();
-       timer = start_atimer (ATIMER_RELATIVE, offset, create_process_1, 0);
-
-       if (forkin >= 0)
-         emacs_close (forkin);
-
-       cancel_atimer (timer);
-       start_polling ();
-      }
-
-      if (forkin != forkout && forkout >= 0)
-       emacs_close (forkout);
-
       pset_tty_name (XPROCESS (process), lisp_pty_name);
 
 #ifndef WINDOWSNT