[llvm-commits] [llvm] r75055 - in /llvm/trunk: include/llvm/System/Program.h lib/System/Unix/Program.inc lib/System/Win32/Program.inc

David Greene greened at obbligato.org
Wed Jul 8 14:46:41 PDT 2009


Author: greened
Date: Wed Jul  8 16:46:40 2009
New Revision: 75055

URL: http://llvm.org/viewvc/llvm-project?rev=75055&view=rev
Log:

Add an ExecuteNoWait interface to support asynchronous process spawning.

Modified:
    llvm/trunk/include/llvm/System/Program.h
    llvm/trunk/lib/System/Unix/Program.inc
    llvm/trunk/lib/System/Win32/Program.inc

Modified: llvm/trunk/include/llvm/System/Program.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/System/Program.h?rev=75055&r1=75054&r2=75055&view=diff

==============================================================================
--- llvm/trunk/include/llvm/System/Program.h (original)
+++ llvm/trunk/include/llvm/System/Program.h Wed Jul  8 16:46:40 2009
@@ -87,6 +87,43 @@
       static bool ChangeStdinToBinary();
       static bool ChangeStdoutToBinary();
     /// @}
+
+     /// This function executes the program using the \p arguments provided and
+     /// waits for the program to exit. This function will block the current
+     /// program until the invoked program exits. The invoked program will
+     /// inherit the stdin, stdout, and stderr file descriptors, the
+     /// environment and other configuration settings of the invoking program.
+     /// If Path::executable() does not return true when this function is
+     /// called then a std::string is thrown.
+     /// @returns an integer result code indicating the status of the program.
+     /// A zero or positive value indicates the result code of the program. A
+     /// negative value is the signal number on which it terminated. 
+     /// @see FindProgrambyName
+     /// @brief Executes the program with the given set of \p args.
+     static void ExecuteNoWait(
+        const Path& path,  ///< sys::Path object providing the path of the 
+        ///< program to be executed. It is presumed this is the result of 
+        ///< the FindProgramByName method.
+        const char** args, ///< A vector of strings that are passed to the
+        ///< program.  The first element should be the name of the program.
+        ///< The list *must* be terminated by a null char* entry.
+        const char ** env = 0, ///< An optional vector of strings to use for
+        ///< the program's environment. If not provided, the current program's
+        ///< environment will be used.
+        const sys::Path** redirects = 0, ///< An optional array of pointers to
+        ///< Paths. If the array is null, no redirection is done. The array
+        ///< should have a size of at least three. If the pointer in the array
+        ///< are not null, then the inferior process's stdin(0), stdout(1),
+        ///< and stderr(2) will be redirected to the corresponding Paths.
+        unsigned memoryLimit = 0, ///< If non-zero, this specifies max. amount
+        ///< of memory can be allocated by process. If memory usage will be
+        ///< higher limit, the child is killed and this call returns. If zero -
+        ///< no memory limit.
+        std::string* ErrMsg = 0 ///< If non-zero, provides a pointer to a string
+        ///< instance in which error messages will be returned. If the string 
+        ///< is non-empty upon return an error occurred while invoking the
+        ///< program.
+        );
   };
 }
 }

Modified: llvm/trunk/lib/System/Unix/Program.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/System/Unix/Program.inc?rev=75055&r1=75054&r2=75055&view=diff

==============================================================================
--- llvm/trunk/lib/System/Unix/Program.inc (original)
+++ llvm/trunk/lib/System/Unix/Program.inc Wed Jul  8 16:46:40 2009
@@ -274,6 +274,78 @@
     
 }
 
