llvm.org GIT mirror llvm / c269c4f
Add non-blocking Wait() for launched processes - New ProcessInfo class to encapsulate information about child processes. - Generalized the Wait() to support non-blocking wait on child processes. - ExecuteNoWait() now returns a ProcessInfo object with information about the launched child. Users will be able to use this object to perform non-blocking wait. - ExecuteNoWait() now accepts an ExecutionFailed param that tells if execution failed or not. These changes will allow users to implement basic process parallel tools. Differential Revision: http://llvm-reviews.chandlerc.com/D1728 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@191763 91177308-0d34-0410-b5e6-96231b3b80d8 Tareq A. Siraj 6 years ago
5 changed file(s) with 313 addition(s) and 145 deletion(s). Raw diff Collapse all Expand all
2828 #elif defined (LLVM_ON_WIN32)
2929 const char EnvPathSeparator = ';';
3030 #endif
31
32 /// @brief This struct encapsulates information about a process.
33 struct ProcessInfo {
34 #if defined(LLVM_ON_UNIX)
35 typedef pid_t ProcessId;
36 #elif defined(LLVM_ON_WIN32)
37 typedef unsigned long ProcessId; // Must match the type of DWORD on Windows.
38 typedef void * HANDLE; // Must match the type of HANDLE on Windows.
39 /// The handle to the process (available on Windows only).
40 HANDLE ProcessHandle;
41 #else
42 #error "ProcessInfo is not defined for this platform!"
43 #endif
44
45 /// The process identifier.
46 ProcessId Pid;
47
48 /// The return code, set after execution.
49 int ReturnCode;
50
51 ProcessInfo();
52 };
3153
3254 /// This static constructor (factory) will attempt to locate a program in
3355 /// the operating system's file system using some pre-determined set of
86108 ///< program.
87109 bool *ExecutionFailed = 0);
88110
89 /// Similar to ExecuteAndWait, but return immediately.
90 void ExecuteNoWait(StringRef Program, const char **args, const char **env = 0,
91 const StringRef **redirects = 0, unsigned memoryLimit = 0,
92 std::string *ErrMsg = 0);
111 /// Similar to ExecuteAndWait, but returns immediately.
112 /// @returns The \see ProcessInfo of the newly launced process.
113 /// \Note On Microsoft Windows systems, users will need to either call \see
114 /// Wait until the process finished execution or win32 CloseHandle() API on
115 /// ProcessInfo.ProcessHandle to avoid memory leaks.
116 ProcessInfo
117 ExecuteNoWait(StringRef Program, const char **args, const char **env = 0,
118 const StringRef **redirects = 0, unsigned memoryLimit = 0,
119 std::string *ErrMsg = 0, bool *ExecutionFailed = 0);
93120
94 // Return true if the given arguments fit within system-specific
95 // argument length limits.
121 /// Return true if the given arguments fit within system-specific
122 /// argument length limits.
96123 bool argumentsFitWithinSystemLimits(ArrayRef Args);
97 }
124
125 /// This function waits for the process specified by \p PI to finish.
126 /// \returns A \see ProcessInfo struct with Pid set to:
127 /// \li The process id of the child process if the child process has changed
128 /// state.
129 /// \li 0 if the child process has not changed state.
130 /// \Note Users of this function should always check the ReturnCode member of
131 /// the \see ProcessInfo returned from this function.
132 ProcessInfo Wait(
133 const ProcessInfo &PI, ///< The child process that should be waited on.
134 unsigned SecondsToWait, ///< If non-zero, this specifies the amount of
135 ///< time to wait for the child process to exit. If the time expires, the
136 ///< child is killed and this function returns. If zero, this function
137 ///< will perform a non-blocking wait on the child process.
138 bool WaitUntilTerminates, ///< If true, ignores \p SecondsToWait and waits
139 ///< until child has terminated.
140 std::string *ErrMsg = 0 ///< If non-zero, provides a pointer to a string
141 ///< instance in which error messages will be returned. If the string
142 ///< is non-empty upon return an error occurred while invoking the
143 ///< program.
144 );
145 }
98146 }
99147
100148 #endif
2121 //=== independent code.
2222 //===----------------------------------------------------------------------===//
2323
24 static bool Execute(void **Data, StringRef Program, const char **args,
24 static bool Execute(ProcessInfo &PI, StringRef Program, const char **args,
2525 const char **env, const StringRef **Redirects,
2626 unsigned memoryLimit, std::string *ErrMsg);
27
28 static int Wait(void *&Data, StringRef Program, unsigned secondsToWait,
29 std::string *ErrMsg);
3027
3128 int sys::ExecuteAndWait(StringRef Program, const char **args, const char **envp,
3229 const StringRef **redirects, unsigned secondsToWait,
3330 unsigned memoryLimit, std::string *ErrMsg,
3431 bool *ExecutionFailed) {
35 void *Data = 0;
36 if (Execute(&Data, Program, args, envp, redirects, memoryLimit, ErrMsg)) {
37 if (ExecutionFailed) *ExecutionFailed = false;
38 return Wait(Data, Program, secondsToWait, ErrMsg);
32 ProcessInfo PI;
33 if (Execute(PI, Program, args, envp, redirects, memoryLimit, ErrMsg)) {
34 if (ExecutionFailed)
35 *ExecutionFailed = false;
36 ProcessInfo Result = Wait(PI, secondsToWait, true, ErrMsg);
37 return Result.ReturnCode;
3938 }
40 if (ExecutionFailed) *ExecutionFailed = true;
39
40 if (ExecutionFailed)
41 *ExecutionFailed = true;
42
4143 return -1;
4244 }
4345
44 void sys::ExecuteNoWait(StringRef Program, const char **args, const char **envp,
45 const StringRef **redirects, unsigned memoryLimit,
46 std::string *ErrMsg) {
47 Execute(/*Data*/ 0, Program, args, envp, redirects, memoryLimit, ErrMsg);
46 ProcessInfo sys::ExecuteNoWait(StringRef Program, const char **args,
47 const char **envp, const StringRef **redirects,
48 unsigned memoryLimit, std::string *ErrMsg,
49 bool *ExecutionFailed) {
50 ProcessInfo PI;
51 if (ExecutionFailed)
52 *ExecutionFailed = false;
53 if (!Execute(PI, Program, args, envp, redirects, memoryLimit, ErrMsg))
54 if (ExecutionFailed)
55 *ExecutionFailed = true;
56
57 return PI;
4858 }
4959
5060 // Include the platform-specific parts of this class.
4646 namespace llvm {
4747 using namespace sys;
4848
49 ProcessInfo::ProcessInfo() : Pid(0), ReturnCode(0) {}
50
4951 // This function just uses the PATH environment variable to find the program.
5052 std::string
5153 sys::FindProgramByName(const std::string& progName) {
174176
175177 }
176178
177 static bool Execute(void **Data, StringRef Program, const char **args,
179 static bool Execute(ProcessInfo &PI, StringRef Program, const char **args,
178180 const char **envp, const StringRef **redirects,
179181 unsigned memoryLimit, std::string *ErrMsg) {
182 if (!llvm::sys::fs::exists(Program)) {
183 if (ErrMsg)
184 *ErrMsg = std::string("Executable \"") + Program.str() +
185 std::string("\" doesn't exist!");
186 return false;
187 }
188
180189 // If this OS has posix_spawn and there is no memory limit being implied, use
181190 // posix_spawn. It is more efficient than fork/exec.
182191 #ifdef HAVE_POSIX_SPAWN
238247 if (Err)
239248 return !MakeErrMsg(ErrMsg, "posix_spawn failed", Err);
240249
241 if (Data)
242 *Data = reinterpret_cast(PID);
250 PI.Pid = PID;
251
243252 return true;
244253 }
245254 #endif
302311 break;
303312 }
304313
305 if (Data)
306 *Data = reinterpret_cast(child);
314 PI.Pid = child;
307315
308316 return true;
309317 }
310318
311 static int Wait(void *&Data, StringRef Program, unsigned secondsToWait,
312 std::string *ErrMsg) {
319 namespace llvm {
320
321 ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait,
322 bool WaitUntilTerminates, std::string *ErrMsg) {
313323 #ifdef HAVE_SYS_WAIT_H
314324 struct sigaction Act, Old;
315 assert(Data && "invalid pid to wait on, process not started?");
316
317 // Install a timeout handler. The handler itself does nothing, but the simple
318 // fact of having a handler at all causes the wait below to return with EINTR,
319 // unlike if we used SIG_IGN.
320 if (secondsToWait) {
325 assert(PI.Pid && "invalid pid to wait on, process not started?");
326
327 int WaitPidOptions = 0;
328 pid_t ChildPid = PI.Pid;
329 if (WaitUntilTerminates) {
330 SecondsToWait = 0;
331 ChildPid = -1; // mimic a wait() using waitpid()
332 } else if (SecondsToWait) {
333 // Install a timeout handler. The handler itself does nothing, but the
334 // simple fact of having a handler at all causes the wait below to return
335 // with EINTR, unlike if we used SIG_IGN.
321336 memset(&Act, 0, sizeof(Act));
322337 Act.sa_handler = TimeOutHandler;
323338 sigemptyset(&Act.sa_mask);
324339 sigaction(SIGALRM, &Act, &Old);
325 alarm(secondsToWait);
326 }
340 alarm(SecondsToWait);
341 } else if (SecondsToWait == 0)
342 WaitPidOptions = WNOHANG;
327343
328344 // Parent process: Wait for the child process to terminate.
329345 int status;
330 uint64_t pid = reinterpret_cast(Data);
331 pid_t child = static_cast(pid);
332 while (waitpid(pid, &status, 0) != child)
333 if (secondsToWait && errno == EINTR) {
334 // Kill the child.
335 kill(child, SIGKILL);
336
337 // Turn off the alarm and restore the signal handler
338 alarm(0);
339 sigaction(SIGALRM, &Old, 0);
340
341 // Wait for child to die
342 if (wait(&status) != child)
343 MakeErrMsg(ErrMsg, "Child timed out but wouldn't die");
344 else
345 MakeErrMsg(ErrMsg, "Child timed out", 0);
346
347 return -2; // Timeout detected
348 } else if (errno != EINTR) {
349 MakeErrMsg(ErrMsg, "Error waiting for child process");
350 return -1;
351 }
346 ProcessInfo WaitResult;
347 WaitResult.Pid = waitpid(ChildPid, &status, WaitPidOptions);
348 if (WaitResult.Pid != PI.Pid) {
349 if (WaitResult.Pid == 0) {
350 // Non-blocking wait.
351 return WaitResult;
352 } else {
353 if (SecondsToWait && errno == EINTR) {
354 // Kill the child.
355 kill(PI.Pid, SIGKILL);
356
357 // Turn off the alarm and restore the signal handler
358 alarm(0);
359 sigaction(SIGALRM, &Old, 0);
360
361 // Wait for child to die
362 if (wait(&status) != ChildPid)
363 MakeErrMsg(ErrMsg, "Child timed out but wouldn't die");
364 else
365 MakeErrMsg(ErrMsg, "Child timed out", 0);
366
367 WaitResult.ReturnCode = -2; // Timeout detected
368 return WaitResult;
369 } else if (errno != EINTR) {
370 MakeErrMsg(ErrMsg, "Error waiting for child process");
371 WaitResult.ReturnCode = -1;
372 return WaitResult;
373 }
374 }
375 }
352376
353377 // We exited normally without timeout, so turn off the timer.
354 if (secondsToWait) {
378 if (SecondsToWait && !WaitUntilTerminates) {
355379 alarm(0);
356380 sigaction(SIGALRM, &Old, 0);
357381 }
361385 int result = 0;
362386 if (WIFEXITED(status)) {
363387 result = WEXITSTATUS(status);
364 #ifdef HAVE_POSIX_SPAWN
365 // The posix_spawn child process returns 127 on any kind of error.
366 // Following the POSIX convention for command-line tools (which posix_spawn
367 // itself apparently does not), check to see if the failure was due to some
368 // reason other than the file not existing, and return 126 in this case.
369 bool Exists;
370 if (result == 127 && !llvm::sys::fs::exists(Program, Exists) && Exists)
371 result = 126;
372 #endif
388 WaitResult.ReturnCode = result;
389
373390 if (result == 127) {
374391 if (ErrMsg)
375392 *ErrMsg = llvm::sys::StrError(ENOENT);
376 return -1;
393 WaitResult.ReturnCode = -1;
394 return WaitResult;
377395 }
378396 if (result == 126) {
379397 if (ErrMsg)
380398 *ErrMsg = "Program could not be executed";
381 return -1;
399 WaitResult.ReturnCode = -1;
400 return WaitResult;
382401 }
383402 } else if (WIFSIGNALED(status)) {
384403 if (ErrMsg) {
390409 }
391410 // Return a special value to indicate that the process received an unhandled
392411 // signal during execution as opposed to failing to execute.
393 return -2;
394 }
395 return result;
412 WaitResult.ReturnCode = -2;
413 }
396414 #else
397415 if (ErrMsg)
398416 *ErrMsg = "Program::Wait is not implemented on this platform yet!";
399 return -1;
400 #endif
401 }
402
403 namespace llvm {
417 WaitResult.ReturnCode = -2;
418 #endif
419 return WaitResult;
420 }
404421
405422 error_code sys::ChangeStdinToBinary(){
406423 // Do nothing, as Unix doesn't differentiate between text and binary.
437454 }
438455 return true;
439456 }
440
441 }
457 }
2323 //=== and must not be UNIX code
2424 //===----------------------------------------------------------------------===//
2525
26 namespace {
27 struct Win32ProcessInfo {
28 HANDLE hProcess;
29 DWORD dwProcessId;
30 };
31 }
32
3326 namespace llvm {
3427 using namespace sys;
28
29 ProcessInfo::ProcessInfo() : Pid(0), ProcessHandle(0), ReturnCode(0) {}
3530
3631 // This function just uses the PATH environment variable to find the program.
3732 std::string sys::FindProgramByName(const std::string &progName) {
170165
171166 }
172167
173 static bool Execute(void **Data,
174 StringRef Program,
175 const char** args,
176 const char** envp,
177 const StringRef** redirects,
178 unsigned memoryLimit,
179 std::string* ErrMsg) {
168 static bool Execute(ProcessInfo &PI, StringRef Program, const char **args,
169 const char **envp, const StringRef **redirects,
170 unsigned memoryLimit, std::string *ErrMsg) {
180171 if (!sys::fs::can_execute(Program)) {
181172 if (ErrMsg)
182173 *ErrMsg = "program not executable";
315306 ProgramStr + "'");
316307 return false;
317308 }
318 if (Data) {
319 Win32ProcessInfo* wpi = new Win32ProcessInfo;
320 wpi->hProcess = pi.hProcess;
321 wpi->dwProcessId = pi.dwProcessId;
322 *Data = wpi;
323 }
309
310 PI.Pid = pi.dwProcessId;
311 PI.ProcessHandle = pi.hProcess;
324312
325313 // Make sure these get closed no matter what.
326314 ScopedCommonHandle hThread(pi.hThread);
350338 }
351339 }
352340
353 // Don't leak the handle if the caller doesn't want it.
354 if (!Data)
355 CloseHandle(pi.hProcess);
356
357341 return true;
358342 }
359343
360 static int WaitAux(Win32ProcessInfo *wpi, unsigned secondsToWait,
361 std::string *ErrMsg) {
362 // Wait for the process to terminate.
363 HANDLE hProcess = wpi->hProcess;
364 DWORD millisecondsToWait = INFINITE;
365 if (secondsToWait > 0)
366 millisecondsToWait = secondsToWait * 1000;
367
368 if (WaitForSingleObject(hProcess, millisecondsToWait) == WAIT_TIMEOUT) {
369 if (!TerminateProcess(hProcess, 1)) {
370 MakeErrMsg(ErrMsg, "Failed to terminate timed-out program.");
371 // -2 indicates a crash or timeout as opposed to failure to execute.
372 return -2;
373 }
374 WaitForSingleObject(hProcess, INFINITE);
344 namespace llvm {
345 ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait,
346 bool WaitUntilChildTerminates, std::string *ErrMsg) {
347 assert(PI.Pid && "invalid pid to wait on, process not started?");
348 assert(PI.ProcessHandle &&
349 "invalid process handle to wait on, process not started?");
350 DWORD milliSecondsToWait = 0;
351 if (WaitUntilChildTerminates)
352 milliSecondsToWait = INFINITE;
353 else if (SecondsToWait > 0)
354 milliSecondsToWait = SecondsToWait * 1000;
355
356 ProcessInfo WaitResult = PI;
357 DWORD WaitStatus = WaitForSingleObject(PI.ProcessHandle, milliSecondsToWait);
358 if (WaitStatus == WAIT_TIMEOUT) {
359 if (SecondsToWait) {
360 if (!TerminateProcess(PI.ProcessHandle, 1)) {
361 if (ErrMsg)
362 MakeErrMsg(ErrMsg, "Failed to terminate timed-out program.");
363
364 // -2 indicates a crash or timeout as opposed to failure to execute.
365 WaitResult.ReturnCode = -2;
366 CloseHandle(PI.ProcessHandle);
367 return WaitResult;
368 }
369 WaitForSingleObject(PI.ProcessHandle, INFINITE);
370 CloseHandle(PI.ProcessHandle);
371 } else {
372 // Non-blocking wait.
373 return ProcessInfo();
374 }
375375 }
376376
377377 // Get its exit status.
378378 DWORD status;
379 BOOL rc = GetExitCodeProcess(hProcess, &status);
379 BOOL rc = GetExitCodeProcess(PI.ProcessHandle, &status);
380380 DWORD err = GetLastError();
381 CloseHandle(PI.ProcessHandle);
381382
382383 if (!rc) {
383384 SetLastError(err);
384 MakeErrMsg(ErrMsg, "Failed getting status for program.");
385 if (ErrMsg)
386 MakeErrMsg(ErrMsg, "Failed getting status for program.");
387
385388 // -2 indicates a crash or timeout as opposed to failure to execute.
386 return -2;
389 WaitResult.ReturnCode = -2;
390 return WaitResult;
387391 }
388392
389393 if (!status)
390 return 0;
394 return WaitResult;
391395
392396 // Pass 10(Warning) and 11(Error) to the callee as negative value.
393397 if ((status & 0xBFFF0000U) == 0x80000000U)
394 return (int)status;
395
396 if (status & 0xFF)
397 return status & 0x7FFFFFFF;
398
399 return 1;
400 }
401
402 static int Wait(void *&Data, StringRef Program, unsigned secondsToWait,
403 std::string *ErrMsg) {
404 Win32ProcessInfo *wpi = reinterpret_cast(Data);
405 int Ret = WaitAux(wpi, secondsToWait, ErrMsg);
406
407 CloseHandle(wpi->hProcess);
408 delete wpi;
409 Data = 0;
410
411 return Ret;
412 }
413
414 namespace llvm {
398 WaitResult.ReturnCode = static_cast(status);
399 else if (status & 0xFF)
400 WaitResult.ReturnCode = status & 0x7FFFFFFF;
401 else
402 WaitResult.ReturnCode = 1;
403
404 return WaitResult;
405 }
406
415407 error_code sys::ChangeStdinToBinary(){
416408 int result = _setmode( _fileno(stdin), _O_BINARY );
417409 if (result == -1)
448440 }
449441 return true;
450442 }
451
452 }
443 }
1818 #elif !defined(_MSC_VER)
1919 // Forward declare environ in case it's not provided by stdlib.h.
2020 extern char **environ;
21 #endif
22
23 #if defined(LLVM_ON_UNIX)
24 #include
25 void sleep_for(unsigned int seconds) {
26 sleep(seconds);
27 }
28 #elif defined(LLVM_ON_WIN32)
29 #include
30 void sleep_for(unsigned int seconds) {
31 Sleep(seconds * 1000);
32 }
33 #else
34 #error sleep_for is not implemented on your platform.
2135 #endif
2236
2337 // From TestMain.cpp.
87101 EXPECT_EQ(0, rc);
88102 }
89103
104 TEST(ProgramTest, TestExecuteNoWait) {
105 using namespace llvm::sys;
106
107 if (getenv("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT")) {
108 sleep_for(/*seconds*/ 1);
109 exit(0);
110 }
111
112 std::string Executable =
113 sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
114 const char *argv[] = {
115 Executable.c_str(),
116 "--gtest_filter=ProgramTest.TestExecuteNoWait",
117 0
118 };
119
120 // Add LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT to the environment of the child.
121 std::vector envp;
122 CopyEnvironment(envp);
123 envp.push_back("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT=1");
124 envp.push_back(0);
125
126 std::string Error;
127 bool ExecutionFailed;
128 ProcessInfo PI1 =
129 ExecuteNoWait(Executable, argv, &envp[0], 0, 0, &Error, &ExecutionFailed);
130 ASSERT_FALSE(ExecutionFailed) << Error;
131 ASSERT_NE(PI1.Pid, 0) << "Invalid process id";
132
133 unsigned LoopCount = 0;
134
135 // Test that Wait() with WaitUntilTerminates=true works. In this case,
136 // LoopCount should only be incremented once.
137 while (true) {
138 ++LoopCount;
139 ProcessInfo WaitResult = Wait(PI1, 0, true, &Error);
140 ASSERT_TRUE(Error.empty());
141 if (WaitResult.Pid == PI1.Pid)
142 break;
143 }
144
145 EXPECT_EQ(LoopCount, 1u) << "LoopCount should be 1";
146
147 ProcessInfo PI2 =
148 ExecuteNoWait(Executable, argv, &envp[0], 0, 0, &Error, &ExecutionFailed);
149 ASSERT_FALSE(ExecutionFailed) << Error;
150 ASSERT_NE(PI2.Pid, 0) << "Invalid process id";
151
152 // Test that Wait() with SecondsToWait=0 performs a non-blocking wait. In this
153 // cse, LoopCount should be greater than 1 (more than one increment occurs).
154 while (true) {
155 ++LoopCount;
156 ProcessInfo WaitResult = Wait(PI2, 0, false, &Error);
157 ASSERT_TRUE(Error.empty());
158 if (WaitResult.Pid == PI2.Pid)
159 break;
160 }
161
162 ASSERT_GT(LoopCount, 1u) << "LoopCount should be >1";
163 }
164
165 TEST(ProgramTest, TestExecuteNegative) {
166 std::string Executable = "i_dont_exist";
167 const char *argv[] = { Executable.c_str(), 0 };
168
169 {
170 std::string Error;
171 bool ExecutionFailed;
172 int RetCode =
173 ExecuteAndWait(Executable, argv, 0, 0, 0, 0, &Error, &ExecutionFailed);
174 ASSERT_TRUE(RetCode < 0) << "On error ExecuteAndWait should return 0 or "
175 "positive value indicating the result code";
176 ASSERT_TRUE(ExecutionFailed);
177 ASSERT_FALSE(Error.empty());
178 }
179
180 {
181 std::string Error;
182 bool ExecutionFailed;
183 ProcessInfo PI =
184 ExecuteNoWait(Executable, argv, 0, 0, 0, &Error, &ExecutionFailed);
185 ASSERT_EQ(PI.Pid, 0)
186 << "On error ExecuteNoWait should return an invalid ProcessInfo";
187 ASSERT_TRUE(ExecutionFailed);
188 ASSERT_FALSE(Error.empty());
189 }
190
191 }
192
90193 } // end anonymous namespace