summaryrefslogtreecommitdiff
path: root/chromium/content/browser/child_process_launcher.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/child_process_launcher.cc')
-rw-r--r--chromium/content/browser/child_process_launcher.cc763
1 files changed, 370 insertions, 393 deletions
diff --git a/chromium/content/browser/child_process_launcher.cc b/chromium/content/browser/child_process_launcher.cc
index e47bebc3bdf..23cf832e665 100644
--- a/chromium/content/browser/child_process_launcher.cc
+++ b/chromium/content/browser/child_process_launcher.cc
@@ -1,22 +1,19 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/child_process_launcher.h"
-#include <utility> // For std::pair.
-
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/process/process.h"
+#include "base/profiler/scoped_tracker.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
-#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_descriptors.h"
#include "content/public/common/content_switches.h"
@@ -43,470 +40,453 @@
#endif
#if defined(OS_POSIX)
-#include "base/metrics/stats_table.h"
#include "base/posix/global_descriptors.h"
#include "content/browser/file_descriptor_info_impl.h"
#endif
namespace content {
-// Having the functionality of ChildProcessLauncher be in an internal
-// ref counted object allows us to automatically terminate the process when the
-// parent class destructs, while still holding on to state that we need.
-class ChildProcessLauncher::Context
- : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> {
- public:
- Context()
- : client_(NULL),
- client_thread_id_(BrowserThread::UI),
- termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
- exit_code_(RESULT_CODE_NORMAL_EXIT),
- starting_(true),
- // TODO(earthdok): Re-enable on CrOS http://crbug.com/360622
-#if (defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \
- defined(THREAD_SANITIZER)) && !defined(OS_CHROMEOS)
- terminate_child_on_shutdown_(false)
-#else
- terminate_child_on_shutdown_(true)
-#endif
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
- , zygote_(false)
-#endif
- {
- }
-
- void Launch(SandboxedProcessLauncherDelegate* delegate,
- base::CommandLine* cmd_line,
- int child_process_id,
- Client* client) {
- client_ = client;
-
- CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
+namespace {
+typedef base::Callback<void(bool,
#if defined(OS_ANDROID)
- // We need to close the client end of the IPC channel to reliably detect
- // child termination. We will close this fd after we create the child
- // process which is asynchronous on Android.
- ipcfd_.reset(delegate->TakeIpcFd().release());
+ base::ScopedFD,
#endif
- BrowserThread::PostTask(
- BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(&Context::LaunchInternal,
- make_scoped_refptr(this),
- client_thread_id_,
- child_process_id,
- delegate,
- cmd_line));
+ base::Process)> NotifyCallback;
+
+void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ // Log the launch time, separating out the first one (which will likely be
+ // slower due to the rest of the browser initializing at the same time).
+ static bool done_first_launch = false;
+ if (done_first_launch) {
+ UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
+ } else {
+ UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
+ done_first_launch = true;
}
+}
#if defined(OS_ANDROID)
- static void OnChildProcessStarted(
- // |this_object| is NOT thread safe. Only use it to post a task back.
- scoped_refptr<Context> this_object,
- BrowserThread::ID client_thread_id,
- const base::TimeTicks begin_launch_time,
- base::ProcessHandle handle) {
- RecordHistograms(begin_launch_time);
- if (BrowserThread::CurrentlyOn(client_thread_id)) {
- // This is always invoked on the UI thread which is commonly the
- // |client_thread_id| so we can shortcut one PostTask.
- this_object->Notify(base::Process(handle));
- } else {
- BrowserThread::PostTask(
- client_thread_id, FROM_HERE,
- base::Bind(
- &ChildProcessLauncher::Context::Notify,
- this_object,
- base::Passed(base::Process(handle))));
- }
- }
-#endif
-
- void ResetClient() {
- // No need for locking as this function gets called on the same thread that
- // client_ would be used.
- CHECK(BrowserThread::CurrentlyOn(client_thread_id_));
- client_ = NULL;
- }
-
- void set_terminate_child_on_shutdown(bool terminate_on_shutdown) {
- terminate_child_on_shutdown_ = terminate_on_shutdown;
- }
-
- void GetTerminationStatus() {
- termination_status_ =
- base::GetTerminationStatus(process_.Handle(), &exit_code_);
- }
-
- void SetProcessBackgrounded(bool background) {
- base::Process to_pass = process_.Duplicate();
+// TODO(sievers): Remove this by defining better what happens on what
+// thread in the corresponding Java code.
+void OnChildProcessStartedAndroid(const NotifyCallback& callback,
+ BrowserThread::ID client_thread_id,
+ const base::TimeTicks begin_launch_time,
+ base::ScopedFD ipcfd,
+ base::ProcessHandle handle) {
+ // This can be called on the launcher thread or UI thread.
+ base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time;
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(&RecordHistogramsOnLauncherThread, launch_time));
+
+ base::Closure callback_on_client_thread(
+ base::Bind(callback, false, base::Passed(&ipcfd),
+ base::Passed(base::Process(handle))));
+ if (BrowserThread::CurrentlyOn(client_thread_id)) {
+ callback_on_client_thread.Run();
+ } else {
BrowserThread::PostTask(
- BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(&Context::SetProcessBackgroundedInternal,
- base::Passed(&to_pass), background));
- }
-
- private:
- friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>;
- friend class ChildProcessLauncher;
-
- ~Context() {
- Terminate();
- }
-
- static void RecordHistograms(const base::TimeTicks begin_launch_time) {
- base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time;
- if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
- RecordLaunchHistograms(launch_time);
- } else {
- BrowserThread::PostTask(
- BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(&ChildProcessLauncher::Context::RecordLaunchHistograms,
- launch_time));
- }
- }
-
- static void RecordLaunchHistograms(const base::TimeDelta launch_time) {
- // Log the launch time, separating out the first one (which will likely be
- // slower due to the rest of the browser initializing at the same time).
- static bool done_first_launch = false;
- if (done_first_launch) {
- UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
- } else {
- UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
- done_first_launch = true;
- }
- }
+ client_thread_id, FROM_HERE, callback_on_client_thread);
+ }
+}
+#endif
- static void LaunchInternal(
- // |this_object| is NOT thread safe. Only use it to post a task back.
- scoped_refptr<Context> this_object,
- BrowserThread::ID client_thread_id,
- int child_process_id,
- SandboxedProcessLauncherDelegate* delegate,
- base::CommandLine* cmd_line) {
- scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate);
+void LaunchOnLauncherThread(const NotifyCallback& callback,
+ BrowserThread::ID client_thread_id,
+ int child_process_id,
+ SandboxedProcessLauncherDelegate* delegate,
+#if defined(OS_ANDROID)
+ base::ScopedFD ipcfd,
+#endif
+ base::CommandLine* cmd_line) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate);
#if defined(OS_WIN)
- bool launch_elevated = delegate->ShouldLaunchElevated();
-#elif defined(OS_ANDROID)
- // Uses |ipcfd_| instead of |ipcfd| on Android.
+ bool use_zygote = false;
+ bool launch_elevated = delegate->ShouldLaunchElevated();
#elif defined(OS_MACOSX)
- base::EnvironmentMap env = delegate->GetEnvironment();
- base::ScopedFD ipcfd = delegate->TakeIpcFd();
-#elif defined(OS_POSIX)
- bool use_zygote = delegate->ShouldUseZygote();
- base::EnvironmentMap env = delegate->GetEnvironment();
- base::ScopedFD ipcfd = delegate->TakeIpcFd();
+ bool use_zygote = false;
+ base::EnvironmentMap env = delegate->GetEnvironment();
+ base::ScopedFD ipcfd = delegate->TakeIpcFd();
+#elif defined(OS_POSIX) && !defined(OS_ANDROID)
+ bool use_zygote = delegate->ShouldUseZygote();
+ base::EnvironmentMap env = delegate->GetEnvironment();
+ base::ScopedFD ipcfd = delegate->TakeIpcFd();
#endif
- scoped_ptr<base::CommandLine> cmd_line_deleter(cmd_line);
- base::TimeTicks begin_launch_time = base::TimeTicks::Now();
+ scoped_ptr<base::CommandLine> cmd_line_deleter(cmd_line);
+ base::TimeTicks begin_launch_time = base::TimeTicks::Now();
+ base::Process process;
#if defined(OS_WIN)
- base::ProcessHandle handle = base::kNullProcessHandle;
- if (launch_elevated) {
- base::LaunchOptions options;
- options.start_hidden = true;
- base::LaunchElevatedProcess(*cmd_line, options, &handle);
- } else {
- handle = StartSandboxedProcess(delegate, cmd_line);
- }
+ if (launch_elevated) {
+ base::LaunchOptions options;
+ options.start_hidden = true;
+ process = base::LaunchElevatedProcess(*cmd_line, options);
+ } else {
+ process = StartSandboxedProcess(delegate, cmd_line);
+ }
#elif defined(OS_POSIX)
- std::string process_type =
- cmd_line->GetSwitchValueASCII(switches::kProcessType);
- scoped_ptr<FileDescriptorInfo> files_to_register(
- FileDescriptorInfoImpl::Create());
+ std::string process_type =
+ cmd_line->GetSwitchValueASCII(switches::kProcessType);
+ scoped_ptr<FileDescriptorInfo> files_to_register(
+ FileDescriptorInfoImpl::Create());
#if defined(OS_ANDROID)
- files_to_register->Share(kPrimaryIPCChannel, this_object->ipcfd_.get());
+ files_to_register->Share(kPrimaryIPCChannel, ipcfd.get());
#else
- files_to_register->Transfer(kPrimaryIPCChannel, ipcfd.Pass());
+ files_to_register->Transfer(kPrimaryIPCChannel, ipcfd.Pass());
#endif
- base::StatsTable* stats_table = base::StatsTable::current();
- if (stats_table &&
- base::SharedMemory::IsHandleValid(
- stats_table->GetSharedMemoryHandle())) {
- base::FileDescriptor fd = stats_table->GetSharedMemoryHandle();
- DCHECK(!fd.auto_close);
- files_to_register->Share(kStatsTableSharedMemFd, fd.fd);
- }
#endif
#if defined(OS_ANDROID)
- // Android WebView runs in single process, ensure that we never get here
- // when running in single process mode.
- CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
-
- GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
- *cmd_line, child_process_id, files_to_register.get());
-
- StartChildProcess(
- cmd_line->argv(),
- child_process_id,
- files_to_register.Pass(),
- base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted,
- this_object,
- client_thread_id,
- begin_launch_time));
+ // Android WebView runs in single process, ensure that we never get here
+ // when running in single process mode.
+ CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
+
+ GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
+ *cmd_line, child_process_id, files_to_register.get());
+
+ StartChildProcess(
+ cmd_line->argv(), child_process_id, files_to_register.Pass(),
+ base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id,
+ begin_launch_time, base::Passed(&ipcfd)));
#elif defined(OS_POSIX)
- base::ProcessHandle handle = base::kNullProcessHandle;
- // We need to close the client end of the IPC channel to reliably detect
- // child termination.
+ // We need to close the client end of the IPC channel to reliably detect
+ // child termination.
#if !defined(OS_MACOSX)
- GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
- *cmd_line, child_process_id, files_to_register.get());
- if (use_zygote) {
- handle = ZygoteHostImpl::GetInstance()->ForkRequest(
- cmd_line->argv(), files_to_register.Pass(), process_type);
- } else
- // Fall through to the normal posix case below when we're not zygoting.
+ GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
+ *cmd_line, child_process_id, files_to_register.get());
+ if (use_zygote) {
+ base::ProcessHandle handle = ZygoteHostImpl::GetInstance()->ForkRequest(
+ cmd_line->argv(), files_to_register.Pass(), process_type);
+ process = base::Process(handle);
+ } else
+ // Fall through to the normal posix case below when we're not zygoting.
#endif // !defined(OS_MACOSX)
- {
- // Convert FD mapping to FileHandleMappingVector
- base::FileHandleMappingVector fds_to_map =
- files_to_register->GetMappingWithIDAdjustment(
- base::GlobalDescriptors::kBaseDescriptor);
+ {
+ // Convert FD mapping to FileHandleMappingVector
+ base::FileHandleMappingVector fds_to_map =
+ files_to_register->GetMappingWithIDAdjustment(
+ base::GlobalDescriptors::kBaseDescriptor);
#if !defined(OS_MACOSX)
- if (process_type == switches::kRendererProcess) {
- const int sandbox_fd =
- RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
- fds_to_map.push_back(std::make_pair(
- sandbox_fd,
- GetSandboxFD()));
- }
+ if (process_type == switches::kRendererProcess) {
+ const int sandbox_fd =
+ RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
+ fds_to_map.push_back(std::make_pair(
+ sandbox_fd,
+ GetSandboxFD()));
+ }
#endif // defined(OS_MACOSX)
- // Actually launch the app.
- base::LaunchOptions options;
- options.environ = env;
- options.fds_to_remap = &fds_to_map;
+ // Actually launch the app.
+ base::LaunchOptions options;
+ options.environ = env;
+ options.fds_to_remap = &fds_to_map;
#if defined(OS_MACOSX)
- // Hold the MachBroker lock for the duration of LaunchProcess. The child
- // will send its task port to the parent almost immediately after startup.
- // The Mach message will be delivered to the parent, but updating the
- // record of the launch will wait until after the placeholder PID is
- // inserted below. This ensures that while the child process may send its
- // port to the parent prior to the parent leaving LaunchProcess, the
- // order in which the record in MachBroker is updated is correct.
- MachBroker* broker = MachBroker::GetInstance();
- broker->GetLock().Acquire();
-
- // Make sure the MachBroker is running, and inform it to expect a
- // check-in from the new process.
- broker->EnsureRunning();
-
- const int bootstrap_sandbox_policy = delegate->GetSandboxType();
- if (ShouldEnableBootstrapSandbox() &&
- bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) {
- options.replacement_bootstrap_name =
- GetBootstrapSandbox()->server_bootstrap_name();
- GetBootstrapSandbox()->PrepareToForkWithPolicy(
- bootstrap_sandbox_policy);
- }
+ // Hold the MachBroker lock for the duration of LaunchProcess. The child
+ // will send its task port to the parent almost immediately after startup.
+ // The Mach message will be delivered to the parent, but updating the
+ // record of the launch will wait until after the placeholder PID is
+ // inserted below. This ensures that while the child process may send its
+ // port to the parent prior to the parent leaving LaunchProcess, the
+ // order in which the record in MachBroker is updated is correct.
+ MachBroker* broker = MachBroker::GetInstance();
+ broker->GetLock().Acquire();
+
+ // Make sure the MachBroker is running, and inform it to expect a
+ // check-in from the new process.
+ broker->EnsureRunning();
+
+ const int bootstrap_sandbox_policy = delegate->GetSandboxType();
+ if (ShouldEnableBootstrapSandbox() &&
+ bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) {
+ options.replacement_bootstrap_name =
+ GetBootstrapSandbox()->server_bootstrap_name();
+ GetBootstrapSandbox()->PrepareToForkWithPolicy(
+ bootstrap_sandbox_policy);
+ }
#endif // defined(OS_MACOSX)
- bool launched = base::LaunchProcess(*cmd_line, options, &handle);
- if (!launched)
- handle = base::kNullProcessHandle;
+ process = base::LaunchProcess(*cmd_line, options);
#if defined(OS_MACOSX)
- if (ShouldEnableBootstrapSandbox() &&
- bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) {
- GetBootstrapSandbox()->FinishedFork(handle);
- }
+ if (ShouldEnableBootstrapSandbox() &&
+ bootstrap_sandbox_policy != SANDBOX_TYPE_INVALID) {
+ GetBootstrapSandbox()->FinishedFork(process.Handle());
+ }
- if (launched)
- broker->AddPlaceholderForPid(handle);
+ if (process.IsValid())
+ broker->AddPlaceholderForPid(process.Pid(), child_process_id);
- // After updating the broker, release the lock and let the child's
- // messasge be processed on the broker's thread.
- broker->GetLock().Release();
+ // After updating the broker, release the lock and let the child's
+ // messasge be processed on the broker's thread.
+ broker->GetLock().Release();
#endif // defined(OS_MACOSX)
- }
+ }
#endif // else defined(OS_POSIX)
#if !defined(OS_ANDROID)
- if (handle)
- RecordHistograms(begin_launch_time);
- BrowserThread::PostTask(
- client_thread_id, FROM_HERE,
- base::Bind(
- &Context::Notify,
- this_object.get(),
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
- use_zygote,
-#endif
- base::Passed(base::Process(handle))));
-#endif // !defined(OS_ANDROID)
- }
-
- void Notify(
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
- bool zygote,
-#endif
- base::Process process) {
-#if defined(OS_ANDROID)
- // Finally close the ipcfd
- base::ScopedFD ipcfd_closer = ipcfd_.Pass();
-#endif
- starting_ = false;
- process_ = process.Pass();
- if (!process_.IsValid())
- LOG(ERROR) << "Failed to launch child process";
-
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
- zygote_ = zygote;
-#endif
- if (client_) {
- if (process_.IsValid()) {
- client_->OnProcessLaunched();
- } else {
- client_->OnProcessLaunchFailed();
- }
- } else {
- Terminate();
- }
- }
-
- void Terminate() {
- if (!process_.IsValid())
- return;
-
- if (!terminate_child_on_shutdown_)
- return;
-
- // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
- // don't this on the UI/IO threads.
- BrowserThread::PostTask(
- BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(
- &Context::TerminateInternal,
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
- zygote_,
-#endif
- base::Passed(&process_)));
- }
-
- static void SetProcessBackgroundedInternal(base::Process process,
- bool background) {
- process.SetProcessBackgrounded(background);
-#if defined(OS_ANDROID)
- SetChildProcessInForeground(process.Handle(), !background);
-#endif
+ if (process.IsValid()) {
+ RecordHistogramsOnLauncherThread(base::TimeTicks::Now() -
+ begin_launch_time);
}
+ BrowserThread::PostTask(client_thread_id, FROM_HERE,
+ base::Bind(callback,
+ use_zygote,
+ base::Passed(&process)));
+#endif // !defined(OS_ANDROID)
+}
- static void TerminateInternal(
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
- bool zygote,
-#endif
- base::Process process) {
+void TerminateOnLauncherThread(bool zygote, base::Process process) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
#if defined(OS_ANDROID)
- VLOG(1) << "ChromeProcess: Stopping process with handle "
- << process.Handle();
- StopChildProcess(process.Handle());
+ VLOG(1) << "ChromeProcess: Stopping process with handle "
+ << process.Handle();
+ StopChildProcess(process.Handle());
#else
- // Client has gone away, so just kill the process. Using exit code 0
- // means that UMA won't treat this as a crash.
- process.Terminate(RESULT_CODE_NORMAL_EXIT);
- // On POSIX, we must additionally reap the child.
+ // Client has gone away, so just kill the process. Using exit code 0
+ // means that UMA won't treat this as a crash.
+ process.Terminate(RESULT_CODE_NORMAL_EXIT, false);
+ // On POSIX, we must additionally reap the child.
#if defined(OS_POSIX)
#if !defined(OS_MACOSX)
- if (zygote) {
- // If the renderer was created via a zygote, we have to proxy the reaping
- // through the zygote process.
- ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(process.Handle());
- } else
+ if (zygote) {
+ // If the renderer was created via a zygote, we have to proxy the reaping
+ // through the zygote process.
+ ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(process.Handle());
+ } else
#endif // !OS_MACOSX
- {
- base::EnsureProcessTerminated(process.Handle());
- }
+ base::EnsureProcessTerminated(process.Pass());
#endif // OS_POSIX
#endif // defined(OS_ANDROID)
- }
+}
- Client* client_;
- BrowserThread::ID client_thread_id_;
- base::Process process_;
- base::TerminationStatus termination_status_;
- int exit_code_;
- bool starting_;
- // Controls whether the child process should be terminated on browser
- // shutdown. Default behavior is to terminate the child.
- bool terminate_child_on_shutdown_;
+void SetProcessBackgroundedOnLauncherThread(base::Process process,
+ bool background) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+#if defined(OS_MACOSX)
+ MachBroker* broker = MachBroker::GetInstance();
+ mach_port_t task_port = broker->TaskForPid(process.Pid());
+ if (task_port != TASK_NULL) {
+ process.SetProcessBackgrounded(task_port, background);
+ }
+#else
+ process.SetProcessBackgrounded(background);
+#endif // defined(OS_MACOSX)
#if defined(OS_ANDROID)
- // The fd to close after creating the process.
- base::ScopedFD ipcfd_;
-#elif defined(OS_POSIX) && !defined(OS_MACOSX)
- bool zygote_;
+ SetChildProcessInForeground(process.Handle(), !background);
#endif
-};
+}
+} // anonymous namespace
ChildProcessLauncher::ChildProcessLauncher(
SandboxedProcessLauncherDelegate* delegate,
base::CommandLine* cmd_line,
int child_process_id,
- Client* client) {
- context_ = new Context();
- context_->Launch(
- delegate,
- cmd_line,
- child_process_id,
- client);
+ Client* client,
+ bool terminate_on_shutdown)
+ : client_(client),
+ termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
+ exit_code_(RESULT_CODE_NORMAL_EXIT),
+ zygote_(false),
+ starting_(true),
+#if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \
+ defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
+ defined(UNDEFINED_SANITIZER)
+ terminate_child_on_shutdown_(false),
+#else
+ terminate_child_on_shutdown_(terminate_on_shutdown),
+#endif
+ weak_factory_(this) {
+ DCHECK(CalledOnValidThread());
+ CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
+ Launch(delegate, cmd_line, child_process_id);
}
ChildProcessLauncher::~ChildProcessLauncher() {
- context_->ResetClient();
+ DCHECK(CalledOnValidThread());
+ if (process_.IsValid() && terminate_child_on_shutdown_) {
+ // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
+ // don't this on the UI/IO threads.
+ BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(&TerminateOnLauncherThread, zygote_,
+ base::Passed(&process_)));
+ }
}
-bool ChildProcessLauncher::IsStarting() {
- return context_->starting_;
-}
+void ChildProcessLauncher::Launch(
+ SandboxedProcessLauncherDelegate* delegate,
+ base::CommandLine* cmd_line,
+ int child_process_id) {
+ DCHECK(CalledOnValidThread());
-const base::Process& ChildProcessLauncher::GetProcess() const {
- DCHECK(!context_->starting_);
- return context_->process_;
+#if defined(OS_ANDROID)
+ // Android only supports renderer, sandboxed utility and gpu.
+ std::string process_type =
+ cmd_line->GetSwitchValueASCII(switches::kProcessType);
+ CHECK(process_type == switches::kGpuProcess ||
+ process_type == switches::kRendererProcess ||
+ process_type == switches::kUtilityProcess)
+ << "Unsupported process type: " << process_type;
+
+ // Non-sandboxed utility or renderer process are currently not supported.
+ DCHECK(process_type == switches::kGpuProcess ||
+ !cmd_line->HasSwitch(switches::kNoSandbox));
+
+ // We need to close the client end of the IPC channel to reliably detect
+ // child termination. We will close this fd after we create the child
+ // process which is asynchronous on Android.
+ base::ScopedFD ipcfd(delegate->TakeIpcFd().release());
+#endif
+ NotifyCallback reply_callback(base::Bind(&ChildProcessLauncher::DidLaunch,
+ weak_factory_.GetWeakPtr(),
+ terminate_child_on_shutdown_));
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(&LaunchOnLauncherThread, reply_callback, client_thread_id_,
+ child_process_id, delegate,
+#if defined(OS_ANDROID)
+ base::Passed(&ipcfd),
+#endif
+ cmd_line));
}
-base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
- bool known_dead,
- int* exit_code) {
- if (!context_->process_.IsValid()) {
- // Process is already gone, so return the cached termination status.
- if (exit_code)
- *exit_code = context_->exit_code_;
- return context_->termination_status_;
- }
+void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) {
+ DCHECK(CalledOnValidThread());
#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
- if (context_->zygote_) {
- context_->termination_status_ = ZygoteHostImpl::GetInstance()->
- GetTerminationStatus(context_->process_.Handle(), known_dead,
- &context_->exit_code_);
+ if (zygote_) {
+ termination_status_ = ZygoteHostImpl::GetInstance()->
+ GetTerminationStatus(process_.Handle(), known_dead, &exit_code_);
} else if (known_dead) {
- context_->termination_status_ =
- base::GetKnownDeadTerminationStatus(context_->process_.Handle(),
- &context_->exit_code_);
+ termination_status_ =
+ base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
} else {
#elif defined(OS_MACOSX)
if (known_dead) {
- context_->termination_status_ =
- base::GetKnownDeadTerminationStatus(context_->process_.Handle(),
- &context_->exit_code_);
+ termination_status_ =
+ base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
} else {
#elif defined(OS_ANDROID)
- if (IsChildProcessOomProtected(context_->process_.Handle())) {
- context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED;
+ if (IsChildProcessOomProtected(process_.Handle())) {
+ termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED;
} else {
#else
{
#endif
- context_->GetTerminationStatus();
+ termination_status_ =
+ base::GetTerminationStatus(process_.Handle(), &exit_code_);
}
+}
+void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
+ DCHECK(CalledOnValidThread());
+ base::Process to_pass = process_.Duplicate();
+ BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(&SetProcessBackgroundedOnLauncherThread,
+ base::Passed(&to_pass), background));
+}
+
+void ChildProcessLauncher::DidLaunch(
+ base::WeakPtr<ChildProcessLauncher> instance,
+ bool terminate_on_shutdown,
+ bool zygote,
+#if defined(OS_ANDROID)
+ base::ScopedFD ipcfd,
+#endif
+ base::Process process) {
+ if (!process.IsValid())
+ LOG(ERROR) << "Failed to launch child process";
+
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/465841
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile1(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "465841 ChildProcessLauncher::Context::Notify::Start"));
+
+ if (instance.get()) {
+ instance->Notify(zygote,
+#if defined(OS_ANDROID)
+ ipcfd.Pass(),
+#endif
+ process.Pass());
+ } else {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/465841
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile4(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "465841 ChildProcessLauncher::Context::Notify::ProcessTerminate"));
+ if (process.IsValid() && terminate_on_shutdown) {
+ // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
+ // don't this on the UI/IO threads.
+ BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(&TerminateOnLauncherThread, zygote,
+ base::Passed(&process)));
+ }
+ }
+}
+
+void ChildProcessLauncher::Notify(
+ bool zygote,
+#if defined(OS_ANDROID)
+ base::ScopedFD ipcfd,
+#endif
+ base::Process process) {
+ DCHECK(CalledOnValidThread());
+ starting_ = false;
+ process_ = process.Pass();
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
+ zygote_ = zygote;
+#endif
+ if (process_.IsValid()) {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/465841
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile2(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "465841 ChildProcessLauncher::Context::Notify::ProcessLaunched"));
+ client_->OnProcessLaunched();
+ } else {
+ // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/465841
+ // is fixed.
+ tracked_objects::ScopedTracker tracking_profile3(
+ FROM_HERE_WITH_EXPLICIT_FUNCTION(
+ "465841 ChildProcessLauncher::Context::Notify::ProcessFailed"));
+ client_->OnProcessLaunchFailed();
+ }
+}
+
+bool ChildProcessLauncher::IsStarting() {
+ // TODO(crbug.com/469248): This fails in some tests.
+ // DCHECK(CalledOnValidThread());
+ return starting_;
+}
+
+const base::Process& ChildProcessLauncher::GetProcess() const {
+ // TODO(crbug.com/469248): This fails in some tests.
+ // DCHECK(CalledOnValidThread());
+ return process_;
+}
+
+base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
+ bool known_dead,
+ int* exit_code) {
+ DCHECK(CalledOnValidThread());
+ if (!process_.IsValid()) {
+ // Process is already gone, so return the cached termination status.
+ if (exit_code)
+ *exit_code = exit_code_;
+ return termination_status_;
+ }
+
+ UpdateTerminationStatus(known_dead);
if (exit_code)
- *exit_code = context_->exit_code_;
+ *exit_code = exit_code_;
// POSIX: If the process crashed, then the kernel closed the socket
// for it and so the child has already died by the time we get
@@ -514,20 +494,17 @@ base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
// it'll reap the process. However, if GetTerminationStatus didn't
// reap the child (because it was still running), we'll need to
// Terminate via ProcessWatcher. So we can't close the handle here.
- if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
- context_->process_.Close();
+ if (termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
+ process_.Close();
- return context_->termination_status_;
+ return termination_status_;
}
-void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
- context_->SetProcessBackgrounded(background);
-}
-
-void ChildProcessLauncher::SetTerminateChildOnShutdown(
- bool terminate_on_shutdown) {
- if (context_.get())
- context_->set_terminate_child_on_shutdown(terminate_on_shutdown);
+ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest(
+ Client* client) {
+ Client* ret = client_;
+ client_ = client;
+ return ret;
}
} // namespace content