diff options
author | David Greene <greened@obbligato.org> | 2009-07-08 21:46:40 +0000 |
---|---|---|
committer | David Greene <greened@obbligato.org> | 2009-07-08 21:46:40 +0000 |
commit | a6bedb1233124fcb26603926c2d41605d05dace1 (patch) | |
tree | fa7dfd6c9d2f2c91806527bf317cec5927e81da6 /lib | |
parent | a22234d28be01e309f65b3c17d4a12af0f97ae0b (diff) | |
download | external_llvm-a6bedb1233124fcb26603926c2d41605d05dace1.zip external_llvm-a6bedb1233124fcb26603926c2d41605d05dace1.tar.gz external_llvm-a6bedb1233124fcb26603926c2d41605d05dace1.tar.bz2 |
Add an ExecuteNoWait interface to support asynchronous process spawning.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@75055 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/System/Unix/Program.inc | 72 | ||||
-rw-r--r-- | lib/System/Win32/Program.inc | 165 |
2 files changed, 237 insertions, 0 deletions
diff --git a/lib/System/Unix/Program.inc b/lib/System/Unix/Program.inc index cdc6fee..7e196b0 100644 --- a/lib/System/Unix/Program.inc +++ b/lib/System/Unix/Program.inc @@ -274,6 +274,78 @@ Program::ExecuteAndWait(const Path& path, } +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; diff --git a/lib/System/Win32/Program.inc b/lib/System/Win32/Program.inc index 49086b8..71f686c 100644 --- a/lib/System/Win32/Program.inc +++ b/lib/System/Win32/Program.inc @@ -303,6 +303,171 @@ Program::ExecuteAndWait(const Path& path, 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; |