Android Browser Compositor: Add ScheduleComposite() callback.
[chromium-blink-merge.git] / content / browser / child_process_launcher.cc
blob0bbc03536eb4831cbacc36d2bc3dbe7f75b0ddd8
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/child_process_launcher.h"
7 #include <utility> // For std::pair.
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/process_util.h"
15 #include "base/synchronization/lock.h"
16 #include "base/threading/thread.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/content_browser_client.h"
19 #include "content/public/common/content_descriptors.h"
20 #include "content/public/common/content_switches.h"
21 #include "content/public/common/result_codes.h"
23 #if defined(OS_WIN)
24 #include "base/file_path.h"
25 #include "content/common/sandbox_policy.h"
26 #elif defined(OS_MACOSX)
27 #include "content/browser/mach_broker_mac.h"
28 #elif defined(OS_ANDROID)
29 #include "base/android/jni_android.h"
30 #include "content/browser/android/sandboxed_process_launcher.h"
31 #elif defined(OS_POSIX)
32 #include "base/memory/singleton.h"
33 #include "content/browser/renderer_host/render_sandbox_host_linux.h"
34 #include "content/browser/zygote_host/zygote_host_impl_linux.h"
35 #endif
37 #if defined(OS_POSIX)
38 #include "base/global_descriptors_posix.h"
39 #endif
41 using content::BrowserThread;
43 // Having the functionality of ChildProcessLauncher be in an internal
44 // ref counted object allows us to automatically terminate the process when the
45 // parent class destructs, while still holding on to state that we need.
46 class ChildProcessLauncher::Context
47 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {
48 public:
49 Context()
50 : client_(NULL),
51 client_thread_id_(BrowserThread::UI),
52 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
53 exit_code_(content::RESULT_CODE_NORMAL_EXIT),
54 starting_(true)
55 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
56 , zygote_(false)
57 #endif
59 #if defined(OS_POSIX)
60 terminate_child_on_shutdown_ = !CommandLine::ForCurrentProcess()->
61 HasSwitch(switches::kChildCleanExit);
62 #else
63 terminate_child_on_shutdown_ = true;
64 #endif
67 void Launch(
68 #if defined(OS_WIN)
69 const FilePath& exposed_dir,
70 #elif defined(OS_ANDROID)
71 int ipcfd,
72 #elif defined(OS_POSIX)
73 bool use_zygote,
74 const base::EnvironmentVector& environ,
75 int ipcfd,
76 #endif
77 CommandLine* cmd_line,
78 Client* client) {
79 client_ = client;
81 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
83 #if defined(OS_ANDROID)
84 // We need to close the client end of the IPC channel to reliably detect
85 // child termination. We will close this fd after we create the child
86 // process which is asynchronous on Android.
87 ipcfd_ = ipcfd;
88 #endif
89 BrowserThread::PostTask(
90 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
91 base::Bind(
92 &Context::LaunchInternal,
93 make_scoped_refptr(this),
94 client_thread_id_,
95 #if defined(OS_WIN)
96 exposed_dir,
97 #elif defined(OS_ANDROID)
98 ipcfd,
99 #elif defined(OS_POSIX)
100 use_zygote,
101 environ,
102 ipcfd,
103 #endif
104 cmd_line));
107 #if defined(OS_ANDROID)
108 static void OnSandboxedProcessStarted(
109 // |this_object| is NOT thread safe. Only use it to post a task back.
110 scoped_refptr<Context> this_object,
111 BrowserThread::ID client_thread_id,
112 base::ProcessHandle handle) {
113 if (BrowserThread::CurrentlyOn(client_thread_id)) {
114 // This is always invoked on the UI thread which is commonly the
115 // |client_thread_id| so we can shortcut one PostTask.
116 this_object->Notify(handle);
117 } else {
118 BrowserThread::PostTask(
119 client_thread_id, FROM_HERE,
120 base::Bind(
121 &ChildProcessLauncher::Context::Notify,
122 this_object,
123 handle));
126 #endif
128 void ResetClient() {
129 // No need for locking as this function gets called on the same thread that
130 // client_ would be used.
131 CHECK(BrowserThread::CurrentlyOn(client_thread_id_));
132 client_ = NULL;
135 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) {
136 terminate_child_on_shutdown_ = terminate_on_shutdown;
139 private:
140 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>;
141 friend class ChildProcessLauncher;
143 ~Context() {
144 Terminate();
147 static void LaunchInternal(
148 // |this_object| is NOT thread safe. Only use it to post a task back.
149 scoped_refptr<Context> this_object,
150 BrowserThread::ID client_thread_id,
151 #if defined(OS_WIN)
152 const FilePath& exposed_dir,
153 #elif defined(OS_ANDROID)
154 int ipcfd,
155 #elif defined(OS_POSIX)
156 bool use_zygote,
157 const base::EnvironmentVector& env,
158 int ipcfd,
159 #endif
160 CommandLine* cmd_line) {
161 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
163 #if defined(OS_WIN)
164 base::ProcessHandle handle = sandbox::StartProcessWithAccess(
165 cmd_line, exposed_dir);
166 #elif defined(OS_ANDROID)
167 std::string process_type =
168 cmd_line->GetSwitchValueASCII(switches::kProcessType);
169 std::vector<content::FileDescriptorInfo> files_to_register;
170 files_to_register.push_back(
171 content::FileDescriptorInfo(kPrimaryIPCChannel,
172 base::FileDescriptor(ipcfd, false)));
174 content::GetContentClient()->browser()->
175 GetAdditionalMappedFilesForChildProcess(*cmd_line, &files_to_register);
177 content::StartSandboxedProcess(cmd_line->argv(), files_to_register,
178 base::Bind(&ChildProcessLauncher::Context::OnSandboxedProcessStarted,
179 this_object, client_thread_id));
181 #elif defined(OS_POSIX)
182 base::ProcessHandle handle = base::kNullProcessHandle;
183 // We need to close the client end of the IPC channel to reliably detect
184 // child termination.
185 file_util::ScopedFD ipcfd_closer(&ipcfd);
187 std::string process_type =
188 cmd_line->GetSwitchValueASCII(switches::kProcessType);
189 std::vector<content::FileDescriptorInfo> files_to_register;
190 files_to_register.push_back(
191 content::FileDescriptorInfo(kPrimaryIPCChannel,
192 base::FileDescriptor(ipcfd, false)));
194 #if !defined(OS_MACOSX)
195 content::GetContentClient()->browser()->
196 GetAdditionalMappedFilesForChildProcess(*cmd_line, &files_to_register);
197 if (use_zygote) {
198 handle = ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line->argv(),
199 files_to_register,
200 process_type);
201 } else
202 // Fall through to the normal posix case below when we're not zygoting.
203 #endif // !defined(OS_MACOSX)
205 // Convert FD mapping to FileHandleMappingVector
206 base::FileHandleMappingVector fds_to_map;
207 for (std::vector<content::FileDescriptorInfo>::const_iterator
208 i = files_to_register.begin(); i != files_to_register.end(); ++i) {
209 const content::FileDescriptorInfo& fd_info = *i;
210 fds_to_map.push_back(std::make_pair(
211 fd_info.fd.fd,
212 fd_info.id + base::GlobalDescriptors::kBaseDescriptor));
215 #if !defined(OS_MACOSX)
216 if (process_type == switches::kRendererProcess) {
217 const int sandbox_fd =
218 RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
219 fds_to_map.push_back(std::make_pair(
220 sandbox_fd,
221 kSandboxIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
223 #endif // defined(OS_MACOSX)
225 // Actually launch the app.
226 base::LaunchOptions options;
227 options.environ = &env;
228 options.fds_to_remap = &fds_to_map;
230 #if defined(OS_MACOSX)
231 // Use synchronization to make sure that the MachBroker is ready to
232 // receive a check-in from the new process before the new process
233 // actually tries to check in.
234 base::LaunchSynchronizationHandle synchronization_handle;
235 options.synchronize = &synchronization_handle;
236 #endif // defined(OS_MACOSX)
238 bool launched = base::LaunchProcess(*cmd_line, options, &handle);
240 #if defined(OS_MACOSX)
241 if (launched) {
242 MachBroker* broker = MachBroker::GetInstance();
244 base::AutoLock lock(broker->GetLock());
246 // Make sure the MachBroker is running, and inform it to expect a
247 // check-in from the new process.
248 broker->EnsureRunning();
249 broker->AddPlaceholderForPid(handle);
252 // Now that the MachBroker is ready, the child may continue.
253 base::LaunchSynchronize(synchronization_handle);
255 #endif // defined(OS_MACOSX)
257 if (!launched)
258 handle = base::kNullProcessHandle;
260 #endif // else defined(OS_POSIX)
261 #if !defined(OS_ANDROID)
262 BrowserThread::PostTask(
263 client_thread_id, FROM_HERE,
264 base::Bind(
265 &Context::Notify,
266 this_object.get(),
267 #if defined(OS_POSIX) && !defined(OS_MACOSX)
268 use_zygote,
269 #endif
270 handle));
271 #endif // !defined(OS_ANDROID)
274 void Notify(
275 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
276 bool zygote,
277 #endif
278 base::ProcessHandle handle) {
279 #if defined(OS_ANDROID)
280 // Finally close the ipcfd
281 file_util::ScopedFD ipcfd_closer(&ipcfd_);
282 #endif
283 starting_ = false;
284 process_.set_handle(handle);
285 if (!handle)
286 LOG(ERROR) << "Failed to launch child process";
288 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
289 zygote_ = zygote;
290 #endif
291 if (client_) {
292 client_->OnProcessLaunched();
293 } else {
294 Terminate();
298 void Terminate() {
299 if (!process_.handle())
300 return;
302 if (!terminate_child_on_shutdown_)
303 return;
305 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
306 // don't this on the UI/IO threads.
307 BrowserThread::PostTask(
308 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
309 base::Bind(
310 &Context::TerminateInternal,
311 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
312 zygote_,
313 #endif
314 process_.handle()));
315 process_.set_handle(base::kNullProcessHandle);
318 static void SetProcessBackgrounded(base::ProcessHandle handle,
319 bool background) {
320 base::Process process(handle);
321 process.SetProcessBackgrounded(background);
324 static void TerminateInternal(
325 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
326 bool zygote,
327 #endif
328 base::ProcessHandle handle) {
329 #if defined(OS_ANDROID)
330 LOG(INFO) << "ChromeProcess: Stopping process with handle " << handle;
331 content::StopSandboxedProcess(handle);
332 #else
333 base::Process process(handle);
334 // Client has gone away, so just kill the process. Using exit code 0
335 // means that UMA won't treat this as a crash.
336 process.Terminate(content::RESULT_CODE_NORMAL_EXIT);
337 // On POSIX, we must additionally reap the child.
338 #if defined(OS_POSIX)
339 #if !defined(OS_MACOSX)
340 if (zygote) {
341 // If the renderer was created via a zygote, we have to proxy the reaping
342 // through the zygote process.
343 ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(handle);
344 } else
345 #endif // !OS_MACOSX
347 base::EnsureProcessTerminated(handle);
349 #endif // OS_POSIX
350 process.Close();
351 #endif // defined(OS_ANDROID)
354 Client* client_;
355 BrowserThread::ID client_thread_id_;
356 base::Process process_;
357 base::TerminationStatus termination_status_;
358 int exit_code_;
359 bool starting_;
360 // Controls whether the child process should be terminated on browser
361 // shutdown. Default behavior is to terminate the child.
362 bool terminate_child_on_shutdown_;
363 #if defined(OS_ANDROID)
364 // The fd to close after creating the process.
365 int ipcfd_;
366 #elif defined(OS_POSIX) && !defined(OS_MACOSX)
367 bool zygote_;
368 #endif
372 ChildProcessLauncher::ChildProcessLauncher(
373 #if defined(OS_WIN)
374 const FilePath& exposed_dir,
375 #elif defined(OS_POSIX)
376 bool use_zygote,
377 const base::EnvironmentVector& environ,
378 int ipcfd,
379 #endif
380 CommandLine* cmd_line,
381 Client* client) {
382 context_ = new Context();
383 context_->Launch(
384 #if defined(OS_WIN)
385 exposed_dir,
386 #elif defined(OS_ANDROID)
387 ipcfd,
388 #elif defined(OS_POSIX)
389 use_zygote,
390 environ,
391 ipcfd,
392 #endif
393 cmd_line,
394 client);
397 ChildProcessLauncher::~ChildProcessLauncher() {
398 context_->ResetClient();
401 bool ChildProcessLauncher::IsStarting() {
402 return context_->starting_;
405 base::ProcessHandle ChildProcessLauncher::GetHandle() {
406 DCHECK(!context_->starting_);
407 return context_->process_.handle();
410 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
411 int* exit_code) {
412 base::ProcessHandle handle = context_->process_.handle();
413 if (handle == base::kNullProcessHandle) {
414 // Process is already gone, so return the cached termination status.
415 if (exit_code)
416 *exit_code = context_->exit_code_;
417 return context_->termination_status_;
419 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
420 if (context_->zygote_) {
421 context_->termination_status_ = ZygoteHostImpl::GetInstance()->
422 GetTerminationStatus(handle, &context_->exit_code_);
423 } else
424 #endif
426 context_->termination_status_ =
427 base::GetTerminationStatus(handle, &context_->exit_code_);
430 if (exit_code)
431 *exit_code = context_->exit_code_;
433 // POSIX: If the process crashed, then the kernel closed the socket
434 // for it and so the child has already died by the time we get
435 // here. Since GetTerminationStatus called waitpid with WNOHANG,
436 // it'll reap the process. However, if GetTerminationStatus didn't
437 // reap the child (because it was still running), we'll need to
438 // Terminate via ProcessWatcher. So we can't close the handle here.
439 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
440 context_->process_.Close();
442 return context_->termination_status_;
445 void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
446 BrowserThread::PostTask(
447 BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
448 base::Bind(
449 &ChildProcessLauncher::Context::SetProcessBackgrounded,
450 GetHandle(), background));
453 void ChildProcessLauncher::SetTerminateChildOnShutdown(
454 bool terminate_on_shutdown) {
455 if (context_)
456 context_->set_terminate_child_on_shutdown(terminate_on_shutdown);