+void
+Program::ExecuteNoWait(const Path& path, 
+                       const char** args,
+                       const char** envp,
+                       const Path** redirects,
+                       unsigned memoryLimit,
+                       std::string* ErrMsg) 
+{
+  if (!path.canExecute()) {
+    if (ErrMsg)
+      *ErrMsg = path.toString() + " is not executable";
+    return;
+  }
+
+  // Create a child process.
+  int child = fork();
+  switch (child) {
+    // An error occured:  Return to the caller.
+    case -1:
+      MakeErrMsg(ErrMsg, "Couldn't fork");
+      return;
+
+    // Child process: Execute the program.
+    case 0: {
+      // Redirect file descriptors...
+      if (redirects) {
+        // Redirect stdin
+        if (RedirectIO(redirects[0], 0, ErrMsg)) { return; }
+        // Redirect stdout
+        if (RedirectIO(redirects[1], 1, ErrMsg)) { return; }
+        if (redirects[1] && redirects[2] && 
+            *(redirects[1]) == *(redirects[2])) {
+          // If stdout and stderr should go to the same place, redirect stderr
+          // to the FD already open for stdout.
+          if (-1 == dup2(1,2)) {
+            MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout");
+            return;
+          }
+        } else {
+          // Just redirect stderr
+          if (RedirectIO(redirects[2], 2, ErrMsg)) { return; }
+        }
+      }
+
+      // Set memory limits
+      if (memoryLimit!=0) {
+        SetMemoryLimits(memoryLimit);
+      }
+      
+      // Execute!
+      if (envp != 0)
+        execve (path.c_str(), (char**)args, (char**)envp);
+      else
+        execv (path.c_str(), (char**)args);
+      // If the execve() failed, we should exit and let the parent pick up
+      // our non-zero exit status.
+      exit (errno);
+    }
+
+    // Parent process: Break out of the switch to do our processing.
+    default:
+      break;
+  }
+
+  // Make sure stderr and stdout have been flushed
+  std::cerr << std::flush;
+  std::cout << std::flush;
+  fsync(1);
+  fsync(2);
+
+}
+
 bool Program::ChangeStdinToBinary(){
   // Do nothing, as Unix doesn't differentiate between text and binary.
   return false;

Modified: llvm/trunk/lib/System/Win32/Program.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/System/Win32/Program.inc?rev=75055&r1=75054&r2=75055&view=diff

==============================================================================
--- llvm/trunk/lib/System/Win32/Program.inc (original)
+++ llvm/trunk/lib/System/Win32/Program.inc Wed Jul  8 16:46:40 2009
@@ -303,6 +303,171 @@
   return status;
 }
 
