| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/process/kill.h" | 5 #include "base/process/kill.h" |
| 6 | 6 |
| 7 #include <signal.h> | 7 #include <signal.h> |
| 8 #include <sys/types.h> | 8 #include <sys/types.h> |
| 9 #include <sys/wait.h> | 9 #include <sys/wait.h> |
| 10 #include <unistd.h> | 10 #include <unistd.h> |
| 11 | 11 |
| 12 #include "base/file_util.h" | 12 #include "base/file_util.h" |
| 13 #include "base/files/scoped_file.h" | 13 #include "base/files/scoped_file.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/posix/eintr_wrapper.h" | 15 #include "base/posix/eintr_wrapper.h" |
| 16 #include "base/process/process_iterator.h" | 16 #include "base/process/process_iterator.h" |
| 17 #include "base/synchronization/waitable_event.h" | 17 #include "base/synchronization/waitable_event.h" |
| 18 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | 18 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" |
| 19 #include "base/threading/platform_thread.h" | 19 #include "base/threading/platform_thread.h" |
| 20 | 20 |
| 21 namespace base { | 21 namespace base { |
| 22 | 22 |
| 23 namespace { | 23 namespace { |
| 24 | 24 |
| 25 int WaitpidWithTimeout(ProcessHandle handle, | 25 bool WaitpidWithTimeout(ProcessHandle handle, |
| 26 int64 wait_milliseconds, | 26 int* status, |
| 27 bool* success) { | 27 base::TimeDelta wait) { |
| 28 // This POSIX version of this function only guarantees that we wait no less | 28 // This POSIX version of this function only guarantees that we wait no less |
| 29 // than |wait_milliseconds| for the process to exit. The child process may | 29 // than |wait| for the process to exit. The child process may |
| 30 // exit sometime before the timeout has ended but we may still block for up | 30 // exit sometime before the timeout has ended but we may still block for up |
| 31 // to 256 milliseconds after the fact. | 31 // to 256 milliseconds after the fact. |
| 32 // | 32 // |
| 33 // waitpid() has no direct support on POSIX for specifying a timeout, you can | 33 // waitpid() has no direct support on POSIX for specifying a timeout, you can |
| 34 // either ask it to block indefinitely or return immediately (WNOHANG). | 34 // either ask it to block indefinitely or return immediately (WNOHANG). |
| 35 // When a child process terminates a SIGCHLD signal is sent to the parent. | 35 // When a child process terminates a SIGCHLD signal is sent to the parent. |
| 36 // Catching this signal would involve installing a signal handler which may | 36 // Catching this signal would involve installing a signal handler which may |
| 37 // affect other parts of the application and would be difficult to debug. | 37 // affect other parts of the application and would be difficult to debug. |
| 38 // | 38 // |
| 39 // Our strategy is to call waitpid() once up front to check if the process | 39 // Our strategy is to call waitpid() once up front to check if the process |
| 40 // has already exited, otherwise to loop for wait_milliseconds, sleeping for | 40 // has already exited, otherwise to loop for |wait|, sleeping for |
| 41 // at most 256 milliseconds each time using usleep() and then calling | 41 // at most 256 milliseconds each time using usleep() and then calling |
| 42 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and | 42 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and |
| 43 // we double it every 4 sleep cycles. | 43 // we double it every 4 sleep cycles. |
| 44 // | 44 // |
| 45 // usleep() is speced to exit if a signal is received for which a handler | 45 // usleep() is speced to exit if a signal is received for which a handler |
| 46 // has been installed. This means that when a SIGCHLD is sent, it will exit | 46 // has been installed. This means that when a SIGCHLD is sent, it will exit |
| 47 // depending on behavior external to this function. | 47 // depending on behavior external to this function. |
| 48 // | 48 // |
| 49 // This function is used primarily for unit tests, if we want to use it in | 49 // This function is used primarily for unit tests, if we want to use it in |
| 50 // the application itself it would probably be best to examine other routes. | 50 // the application itself it would probably be best to examine other routes. |
| 51 int status = -1; | 51 |
| 52 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); | 52 if (wait.InMilliseconds() == base::kNoTimeout) { |
| 53 return HANDLE_EINTR(waitpid(handle, status, 0)) > 0; |
| 54 } |
| 55 |
| 56 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); |
| 53 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. | 57 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. |
| 54 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. | 58 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. |
| 55 int64 double_sleep_time = 0; | 59 int64 double_sleep_time = 0; |
| 56 | 60 |
| 57 // If the process hasn't exited yet, then sleep and try again. | 61 // If the process hasn't exited yet, then sleep and try again. |
| 58 TimeTicks wakeup_time = TimeTicks::Now() + | 62 TimeTicks wakeup_time = TimeTicks::Now() + wait; |
| 59 TimeDelta::FromMilliseconds(wait_milliseconds); | |
| 60 while (ret_pid == 0) { | 63 while (ret_pid == 0) { |
| 61 TimeTicks now = TimeTicks::Now(); | 64 TimeTicks now = TimeTicks::Now(); |
| 62 if (now > wakeup_time) | 65 if (now > wakeup_time) |
| 63 break; | 66 break; |
| 64 // Guaranteed to be non-negative! | 67 // Guaranteed to be non-negative! |
| 65 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); | 68 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); |
| 66 // Sleep for a bit while we wait for the process to finish. | 69 // Sleep for a bit while we wait for the process to finish. |
| 67 if (sleep_time_usecs > max_sleep_time_usecs) | 70 if (sleep_time_usecs > max_sleep_time_usecs) |
| 68 sleep_time_usecs = max_sleep_time_usecs; | 71 sleep_time_usecs = max_sleep_time_usecs; |
| 69 | 72 |
| 70 // usleep() will return 0 and set errno to EINTR on receipt of a signal | 73 // usleep() will return 0 and set errno to EINTR on receipt of a signal |
| 71 // such as SIGCHLD. | 74 // such as SIGCHLD. |
| 72 usleep(sleep_time_usecs); | 75 usleep(sleep_time_usecs); |
| 73 ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); | 76 ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); |
| 74 | 77 |
| 75 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && | 78 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && |
| 76 (double_sleep_time++ % 4 == 0)) { | 79 (double_sleep_time++ % 4 == 0)) { |
| 77 max_sleep_time_usecs *= 2; | 80 max_sleep_time_usecs *= 2; |
| 78 } | 81 } |
| 79 } | 82 } |
| 80 | 83 |
| 81 if (success) | 84 return ret_pid > 0; |
| 82 *success = (ret_pid != -1); | |
| 83 | |
| 84 return status; | |
| 85 } | 85 } |
| 86 | 86 |
| 87 TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, | 87 TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, |
| 88 bool can_block, | 88 bool can_block, |
| 89 int* exit_code) { | 89 int* exit_code) { |
| 90 int status = 0; | 90 int status = 0; |
| 91 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, | 91 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, |
| 92 can_block ? 0 : WNOHANG)); | 92 can_block ? 0 : WNOHANG)); |
| 93 if (result == -1) { | 93 if (result == -1) { |
| 94 DPLOG(ERROR) << "waitpid(" << handle << ")"; | 94 DPLOG(ERROR) << "waitpid(" << handle << ")"; |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 219 } | 219 } |
| 220 | 220 |
| 221 // If it didn't exit cleanly, it must have been signaled. | 221 // If it didn't exit cleanly, it must have been signaled. |
| 222 DCHECK(WIFSIGNALED(status)); | 222 DCHECK(WIFSIGNALED(status)); |
| 223 return false; | 223 return false; |
| 224 } | 224 } |
| 225 | 225 |
| 226 bool WaitForExitCodeWithTimeout(ProcessHandle handle, | 226 bool WaitForExitCodeWithTimeout(ProcessHandle handle, |
| 227 int* exit_code, | 227 int* exit_code, |
| 228 base::TimeDelta timeout) { | 228 base::TimeDelta timeout) { |
| 229 bool waitpid_success = false; | 229 int status; |
| 230 int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(), | 230 if (!WaitpidWithTimeout(handle, &status, timeout)) |
| 231 &waitpid_success); | |
| 232 if (status == -1) | |
| 233 return false; | |
| 234 if (!waitpid_success) | |
| 235 return false; | 231 return false; |
| 236 if (WIFSIGNALED(status)) { | 232 if (WIFSIGNALED(status)) { |
| 237 *exit_code = -1; | 233 *exit_code = -1; |
| 238 return true; | 234 return true; |
| 239 } | 235 } |
| 240 if (WIFEXITED(status)) { | 236 if (WIFEXITED(status)) { |
| 241 *exit_code = WEXITSTATUS(status); | 237 *exit_code = WEXITSTATUS(status); |
| 242 return true; | 238 return true; |
| 243 } | 239 } |
| 244 return false; | 240 return false; |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 362 if (parent_pid != our_pid) { | 358 if (parent_pid != our_pid) { |
| 363 #if defined(OS_MACOSX) | 359 #if defined(OS_MACOSX) |
| 364 // On Mac we can wait on non child processes. | 360 // On Mac we can wait on non child processes. |
| 365 return WaitForSingleNonChildProcess(handle, wait); | 361 return WaitForSingleNonChildProcess(handle, wait); |
| 366 #else | 362 #else |
| 367 // Currently on Linux we can't handle non child processes. | 363 // Currently on Linux we can't handle non child processes. |
| 368 NOTIMPLEMENTED(); | 364 NOTIMPLEMENTED(); |
| 369 #endif // OS_MACOSX | 365 #endif // OS_MACOSX |
| 370 } | 366 } |
| 371 | 367 |
| 372 bool waitpid_success; | 368 int status; |
| 373 int status = -1; | 369 if (!WaitpidWithTimeout(handle, &status, wait)) |
| 374 if (wait.InMilliseconds() == base::kNoTimeout) { | |
| 375 waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1); | |
| 376 } else { | |
| 377 status = WaitpidWithTimeout( | |
| 378 handle, wait.InMilliseconds(), &waitpid_success); | |
| 379 } | |
| 380 | |
| 381 if (status != -1) { | |
| 382 DCHECK(waitpid_success); | |
| 383 return WIFEXITED(status); | |
| 384 } else { | |
| 385 return false; | 370 return false; |
| 386 } | 371 return WIFEXITED(status); |
| 387 } | 372 } |
| 388 | 373 |
| 389 bool CleanupProcesses(const FilePath::StringType& executable_name, | 374 bool CleanupProcesses(const FilePath::StringType& executable_name, |
| 390 base::TimeDelta wait, | 375 base::TimeDelta wait, |
| 391 int exit_code, | 376 int exit_code, |
| 392 const ProcessFilter* filter) { | 377 const ProcessFilter* filter) { |
| 393 bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter); | 378 bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter); |
| 394 if (!exited_cleanly) | 379 if (!exited_cleanly) |
| 395 KillProcesses(executable_name, exit_code, filter); | 380 KillProcesses(executable_name, exit_code, filter); |
| 396 return exited_cleanly; | 381 return exited_cleanly; |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 488 if (IsChildDead(process)) | 473 if (IsChildDead(process)) |
| 489 return; | 474 return; |
| 490 | 475 |
| 491 BackgroundReaper* reaper = new BackgroundReaper(process, 0); | 476 BackgroundReaper* reaper = new BackgroundReaper(process, 0); |
| 492 PlatformThread::CreateNonJoinable(0, reaper); | 477 PlatformThread::CreateNonJoinable(0, reaper); |
| 493 } | 478 } |
| 494 | 479 |
| 495 #endif // !defined(OS_MACOSX) | 480 #endif // !defined(OS_MACOSX) |
| 496 | 481 |
| 497 } // namespace base | 482 } // namespace base |
| OLD | NEW |