Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Side by Side Diff: base/process/process_posix.cc

Issue 988693005: Chromium roll (https://codereview.chromium.org/976353002) (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: fixed bad android build patch Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/process/launch_posix.cc ('k') | base/process/process_win.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « base/process/launch_posix.cc ('k') | base/process/process_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698