+void
+Program::ExecuteNoWait(const Path& path,
+                       const char** args,
+                       const char** envp,
+                       const Path** redirects,
+                       unsigned memoryLimit,
+                       std::string* ErrMsg) {
+  if (!path.canExecute()) {
+    if (ErrMsg)
+      *ErrMsg = "program not executable";
+    return;
+  }
+
+  // Windows wants a command line, not an array of args, to pass to the new
+  // process.  We have to concatenate them all, while quoting the args that
+  // have embedded spaces.
+
+  // First, determine the length of the command line.
+  unsigned len = 0;
+  for (unsigned i = 0; args[i]; i++) {
+    len += strlen(args[i]) + 1;
+    if (strchr(args[i], ' '))
+      len += 2;
+  }
+
+  // Now build the command line.
+  char *command = reinterpret_cast<char *>(_alloca(len+1));
+  char *p = command;
+
+  for (unsigned i = 0; args[i]; i++) {
+    const char *arg = args[i];
+    size_t len = strlen(arg);
+    bool needsQuoting = strchr(arg, ' ') != 0;
+    if (needsQuoting)
+      *p++ = '"';
+    memcpy(p, arg, len);
+    p += len;
+    if (needsQuoting)
+      *p++ = '"';
+    *p++ = ' ';
+  }
+
+  *p = 0;
+
+  // The pointer to the environment block for the new process.
+  char *envblock = 0;
+
+  if (envp) {
+    // An environment block consists of a null-terminated block of
+    // null-terminated strings. Convert the array of environment variables to
+    // an environment block by concatenating them.
+
+    // First, determine the length of the environment block.
+    len = 0;
+    for (unsigned i = 0; envp[i]; i++)
+      len += strlen(envp[i]) + 1;
+
+    // Now build the environment block.
+    envblock = reinterpret_cast<char *>(_alloca(len+1));
+    p = envblock;
+
+    for (unsigned i = 0; envp[i]; i++) {
+      const char *ev = envp[i];
+      size_t len = strlen(ev) + 1;
+      memcpy(p, ev, len);
+      p += len;
+    }
+
+    *p = 0;
+  }
+
+  // Create a child process.
+  STARTUPINFO si;
+  memset(&si, 0, sizeof(si));
+  si.cb = sizeof(si);
+  si.hStdInput = INVALID_HANDLE_VALUE;
+  si.hStdOutput = INVALID_HANDLE_VALUE;
+  si.hStdError = INVALID_HANDLE_VALUE;
+
+  if (redirects) {
+    si.dwFlags = STARTF_USESTDHANDLES;
+
+    si.hStdInput = RedirectIO(redirects[0], 0, ErrMsg);
+    if (si.hStdInput == INVALID_HANDLE_VALUE) {
+      MakeErrMsg(ErrMsg, "can't redirect stdin");
+      return;
+    }
+    si.hStdOutput = RedirectIO(redirects[1], 1, ErrMsg);
+    if (si.hStdOutput == INVALID_HANDLE_VALUE) {
+      CloseHandle(si.hStdInput);
+      MakeErrMsg(ErrMsg, "can't redirect stdout");
+      return;
+    }
+    if (redirects[1] && redirects[2] && *(redirects[1]) == *(redirects[2])) {
+      // If stdout and stderr should go to the same place, redirect stderr
+      // to the handle already open for stdout.
+      DuplicateHandle(GetCurrentProcess(), si.hStdOutput,
+                      GetCurrentProcess(), &si.hStdError,
+                      0, TRUE, DUPLICATE_SAME_ACCESS);
+    } else {
+      // Just redirect stderr
+      si.hStdError = RedirectIO(redirects[2], 2, ErrMsg);
+      if (si.hStdError == INVALID_HANDLE_VALUE) {
+        CloseHandle(si.hStdInput);
+        CloseHandle(si.hStdOutput);
+        MakeErrMsg(ErrMsg, "can't redirect stderr");
+        return;
+      }
+    }
+  }
+
+  PROCESS_INFORMATION pi;
+  memset(&pi, 0, sizeof(pi));
+
+  fflush(stdout);
+  fflush(stderr);
+  BOOL rc = CreateProcess(path.c_str(), command, NULL, NULL, TRUE, 0,
+                          envblock, NULL, &si, &pi);
+  DWORD err = GetLastError();
+
+  // Regardless of whether the process got created or not, we are done with
+  // the handles we created for it to inherit.
+  CloseHandle(si.hStdInput);
+  CloseHandle(si.hStdOutput);
+  CloseHandle(si.hStdError);
+
+  // Now return an error if the process didn't get created.
+  if (!rc)
+  {
+    SetLastError(err);
+    MakeErrMsg(ErrMsg, std::string("Couldn't execute program '") +
+               path.toString() + "'");
+    return;
+  }
+
+  // Make sure these get closed no matter what.
+  AutoHandle hProcess(pi.hProcess);
+  AutoHandle hThread(pi.hThread);
+
+  // Assign the process to a job if a memory limit is defined.
+  AutoHandle hJob(0);
+  if (memoryLimit != 0) {
+    hJob = CreateJobObject(0, 0);
+    bool success = false;
+    if (hJob != 0) {
+      JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
+      memset(&jeli, 0, sizeof(jeli));
+      jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY;
+      jeli.ProcessMemoryLimit = uintptr_t(memoryLimit) * 1048576;
+      if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation,
+                                  &jeli, sizeof(jeli))) {
+        if (AssignProcessToJobObject(hJob, pi.hProcess))
+          success = true;
+      }
+    }
+    if (!success) {
+      SetLastError(GetLastError());
+      MakeErrMsg(ErrMsg, std::string("Unable to set memory limit"));
+      TerminateProcess(pi.hProcess, 1);
+      WaitForSingleObject(pi.hProcess, INFINITE);
+      return;
+    }
+  }
+}
+
 bool Program::ChangeStdinToBinary(){
   int result = _setmode( _fileno(stdin), _O_BINARY );
   return result == -1;





More information about the llvm-commits mailing list