Change pexecute to use vfork

Ian Lance Taylor ian@wasabisystems.com
Thu Mar 18 03:16:00 GMT 2004


I am about to commit this patch to change pexecute to use vfork
instead of fork.  I believe the patch is safe; the long comment before
the function explains why.

With this patch, a bootstrap on i386 NetBSD 1.6.1 was 0.5% faster.  A
bootstrap on Red Hat Linux 7.3 was 0.6% faster.

Ian


2004-03-17  Ian Lance Taylor  <ian@wasabisystems.com>

	* pex-unix.c (pexecute): Use vfork instead of fork, with
	appropriate changes to make this safe.
	* pex-common.h (STDERR_FILE_NO): Define.


Index: pex-unix.c
===================================================================
RCS file: /cvs/gcc/gcc/libiberty/pex-unix.c,v
retrieving revision 1.1
diff -p -u -r1.1 pex-unix.c
--- pex-unix.c	24 Jan 2003 20:02:11 -0000	1.1
+++ pex-unix.c	18 Mar 2004 03:15:49 -0000
@@ -1,7 +1,7 @@
 /* Utilities to execute a program in a subprocess (possibly linked by pipes
    with other subprocesses), and wait for it.  Generic Unix version
    (also used for UWIN and VMS).
-   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003
+   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004
    Free Software Foundation, Inc.
 
 This file is part of the libiberty library.
@@ -44,28 +44,66 @@ extern int errno;
 #define waitpid(pid, status, flags) wait(status)
 #endif
 
-extern int execv ();
-extern int execvp ();
+#ifdef vfork /* Autoconf may define this to fork for us. */
+# define VFORK_STRING "fork"
+#else
+# define VFORK_STRING "vfork"
+#endif
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif
+#ifdef VMS
+#define vfork() (decc$$alloc_vfork_blocks() >= 0 ? \
+               lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
+#endif /* VMS */
+
+/* Execute a program, possibly setting up pipes to programs executed
+   via other calls to this function.
+
+   This version of the function uses vfork.  In general vfork is
+   similar to setjmp/longmp, in that any variable which is modified by
+   the child process has an indeterminate value in the parent process.
+   We follow a safe approach here by not modifying any variables at
+   all in the child process (with the possible exception of variables
+   modified by xstrerror if exec fails, but this is unlikely to be
+   detectable).
+
+   We work a little bit harder to avoid gcc warnings.  gcc will warn
+   about any automatic variable which is live at the time of the
+   vfork, which is non-volatile, and which is either set more than
+   once or is an argument to the function.  This warning isn't quite
+   right, since what we really care about is whether the variable is
+   live at the time of the vfork and set afterward by the child
+   process, but gcc only checks whether the variable is set more than
+   once.  To avoid this warning, we ensure that any variable which is
+   live at the time of the vfork (i.e., used after the vfork) is set
+   exactly once and is not an argument, or is marked volatile.  */
 
 int
-pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
+pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg,
+	  flagsarg)
      const char *program;
      char * const *argv;
      const char *this_pname;
      const char *temp_base ATTRIBUTE_UNUSED;
      char **errmsg_fmt, **errmsg_arg;
-     int flags;
+     int flagsarg;
 {
-  int (*func)() = (flags & PEXECUTE_SEARCH ? execvp : execv);
   int pid;
   int pdes[2];
+  int out;
   int input_desc, output_desc;
-  int retries, sleep_interval;
+  int flags;
+  /* We declare these to be volatile to avoid warnings from gcc about
+     them being clobbered by vfork.  */
+  volatile int retries, sleep_interval;
   /* Pipe waiting from last process, to be used as input for the next one.
      Value is STDIN_FILE_NO if no pipe is waiting
      (i.e. the next command is the first of a group).  */
   static int last_pipe_input;
 
+  flags = flagsarg;
+
   /* If this is the first process, initialize.  */
   if (flags & PEXECUTE_FIRST)
     last_pipe_input = STDIN_FILE_NO;
@@ -82,22 +120,24 @@ pexecute (program, argv, this_pname, tem
 	  *errmsg_arg = NULL;
 	  return -1;
 	}
-      output_desc = pdes[WRITE_PORT];
+      out = pdes[WRITE_PORT];
       last_pipe_input = pdes[READ_PORT];
     }
   else
     {
       /* Last process.  */
-      output_desc = STDOUT_FILE_NO;
+      out = STDOUT_FILE_NO;
       last_pipe_input = STDIN_FILE_NO;
     }
 
+  output_desc = out;
+
   /* Fork a subprocess; wait and retry if it fails.  */
   sleep_interval = 1;
   pid = -1;
   for (retries = 0; retries < 4; retries++)
     {
-      pid = fork ();
+      pid = vfork ();
       if (pid >= 0)
 	break;
       sleep (sleep_interval);
@@ -131,12 +171,21 @@ pexecute (program, argv, this_pname, tem
 	close (last_pipe_input);
 
       /* Exec the program.  */
-      (*func) (program, argv);
-
-      fprintf (stderr, "%s: ", this_pname);
-      fprintf (stderr, install_error_msg, program);
-      fprintf (stderr, ": %s\n", xstrerror (errno));
-      exit (-1);
+      if (flags & PEXECUTE_SEARCH)
+	execvp (program, argv);
+      else
+	execv (program, argv);
+
+      /* We don't want to call fprintf after vfork.  */
+#define writeerr(s) write (STDERR_FILE_NO, s, strlen (s))
+      writeerr (this_pname);
+      writeerr (": ");
+      writeerr ("installation problem, cannot exec '");
+      writeerr (program);
+      writeerr ("': ");
+      writeerr (xstrerror (errno));
+      writeerr ("\n");
+      _exit (-1);
       /* NOTREACHED */
       return 0;
 
Index: pex-common.h
===================================================================
RCS file: /cvs/gcc/gcc/libiberty/pex-common.h,v
retrieving revision 1.1
diff -p -u -r1.1 pex-common.h
--- pex-common.h	24 Jan 2003 20:02:11 -0000	1.1
+++ pex-common.h	18 Mar 2004 03:15:49 -0000
@@ -1,6 +1,6 @@
 /* Utilities to execute a program in a subprocess (possibly linked by pipes
    with other subprocesses), and wait for it.  Shared logic.
-   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003
+   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004
    Free Software Foundation, Inc.
 
 This file is part of the libiberty library.
@@ -32,6 +32,9 @@ Boston, MA 02111-1307, USA.  */
 
 /* stdout file number.  */
 #define STDOUT_FILE_NO 1
+
+/* stderr file number.  */
+#define STDERR_FILE_NO 2
 
 /* value of `pipe': port index for reading.  */
 #define READ_PORT 0



More information about the Gcc-patches mailing list