| Index: content/browser/child_process_launcher.cc
|
| diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
|
| index 24fd73721846b21a9a9b63a390c4940ac8a472ac..28be72bb9c32eb853702adbdb0726379a3f9825c 100644
|
| --- a/content/browser/child_process_launcher.cc
|
| +++ b/content/browser/child_process_launcher.cc
|
| @@ -4,12 +4,9 @@
|
|
|
| #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"
|
| @@ -17,7 +14,6 @@
|
| #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"
|
| @@ -50,279 +46,71 @@
|
|
|
| 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();
|
| -
|
| - // Posts a task to a dedicated thread to do the actual work.
|
| - // Must be called on the UI thread.
|
| - void Launch(SandboxedProcessLauncherDelegate* delegate,
|
| - base::CommandLine* cmd_line,
|
| - int child_process_id,
|
| - Client* client);
|
| +namespace {
|
|
|
| +typedef base::Callback<void(bool,
|
| #if defined(OS_ANDROID)
|
| - // Called on the UI thread with the operation result. Calls Notify().
|
| - 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);
|
| + base::ScopedFD,
|
| #endif
|
| + base::Process)> NotifyCallback;
|
|
|
| - // Resets the client (the client is going away).
|
| - void ResetClient();
|
| -
|
| - bool starting() const { return starting_; }
|
| -
|
| - const base::Process& process() const { return process_; }
|
| -
|
| - int exit_code() const { return exit_code_; }
|
| -
|
| - base::TerminationStatus termination_status() const {
|
| - return termination_status_;
|
| - }
|
| -
|
| - void set_terminate_child_on_shutdown(bool terminate_on_shutdown) {
|
| - terminate_child_on_shutdown_ = terminate_on_shutdown;
|
| - }
|
| -
|
| - void UpdateTerminationStatus(bool known_dead);
|
| -
|
| - void Close() { process_.Close(); }
|
| -
|
| - void SetProcessBackgrounded(bool background);
|
| -
|
| - Client* ReplaceClientForTest(Client* client) {
|
| - Client* ret = client_;
|
| - client_ = client;
|
| - return ret;
|
| - }
|
| -
|
| - private:
|
| - friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>;
|
| -
|
| - ~Context() {
|
| - Terminate();
|
| +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;
|
| }
|
| -
|
| - static void RecordHistograms(base::TimeTicks begin_launch_time);
|
| - static void RecordLaunchHistograms(base::TimeDelta launch_time);
|
| -
|
| - // Performs the actual work of launching the process.
|
| - // Runs on the PROCESS_LAUNCHER thread.
|
| - 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);
|
| -
|
| - // Notifies the client about the result of the operation.
|
| - // Runs on the UI thread.
|
| - void Notify(
|
| -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
|
| - bool zygote,
|
| -#endif
|
| - base::Process process);
|
| -
|
| - void Terminate();
|
| -
|
| - static void SetProcessBackgroundedInternal(base::Process process,
|
| - bool background);
|
| -
|
| - static void TerminateInternal(
|
| -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
|
| - bool zygote,
|
| -#endif
|
| - base::Process process);
|
| -
|
| - Client* client_;
|
| - BrowserThread::ID client_thread_id_;
|
| - base::Process process_;
|
| - base::TerminationStatus termination_status_;
|
| - int exit_code_;
|
| -#if defined(OS_ANDROID)
|
| - // The fd to close after creating the process.
|
| - base::ScopedFD ipcfd_;
|
| -#elif defined(OS_POSIX) && !defined(OS_MACOSX)
|
| - bool zygote_;
|
| -#endif
|
| - 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_;
|
| -};
|
| -
|
| -ChildProcessLauncher::Context::Context()
|
| - : client_(NULL),
|
| - client_thread_id_(BrowserThread::UI),
|
| - termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
|
| - exit_code_(RESULT_CODE_NORMAL_EXIT),
|
| -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
|
| - zygote_(false),
|
| -#endif
|
| - 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_(true) {
|
| -#endif
|
| }
|
|
|
| -void ChildProcessLauncher::Context::Launch(
|
| - SandboxedProcessLauncherDelegate* delegate,
|
| - base::CommandLine* cmd_line,
|
| - int child_process_id,
|
| - Client* client) {
|
| - CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
|
| - client_ = client;
|
| -
|
| #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.
|
| - ipcfd_.reset(delegate->TakeIpcFd().release());
|
| -#endif
|
| +// 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(&Context::LaunchInternal,
|
| - make_scoped_refptr(this),
|
| - client_thread_id_,
|
| - child_process_id,
|
| - delegate,
|
| - cmd_line));
|
| -}
|
| + base::Bind(&RecordHistogramsOnLauncherThread, launch_time));
|
|
|
| -#if defined(OS_ANDROID)
|
| -// static
|
| -void ChildProcessLauncher::Context::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);
|
| + base::Closure callback_on_client_thread(
|
| + base::Bind(callback, false, base::Passed(&ipcfd),
|
| + base::Passed(base::Process(handle))));
|
| 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));
|
| + callback_on_client_thread.Run();
|
| } else {
|
| BrowserThread::PostTask(
|
| - client_thread_id, FROM_HERE,
|
| - base::Bind(&ChildProcessLauncher::Context::Notify,
|
| - this_object,
|
| - base::Passed(base::Process(handle))));
|
| - }
|
| + client_thread_id, FROM_HERE, callback_on_client_thread);
|
| + }
|
| }
|
| #endif
|
|
|
| -void ChildProcessLauncher::Context::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 ChildProcessLauncher::Context::UpdateTerminationStatus(bool known_dead) {
|
| -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
|
| - if (zygote_) {
|
| - termination_status_ = ZygoteHostImpl::GetInstance()->
|
| - GetTerminationStatus(process_.Handle(), known_dead, &exit_code_);
|
| - } else if (known_dead) {
|
| - termination_status_ =
|
| - base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
|
| - } else {
|
| -#elif defined(OS_MACOSX)
|
| - if (known_dead) {
|
| - termination_status_ =
|
| - base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
|
| - } else {
|
| -#elif defined(OS_ANDROID)
|
| - if (IsChildProcessOomProtected(process_.Handle())) {
|
| - termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED;
|
| - } else {
|
| -#else
|
| - {
|
| +void LaunchOnLauncherThread(const NotifyCallback& callback,
|
| + BrowserThread::ID client_thread_id,
|
| + int child_process_id,
|
| + SandboxedProcessLauncherDelegate* delegate,
|
| +#if defined(OS_ANDROID)
|
| + base::ScopedFD ipcfd,
|
| #endif
|
| - termination_status_ =
|
| - base::GetTerminationStatus(process_.Handle(), &exit_code_);
|
| - }
|
| -}
|
| -
|
| -void ChildProcessLauncher::Context::SetProcessBackgrounded(bool background) {
|
| - base::Process to_pass = process_.Duplicate();
|
| - BrowserThread::PostTask(
|
| - BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
|
| - base::Bind(&Context::SetProcessBackgroundedInternal,
|
| - base::Passed(&to_pass), background));
|
| -}
|
| -
|
| -// static
|
| -void ChildProcessLauncher::Context::RecordHistograms(
|
| - 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 ChildProcessLauncher::Context::RecordLaunchHistograms(
|
| - 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;
|
| - }
|
| -}
|
| -
|
| -// static
|
| -void ChildProcessLauncher::Context::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) {
|
| + base::CommandLine* cmd_line) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
|
| scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate);
|
| #if defined(OS_WIN)
|
| + bool use_zygote = false;
|
| bool launch_elevated = delegate->ShouldLaunchElevated();
|
| -#elif defined(OS_ANDROID)
|
| - // Uses |ipcfd_| instead of |ipcfd| on Android.
|
| #elif defined(OS_MACOSX)
|
| + bool use_zygote = false;
|
| base::EnvironmentMap env = delegate->GetEnvironment();
|
| base::ScopedFD ipcfd = delegate->TakeIpcFd();
|
| -#elif defined(OS_POSIX)
|
| +#elif defined(OS_POSIX) && !defined(OS_ANDROID)
|
| bool use_zygote = delegate->ShouldUseZygote();
|
| base::EnvironmentMap env = delegate->GetEnvironment();
|
| base::ScopedFD ipcfd = delegate->TakeIpcFd();
|
| @@ -346,7 +134,7 @@ void ChildProcessLauncher::Context::LaunchInternal(
|
| 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());
|
| #endif
|
| @@ -361,13 +149,9 @@ void ChildProcessLauncher::Context::LaunchInternal(
|
| *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));
|
| + 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)
|
| // We need to close the client end of the IPC channel to reliably detect
|
| @@ -447,102 +231,19 @@ void ChildProcessLauncher::Context::LaunchInternal(
|
| }
|
| #endif // else defined(OS_POSIX)
|
| #if !defined(OS_ANDROID)
|
| - if (process.IsValid())
|
| - 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(&process)));
|
| -#endif // !defined(OS_ANDROID)
|
| -}
|
| -
|
| -void ChildProcessLauncher::Context::Notify(
|
| -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
|
| - bool zygote,
|
| -#endif
|
| - base::Process 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 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()) {
|
| - // 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();
|
| - }
|
| - } 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"));
|
| - Terminate();
|
| + 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)
|
| }
|
|
|
| -void ChildProcessLauncher::Context::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 ChildProcessLauncher::Context::SetProcessBackgroundedInternal(
|
| - base::Process process,
|
| - bool background) {
|
| - process.SetProcessBackgrounded(background);
|
| -#if defined(OS_ANDROID)
|
| - SetChildProcessInForeground(process.Handle(), !background);
|
| -#endif
|
| -}
|
| -
|
| -// static
|
| -void ChildProcessLauncher::Context::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();
|
| @@ -565,47 +266,219 @@ void ChildProcessLauncher::Context::TerminateInternal(
|
| #endif // defined(OS_ANDROID)
|
| }
|
|
|
| -// -----------------------------------------------------------------------------
|
| +void SetProcessBackgroundedOnLauncherThread(base::Process process,
|
| + bool background) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
|
| + process.SetProcessBackgrounded(background);
|
| +#if defined(OS_ANDROID)
|
| + 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_)));
|
| + }
|
| +}
|
| +
|
| +void ChildProcessLauncher::Launch(
|
| + SandboxedProcessLauncherDelegate* delegate,
|
| + base::CommandLine* cmd_line,
|
| + int child_process_id) {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| +#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));
|
| +}
|
| +
|
| +void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) {
|
| + DCHECK(CalledOnValidThread());
|
| +#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
|
| + if (zygote_) {
|
| + termination_status_ = ZygoteHostImpl::GetInstance()->
|
| + GetTerminationStatus(process_.Handle(), known_dead, &exit_code_);
|
| + } else if (known_dead) {
|
| + termination_status_ =
|
| + base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
|
| + } else {
|
| +#elif defined(OS_MACOSX)
|
| + if (known_dead) {
|
| + termination_status_ =
|
| + base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
|
| + } else {
|
| +#elif defined(OS_ANDROID)
|
| + if (IsChildProcessOomProtected(process_.Handle())) {
|
| + termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED;
|
| + } else {
|
| +#else
|
| + {
|
| +#endif
|
| + 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() {
|
| - return context_->starting();
|
| + // TODO(crbug.com/469248): This fails in some tests.
|
| + // DCHECK(CalledOnValidThread());
|
| + return starting_;
|
| }
|
|
|
| const base::Process& ChildProcessLauncher::GetProcess() const {
|
| - DCHECK(!context_->starting());
|
| - return context_->process();
|
| + // TODO(crbug.com/469248): This fails in some tests.
|
| + // DCHECK(CalledOnValidThread());
|
| + return process_;
|
| }
|
|
|
| base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
|
| bool known_dead,
|
| int* exit_code) {
|
| - if (!context_->process().IsValid()) {
|
| + DCHECK(CalledOnValidThread());
|
| + if (!process_.IsValid()) {
|
| // Process is already gone, so return the cached termination status.
|
| if (exit_code)
|
| - *exit_code = context_->exit_code();
|
| - return context_->termination_status();
|
| + *exit_code = exit_code_;
|
| + return termination_status_;
|
| }
|
|
|
| - context_->UpdateTerminationStatus(known_dead);
|
| + 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
|
| @@ -613,25 +486,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_->Close();
|
| + if (termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
|
| + process_.Close();
|
|
|
| - return context_->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);
|
| + return termination_status_;
|
| }
|
|
|
| ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest(
|
| Client* client) {
|
| - return context_->ReplaceClientForTest(client);
|
| + Client* ret = client_;
|
| + client_ = client;
|
| + return ret;
|
| }
|
|
|
| } // namespace content
|
|
|