OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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/process.h" | 5 #include "base/process/process.h" |
6 | 6 |
7 #include <sys/resource.h> | 7 #include <sys/resource.h> |
8 #include <sys/time.h> | 8 #include <sys/wait.h> |
9 #include <sys/types.h> | 9 |
10 | 10 #include "base/files/scoped_file.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/posix/eintr_wrapper.h" |
12 #include "base/process/kill.h" | 13 #include "base/process/kill.h" |
13 | 14 |
| 15 #if defined(OS_MACOSX) |
| 16 #include <sys/event.h> |
| 17 #endif |
| 18 |
| 19 namespace { |
| 20 |
| 21 #if !defined(OS_NACL_NONSFI) |
| 22 |
| 23 bool WaitpidWithTimeout(base::ProcessHandle handle, |
| 24 int* status, |
| 25 base::TimeDelta wait) { |
| 26 // This POSIX version of this function only guarantees that we wait no less |
| 27 // than |wait| for the process to exit. The child process may |
| 28 // exit sometime before the timeout has ended but we may still block for up |
| 29 // to 256 milliseconds after the fact. |
| 30 // |
| 31 // waitpid() has no direct support on POSIX for specifying a timeout, you can |
| 32 // either ask it to block indefinitely or return immediately (WNOHANG). |
| 33 // When a child process terminates a SIGCHLD signal is sent to the parent. |
| 34 // Catching this signal would involve installing a signal handler which may |
| 35 // affect other parts of the application and would be difficult to debug. |
| 36 // |
| 37 // Our strategy is to call waitpid() once up front to check if the process |
| 38 // has already exited, otherwise to loop for |wait|, sleeping for |
| 39 // at most 256 milliseconds each time using usleep() and then calling |
| 40 // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and |
| 41 // we double it every 4 sleep cycles. |
| 42 // |
| 43 // usleep() is speced to exit if a signal is received for which a handler |
| 44 // has been installed. This means that when a SIGCHLD is sent, it will exit |
| 45 // depending on behavior external to this function. |
| 46 // |
| 47 // This function is used primarily for unit tests, if we want to use it in |
| 48 // the application itself it would probably be best to examine other routes. |
| 49 |
| 50 if (wait == base::TimeDelta::Max()) { |
| 51 return HANDLE_EINTR(waitpid(handle, status, 0)) > 0; |
| 52 } |
| 53 |
| 54 pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); |
| 55 static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. |
| 56 int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. |
| 57 int64 double_sleep_time = 0; |
| 58 |
| 59 // If the process hasn't exited yet, then sleep and try again. |
| 60 base::TimeTicks wakeup_time = base::TimeTicks::Now() + wait; |
| 61 while (ret_pid == 0) { |
| 62 base::TimeTicks now = base::TimeTicks::Now(); |
| 63 if (now > wakeup_time) |
| 64 break; |
| 65 // Guaranteed to be non-negative! |
| 66 int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); |
| 67 // Sleep for a bit while we wait for the process to finish. |
| 68 if (sleep_time_usecs > max_sleep_time_usecs) |
| 69 sleep_time_usecs = max_sleep_time_usecs; |
| 70 |
| 71 // usleep() will return 0 and set errno to EINTR on receipt of a signal |
| 72 // such as SIGCHLD. |
| 73 usleep(sleep_time_usecs); |
| 74 ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); |
| 75 |
| 76 if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && |
| 77 (double_sleep_time++ % 4 == 0)) { |
| 78 max_sleep_time_usecs *= 2; |
| 79 } |
| 80 } |
| 81 |
| 82 return ret_pid > 0; |
| 83 } |
| 84 |
| 85 #if defined(OS_MACOSX) |
| 86 // Using kqueue on Mac so that we can wait on non-child processes. |
| 87 // We can't use kqueues on child processes because we need to reap |
| 88 // our own children using wait. |
| 89 static bool WaitForSingleNonChildProcess(base::ProcessHandle handle, |
| 90 base::TimeDelta wait) { |
| 91 DCHECK_GT(handle, 0); |
| 92 DCHECK_GT(wait, base::TimeDelta()); |
| 93 |
| 94 base::ScopedFD kq(kqueue()); |
| 95 if (!kq.is_valid()) { |
| 96 DPLOG(ERROR) << "kqueue"; |
| 97 return false; |
| 98 } |
| 99 |
| 100 struct kevent change = {0}; |
| 101 EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); |
| 102 int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); |
| 103 if (result == -1) { |
| 104 if (errno == ESRCH) { |
| 105 // If the process wasn't found, it must be dead. |
| 106 return true; |
| 107 } |
| 108 |
| 109 DPLOG(ERROR) << "kevent (setup " << handle << ")"; |
| 110 return false; |
| 111 } |
| 112 |
| 113 // Keep track of the elapsed time to be able to restart kevent if it's |
| 114 // interrupted. |
| 115 bool wait_forever = (wait == base::TimeDelta::Max()); |
| 116 base::TimeDelta remaining_delta; |
| 117 base::TimeTicks deadline; |
| 118 if (!wait_forever) { |
| 119 remaining_delta = wait; |
| 120 deadline = base::TimeTicks::Now() + remaining_delta; |
| 121 } |
| 122 |
| 123 result = -1; |
| 124 struct kevent event = {0}; |
| 125 |
| 126 while (wait_forever || remaining_delta > base::TimeDelta()) { |
| 127 struct timespec remaining_timespec; |
| 128 struct timespec* remaining_timespec_ptr; |
| 129 if (wait_forever) { |
| 130 remaining_timespec_ptr = NULL; |
| 131 } else { |
| 132 remaining_timespec = remaining_delta.ToTimeSpec(); |
| 133 remaining_timespec_ptr = &remaining_timespec; |
| 134 } |
| 135 |
| 136 result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); |
| 137 |
| 138 if (result == -1 && errno == EINTR) { |
| 139 if (!wait_forever) { |
| 140 remaining_delta = deadline - base::TimeTicks::Now(); |
| 141 } |
| 142 result = 0; |
| 143 } else { |
| 144 break; |
| 145 } |
| 146 } |
| 147 |
| 148 if (result < 0) { |
| 149 DPLOG(ERROR) << "kevent (wait " << handle << ")"; |
| 150 return false; |
| 151 } else if (result > 1) { |
| 152 DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " |
| 153 << result; |
| 154 return false; |
| 155 } else if (result == 0) { |
| 156 // Timed out. |
| 157 return false; |
| 158 } |
| 159 |
| 160 DCHECK_EQ(result, 1); |
| 161 |
| 162 if (event.filter != EVFILT_PROC || |
| 163 (event.fflags & NOTE_EXIT) == 0 || |
| 164 event.ident != static_cast<uintptr_t>(handle)) { |
| 165 DLOG(ERROR) << "kevent (wait " << handle |
| 166 << "): unexpected event: filter=" << event.filter |
| 167 << ", fflags=" << event.fflags |
| 168 << ", ident=" << event.ident; |
| 169 return false; |
| 170 } |
| 171 |
| 172 return true; |
| 173 } |
| 174 #endif // OS_MACOSX |
| 175 |
| 176 bool WaitForExitWithTimeoutImpl(base::ProcessHandle handle, |
| 177 int* exit_code, |
| 178 base::TimeDelta timeout) { |
| 179 base::ProcessHandle parent_pid = base::GetParentProcessId(handle); |
| 180 base::ProcessHandle our_pid = base::GetCurrentProcessHandle(); |
| 181 if (parent_pid != our_pid) { |
| 182 #if defined(OS_MACOSX) |
| 183 // On Mac we can wait on non child processes. |
| 184 return WaitForSingleNonChildProcess(handle, timeout); |
| 185 #else |
| 186 // Currently on Linux we can't handle non child processes. |
| 187 NOTIMPLEMENTED(); |
| 188 #endif // OS_MACOSX |
| 189 } |
| 190 |
| 191 int status; |
| 192 if (!WaitpidWithTimeout(handle, &status, timeout)) |
| 193 return false; |
| 194 if (WIFSIGNALED(status)) { |
| 195 *exit_code = -1; |
| 196 return true; |
| 197 } |
| 198 if (WIFEXITED(status)) { |
| 199 *exit_code = WEXITSTATUS(status); |
| 200 return true; |
| 201 } |
| 202 return false; |
| 203 } |
| 204 #endif // !defined(OS_NACL_NONSFI) |
| 205 |
| 206 } // namespace |
| 207 |
14 namespace base { | 208 namespace base { |
15 | 209 |
16 Process::Process(ProcessHandle handle) : process_(handle) { | 210 Process::Process(ProcessHandle handle) : process_(handle) { |
17 } | 211 } |
18 | 212 |
19 Process::Process(RValue other) | 213 Process::Process(RValue other) |
20 : process_(other.object->process_) { | 214 : process_(other.object->process_) { |
21 other.object->Close(); | 215 other.object->Close(); |
22 } | 216 } |
23 | 217 |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
95 | 289 |
96 void Process::Terminate(int result_code) { | 290 void Process::Terminate(int result_code) { |
97 // result_code isn't supportable. | 291 // result_code isn't supportable. |
98 DCHECK(IsValid()); | 292 DCHECK(IsValid()); |
99 // We don't wait here. It's the responsibility of other code to reap the | 293 // We don't wait here. It's the responsibility of other code to reap the |
100 // child. | 294 // child. |
101 KillProcess(process_, result_code, false); | 295 KillProcess(process_, result_code, false); |
102 } | 296 } |
103 | 297 |
104 bool Process::WaitForExit(int* exit_code) { | 298 bool Process::WaitForExit(int* exit_code) { |
105 // TODO(rvargas) crbug.com/417532: Remove this constant. | 299 return WaitForExitWithTimeout(TimeDelta::Max(), exit_code); |
106 const int kNoTimeout = -1; | |
107 return WaitForExitWithTimeout(TimeDelta::FromMilliseconds(kNoTimeout), | |
108 exit_code); | |
109 } | 300 } |
110 | 301 |
111 bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) { | 302 bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) { |
112 // TODO(rvargas) crbug.com/417532: Move the implementation here. | 303 return WaitForExitWithTimeoutImpl(Handle(), exit_code, timeout); |
113 return base::WaitForExitCodeWithTimeout(Handle(), exit_code, timeout); | |
114 } | 304 } |
115 | 305 |
116 #if !defined(OS_LINUX) | 306 #if !defined(OS_LINUX) |
117 bool Process::IsProcessBackgrounded() const { | 307 bool Process::IsProcessBackgrounded() const { |
118 // See SetProcessBackgrounded(). | 308 // See SetProcessBackgrounded(). |
119 DCHECK(IsValid()); | 309 DCHECK(IsValid()); |
120 return false; | 310 return false; |
121 } | 311 } |
122 | 312 |
123 bool Process::SetProcessBackgrounded(bool value) { | 313 bool Process::SetProcessBackgrounded(bool value) { |
124 // POSIX only allows lowering the priority of a process, so if we | 314 // POSIX only allows lowering the priority of a process, so if we |
125 // were to lower it we wouldn't be able to raise it back to its initial | 315 // were to lower it we wouldn't be able to raise it back to its initial |
126 // priority. | 316 // priority. |
127 DCHECK(IsValid()); | 317 DCHECK(IsValid()); |
128 return false; | 318 return false; |
129 } | 319 } |
130 #endif // !defined(OS_LINUX) | 320 #endif // !defined(OS_LINUX) |
131 | 321 |
132 int Process::GetPriority() const { | 322 int Process::GetPriority() const { |
133 DCHECK(IsValid()); | 323 DCHECK(IsValid()); |
134 return getpriority(PRIO_PROCESS, process_); | 324 return getpriority(PRIO_PROCESS, process_); |
135 } | 325 } |
136 | 326 |
137 } // namspace base | 327 } // namspace base |
OLD | NEW |