Index: llvm/include/llvm/Support/Program.h =================================================================== --- llvm/include/llvm/Support/Program.h +++ llvm/include/llvm/Support/Program.h @@ -209,16 +209,21 @@ Optional SecondsToWait, ///< If None, waits until child has ///< terminated. ///< If a value, this specifies the amount of time to wait for the child - ///< process to exit. If the time expires, the child is killed and this - ///< function returns. If zero, this function will perform a non-blocking + ///< process. If the time expires, and \p Polling is false, the child is + ///< killed and this < function returns. If the time expires and \p Polling + ///< is true, the child is resumed. + ///< + ///< If zero, this function will perform a non-blocking ///< wait on the child process. std::string *ErrMsg = nullptr, ///< 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. - Optional *ProcStat = nullptr ///< If non-zero, provides + Optional *ProcStat = nullptr, ///< If non-zero, provides /// a pointer to a structure in which process execution statistics will be /// stored. + + bool Polling = false ///< If true, do not kill the process on timeout. ); /// Print a command argument, and optionally quote it. Index: llvm/lib/Support/Unix/Program.inc =================================================================== --- llvm/lib/Support/Unix/Program.inc +++ llvm/lib/Support/Unix/Program.inc @@ -385,7 +385,8 @@ ProcessInfo llvm::sys::Wait(const ProcessInfo &PI, Optional SecondsToWait, std::string *ErrMsg, - Optional *ProcStat) { + Optional *ProcStat, + bool Polling) { struct sigaction Act, Old; assert(PI.Pid && "invalid pid to wait on, process not started?"); @@ -403,7 +404,6 @@ sigemptyset(&Act.sa_mask); sigaction(SIGALRM, &Act, &Old); // FIXME The alarm signal may be delivered to another thread. - alarm(*SecondsToWait); } else WaitPidOptions = WNOHANG; @@ -425,22 +425,28 @@ return WaitResult; } else { if (SecondsToWait && errno == EINTR) { - // Kill the child. - kill(PI.Pid, SIGKILL); - - // Turn off the alarm and restore the signal handler - alarm(0); - sigaction(SIGALRM, &Old, nullptr); - - // Wait for child to die - // FIXME This could grab some other child process out from another - // waiting thread and then leave a zombie anyway. - if (wait(&status) != ChildPid) - MakeErrMsg(ErrMsg, "Child timed out but wouldn't die"); - else - MakeErrMsg(ErrMsg, "Child timed out", 0); - - WaitResult.ReturnCode = -2; // Timeout detected + if (!Polling) { + // Kill the child. + kill(PI.Pid, SIGKILL); + + // Turn off the alarm and restore the signal handler + alarm(0); + sigaction(SIGALRM, &Old, nullptr); + + // Wait for child to die + // FIXME This could grab some other child process out from another + // waiting thread and then leave a zombie anyway. + if (wait(&status) != ChildPid) + MakeErrMsg(ErrMsg, "Child timed out but wouldn't die"); + else + MakeErrMsg(ErrMsg, "Child timed out", 0); + + WaitResult.ReturnCode = -2; // Timeout detected + } else { + // Child's not done yet, resume it. + kill(PI.Pid, SIGCONT); + } + return WaitResult; } else if (errno != EINTR) { MakeErrMsg(ErrMsg, "Error waiting for child process"); Index: llvm/lib/Support/Windows/Program.inc =================================================================== --- llvm/lib/Support/Windows/Program.inc +++ llvm/lib/Support/Windows/Program.inc @@ -409,7 +409,8 @@ } ProcessInfo sys::Wait(const ProcessInfo &PI, Optional SecondsToWait, - std::string *ErrMsg, Optional *ProcStat) { + std::string *ErrMsg, Optional *ProcStat, + bool Polling) { assert(PI.Pid && "invalid pid to wait on, process not started?"); assert((PI.Process && PI.Process != INVALID_HANDLE_VALUE) && "invalid process handle to wait on, process not started?"); @@ -424,7 +425,7 @@ ProcStat->reset(); DWORD WaitStatus = WaitForSingleObject(PI.Process, milliSecondsToWait); if (WaitStatus == WAIT_TIMEOUT) { - if (SecondsToWait) { + if (SecondsToWait && !Polling) { if (!TerminateProcess(PI.Process, 1)) { if (ErrMsg) MakeErrMsg(ErrMsg, "Failed to terminate timed-out program"); Index: llvm/unittests/Support/ProgramTest.cpp =================================================================== --- llvm/unittests/Support/ProgramTest.cpp +++ llvm/unittests/Support/ProgramTest.cpp @@ -283,6 +283,44 @@ ASSERT_EQ(-2, RetCode); } +TEST_F(ProgramEnvTest, TestExecuteNoWaitTimeoutNoKill) { + using namespace llvm::sys; + + if (getenv("LLVM_PROGRAM_TEST_TIMEOUT")) { + sleep_for(/*seconds*/ 5); + exit(0); + } + + std::string Executable = + sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); + StringRef argv[] = { + Executable, "--gtest_filter=ProgramEnvTest.TestExecuteNoWaitTimeoutNoKill"}; + + // Add LLVM_PROGRAM_TEST_TIMEOUT to the environment of the child. + addEnvVar("LLVM_PROGRAM_TEST_TIMEOUT=1"); + + std::string Error; + bool ExecutionFailed; + ProcessInfo PI0 = ExecuteNoWait(Executable, argv, getEnviron(), + /*Redirects*/ {}, /*MemoryLimit*/ 0, &Error, + &ExecutionFailed); + ASSERT_FALSE(ExecutionFailed) << Error; + ASSERT_NE(PI0.Pid, ProcessInfo::InvalidPid) << "Invalid process id"; + + // Check that we don't kill the process with a non-0 SecondsToWait if Polling. + unsigned LoopCount = 0; + ProcessInfo WaitResult; + do { + ++LoopCount; + WaitResult = llvm::sys::Wait(PI0, /*SecondsToWait*/ 1, &Error, + /*ProcStats*/nullptr, + /*Polling*/true); + ASSERT_TRUE(Error.empty()) << Error; + } while (WaitResult.Pid != PI0.Pid); + + ASSERT_GT(LoopCount, 1u) << "LoopCount should be >1"; +} + TEST(ProgramTest, TestExecuteNegative) { std::string Executable = "i_dont_exist"; StringRef argv[] = {Executable};