Index: llvm/include/llvm/Support/Program.h =================================================================== --- llvm/include/llvm/Support/Program.h +++ llvm/include/llvm/Support/Program.h @@ -205,22 +205,27 @@ /// \li 0 if the child process has not changed state. /// \note Users of this function should always check the ReturnCode member of /// the \see ProcessInfo returned from this function. - ProcessInfo Wait( - const ProcessInfo &PI, ///< The child process that should be waited on. - std::optional SecondsToWait, ///< If std::nullopt, 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 - ///< 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. - std::optional *ProcStat = - nullptr ///< If non-zero, provides - /// a pointer to a structure in which process execution statistics will - /// be stored. + ProcessInfo + Wait(const ProcessInfo &PI, ///< The child process that should be waited on. + std::optional SecondsToWait, ///< If std::nullopt, waits until + ///< child has terminated. + ///< If a value, this specifies the amount of time to wait for the child + ///< 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. + std::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, std::optional SecondsToWait, std::string *ErrMsg, - std::optional *ProcStat) { + std::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; @@ -426,6 +426,9 @@ return WaitResult; } else { if (SecondsToWait && errno == EINTR) { + if (Polling) + return WaitResult; + // Kill the child. kill(PI.Pid, SIGKILL); 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, std::optional SecondsToWait, - std::string *ErrMsg, std::optional *ProcStat) { + std::string *ErrMsg, std::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?"); @@ -420,7 +421,7 @@ ProcStat->reset(); DWORD WaitStatus = WaitForSingleObject(PI.Process, milliSecondsToWait); if (WaitStatus == WAIT_TIMEOUT) { - if (*SecondsToWait > 0) { + if (!Polling && *SecondsToWait > 0) { 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 @@ -284,6 +284,45 @@ ASSERT_EQ(-2, RetCode); } +TEST_F(ProgramEnvTest, TestExecuteNoWaitTimeoutPolling) { + 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.TestExecuteNoWaitTimeoutPolling"}; + + // 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};