From ee01079735d08d5d9481b26d89f87257ac02efff Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 20 Jul 2013 23:53:47 -0700 Subject: [PATCH] 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. --- src/ChangeLog | 10 ++++++++++ src/callproc.c | 14 +++++++++----- src/process.c | 39 ++++++--------------------------------- 3 files changed, 25 insertions(+), 38 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 24738f49183..e97e07aac60 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,13 @@ +2013-07-21 Paul Eggert + + 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 * image.c (Fimage_flush): Fix doc typo. diff --git a/src/callproc.c b/src/callproc.c index e0040ada609..282a08056f8 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -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); diff --git a/src/process.c b/src/process.c index 12035da7b58..34783fae5fd 100644 --- a/src/process.c +++ b/src/process.c @@ -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 -- 2.11.4.GIT