Index: llvm/include/llvm/Support/Program.h =================================================================== --- llvm/include/llvm/Support/Program.h +++ llvm/include/llvm/Support/Program.h @@ -210,17 +210,22 @@ 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 + ///< 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 + 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 @@ -386,7 +386,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?"); @@ -407,12 +408,11 @@ sigemptyset(&Act.sa_mask); sigaction(SIGALRM, &Act, &Old); // FIXME The alarm signal may be delivered to another thread. - alarm(*SecondsToWait); } // Parent process: Wait for the child process to terminate. - int status; + int status = 0; ProcessInfo WaitResult; rusage Info; if (ProcStat) @@ -427,7 +427,7 @@ // Non-blocking wait. return WaitResult; } else { - if (SecondsToWait && errno == EINTR) { + if (SecondsToWait && errno == EINTR && !Polling) { // 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 @@ -411,7 +411,8 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, std::optional SecondsToWait, std::string *ErrMsg, - std::optional *ProcStat) { + 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?"); @@ -422,7 +423,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};