diff options
Diffstat (limited to 'chromium/content/browser/child_process_launcher.cc')
-rw-r--r-- | chromium/content/browser/child_process_launcher.cc | 763 |
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 |