| 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/files/file_util.h" | 12 #include "base/files/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 #if !defined(OS_NACL_NONSFI) | |
| 26 bool WaitpidWithTimeout(ProcessHandle handle, | |
| 27 int* status, | |
| 28 base::TimeDelta wait) { | |
| 29 // This POSIX version of this function only guarantees that we wait no less | |
| 30 // than |wait| for the process to exit. The child process may | |
| 31 // exit sometime before the timeout has ended but we may still block for up | |
| 32 // to 256 milliseconds after the fact. | |
| 33 // | |
| 34 // waitpid() has no direct support on POSIX for specifying a timeout, you can | |
| 35 // either ask it to block indefinitely or return immediately (WNOHANG). | |
| 36 // When a child process terminates a SIGCHLD signal is sent to the parent. | |
| 37 // Catching this signal would involve installing a signal handler which may | |
| 38 // affect other parts of the application and would be difficult to debug. | |
| 39 // | |
| 40 // Our strategy is to call waitpid() once up front to check if the process | |
| 41 // has already exited, otherwise to loop for |wait|, sleeping for | |
| 42 // at most 256 milliseconds each time using usleep() and then calling | |
| 43 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and | |
| 44 // we double it every 4 sleep cycles. | |
| 45 // | |
| 46 // usleep() is speced to exit if a signal is received for which a handler | |
| 47 // has been installed. This means that when a SIGCHLD is sent, it will exit | |
| 48 // depending on behavior external to this function. | |
| 49 // | |
| 50 // This function is used primarily for unit tests, if we want to use it in | |
| 51 // the application itself it would probably be best to examine other routes. | |
| 52 | |
| 53 if (wait.InMilliseconds() == base::kNoTimeout) { | |
| 54 return HANDLE_EINTR(waitpid(handle, status, 0)) > 0; | |
| 55 } | |
| 56 | |
| 57 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); | |
| 58 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. | |
| 59 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. | |
| 60 int64 double_sleep_time = 0; | |
| 61 | |
| 62 // If the process hasn't exited yet, then sleep and try again. | |
| 63 TimeTicks wakeup_time = TimeTicks::Now() + wait; | |
| 64 while (ret_pid == 0) { | |
| 65 TimeTicks now = TimeTicks::Now(); | |
| 66 if (now > wakeup_time) | |
| 67 break; | |
| 68 // Guaranteed to be non-negative! | |
| 69 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); | |
| 70 // Sleep for a bit while we wait for the process to finish. | |
| 71 if (sleep_time_usecs > max_sleep_time_usecs) | |
| 72 sleep_time_usecs = max_sleep_time_usecs; | |
| 73 | |
| 74 // usleep() will return 0 and set errno to EINTR on receipt of a signal | |
| 75 // such as SIGCHLD. | |
| 76 usleep(sleep_time_usecs); | |
| 77 ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); | |
| 78 | |
| 79 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && | |
| 80 (double_sleep_time++ % 4 == 0)) { | |
| 81 max_sleep_time_usecs *= 2; | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 return ret_pid > 0; | |
| 86 } | |
| 87 | |
| 88 #if defined(OS_MACOSX) | |
| 89 // Using kqueue on Mac so that we can wait on non-child processes. | |
| 90 // We can't use kqueues on child processes because we need to reap | |
| 91 // our own children using wait. | |
| 92 static bool WaitForSingleNonChildProcess(ProcessHandle handle, | |
| 93 TimeDelta wait) { | |
| 94 DCHECK_GT(handle, 0); | |
| 95 DCHECK(wait.InMilliseconds() == kNoTimeout || wait > TimeDelta()); | |
| 96 | |
| 97 ScopedFD kq(kqueue()); | |
| 98 if (!kq.is_valid()) { | |
| 99 DPLOG(ERROR) << "kqueue"; | |
| 100 return false; | |
| 101 } | |
| 102 | |
| 103 struct kevent change = {0}; | |
| 104 EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); | |
| 105 int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); | |
| 106 if (result == -1) { | |
| 107 if (errno == ESRCH) { | |
| 108 // If the process wasn't found, it must be dead. | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 DPLOG(ERROR) << "kevent (setup " << handle << ")"; | |
| 113 return false; | |
| 114 } | |
| 115 | |
| 116 // Keep track of the elapsed time to be able to restart kevent if it's | |
| 117 // interrupted. | |
| 118 bool wait_forever = wait.InMilliseconds() == kNoTimeout; | |
| 119 TimeDelta remaining_delta; | |
| 120 TimeTicks deadline; | |
| 121 if (!wait_forever) { | |
| 122 remaining_delta = wait; | |
| 123 deadline = TimeTicks::Now() + remaining_delta; | |
| 124 } | |
| 125 | |
| 126 result = -1; | |
| 127 struct kevent event = {0}; | |
| 128 | |
| 129 while (wait_forever || remaining_delta > TimeDelta()) { | |
| 130 struct timespec remaining_timespec; | |
| 131 struct timespec* remaining_timespec_ptr; | |
| 132 if (wait_forever) { | |
| 133 remaining_timespec_ptr = NULL; | |
| 134 } else { | |
| 135 remaining_timespec = remaining_delta.ToTimeSpec(); | |
| 136 remaining_timespec_ptr = &remaining_timespec; | |
| 137 } | |
| 138 | |
| 139 result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); | |
| 140 | |
| 141 if (result == -1 && errno == EINTR) { | |
| 142 if (!wait_forever) { | |
| 143 remaining_delta = deadline - TimeTicks::Now(); | |
| 144 } | |
| 145 result = 0; | |
| 146 } else { | |
| 147 break; | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 if (result < 0) { | |
| 152 DPLOG(ERROR) << "kevent (wait " << handle << ")"; | |
| 153 return false; | |
| 154 } else if (result > 1) { | |
| 155 DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " | |
| 156 << result; | |
| 157 return false; | |
| 158 } else if (result == 0) { | |
| 159 // Timed out. | |
| 160 return false; | |
| 161 } | |
| 162 | |
| 163 DCHECK_EQ(result, 1); | |
| 164 | |
| 165 if (event.filter != EVFILT_PROC || | |
| 166 (event.fflags & NOTE_EXIT) == 0 || | |
| 167 event.ident != static_cast<uintptr_t>(handle)) { | |
| 168 DLOG(ERROR) << "kevent (wait " << handle | |
| 169 << "): unexpected event: filter=" << event.filter | |
| 170 << ", fflags=" << event.fflags | |
| 171 << ", ident=" << event.ident; | |
| 172 return false; | |
| 173 } | |
| 174 | |
| 175 return true; | |
| 176 } | |
| 177 #endif // OS_MACOSX | |
| 178 #endif // !defined(OS_NACL_NONSFI) | |
| 179 | |
| 180 TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, | 25 TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, |
| 181 bool can_block, | 26 bool can_block, |
| 182 int* exit_code) { | 27 int* exit_code) { |
| 183 int status = 0; | 28 int status = 0; |
| 184 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, | 29 const pid_t result = HANDLE_EINTR(waitpid(handle, &status, |
| 185 can_block ? 0 : WNOHANG)); | 30 can_block ? 0 : WNOHANG)); |
| 186 if (result == -1) { | 31 if (result == -1) { |
| 187 DPLOG(ERROR) << "waitpid(" << handle << ")"; | 32 DPLOG(ERROR) << "waitpid(" << handle << ")"; |
| 188 if (exit_code) | 33 if (exit_code) |
| 189 *exit_code = 0; | 34 *exit_code = 0; |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 295 int* exit_code) { | 140 int* exit_code) { |
| 296 bool result = kill(handle, SIGKILL) == 0; | 141 bool result = kill(handle, SIGKILL) == 0; |
| 297 | 142 |
| 298 if (!result) | 143 if (!result) |
| 299 DPLOG(ERROR) << "Unable to terminate process " << handle; | 144 DPLOG(ERROR) << "Unable to terminate process " << handle; |
| 300 | 145 |
| 301 return GetTerminationStatusImpl(handle, true /* can_block */, exit_code); | 146 return GetTerminationStatusImpl(handle, true /* can_block */, exit_code); |
| 302 } | 147 } |
| 303 | 148 |
| 304 #if !defined(OS_NACL_NONSFI) | 149 #if !defined(OS_NACL_NONSFI) |
| 305 bool WaitForExitCode(ProcessHandle handle, int* exit_code) { | |
| 306 int status; | |
| 307 if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) { | |
| 308 NOTREACHED(); | |
| 309 return false; | |
| 310 } | |
| 311 | |
| 312 if (WIFEXITED(status)) { | |
| 313 *exit_code = WEXITSTATUS(status); | |
| 314 return true; | |
| 315 } | |
| 316 | |
| 317 // If it didn't exit cleanly, it must have been signaled. | |
| 318 DCHECK(WIFSIGNALED(status)); | |
| 319 return false; | |
| 320 } | |
| 321 | |
| 322 bool WaitForExitCodeWithTimeout(ProcessHandle handle, | |
| 323 int* exit_code, | |
| 324 TimeDelta timeout) { | |
| 325 ProcessHandle parent_pid = GetParentProcessId(handle); | |
| 326 ProcessHandle our_pid = GetCurrentProcessHandle(); | |
| 327 if (parent_pid != our_pid) { | |
| 328 #if defined(OS_MACOSX) | |
| 329 // On Mac we can wait on non child processes. | |
| 330 return WaitForSingleNonChildProcess(handle, timeout); | |
| 331 #else | |
| 332 // Currently on Linux we can't handle non child processes. | |
| 333 NOTIMPLEMENTED(); | |
| 334 #endif // OS_MACOSX | |
| 335 } | |
| 336 | |
| 337 int status; | |
| 338 if (!WaitpidWithTimeout(handle, &status, timeout)) | |
| 339 return false; | |
| 340 if (WIFSIGNALED(status)) { | |
| 341 *exit_code = -1; | |
| 342 return true; | |
| 343 } | |
| 344 if (WIFEXITED(status)) { | |
| 345 *exit_code = WEXITSTATUS(status); | |
| 346 return true; | |
| 347 } | |
| 348 return false; | |
| 349 } | |
| 350 | |
| 351 bool WaitForProcessesToExit(const FilePath::StringType& executable_name, | 150 bool WaitForProcessesToExit(const FilePath::StringType& executable_name, |
| 352 TimeDelta wait, | 151 TimeDelta wait, |
| 353 const ProcessFilter* filter) { | 152 const ProcessFilter* filter) { |
| 354 bool result = false; | 153 bool result = false; |
| 355 | 154 |
| 356 // TODO(port): This is inefficient, but works if there are multiple procs. | 155 // TODO(port): This is inefficient, but works if there are multiple procs. |
| 357 // TODO(port): use waitpid to avoid leaving zombies around | 156 // TODO(port): use waitpid to avoid leaving zombies around |
| 358 | 157 |
| 359 TimeTicks end_time = TimeTicks::Now() + wait; | 158 TimeTicks end_time = TimeTicks::Now() + wait; |
| 360 do { | 159 do { |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 472 return; | 271 return; |
| 473 | 272 |
| 474 BackgroundReaper* reaper = new BackgroundReaper(pid, 0); | 273 BackgroundReaper* reaper = new BackgroundReaper(pid, 0); |
| 475 PlatformThread::CreateNonJoinable(0, reaper); | 274 PlatformThread::CreateNonJoinable(0, reaper); |
| 476 } | 275 } |
| 477 | 276 |
| 478 #endif // !defined(OS_MACOSX) | 277 #endif // !defined(OS_MACOSX) |
| 479 #endif // !defined(OS_NACL_NONSFI) | 278 #endif // !defined(OS_NACL_NONSFI) |
| 480 | 279 |
| 481 } // namespace base | 280 } // namespace base |
| OLD | NEW |