| Index: base/mp/mp_child_process_launcher.cc
|
| ===================================================================
|
| --- base/mp/mp_child_process_launcher.cc (revision 0)
|
| +++ base/mp/mp_child_process_launcher.cc (revision 0)
|
| @@ -0,0 +1,233 @@
|
| +// Copyright (c) 2009 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 "base/mp/mp_child_process_launcher.h"
|
| +
|
| +#include "base/command_line.h"
|
| +#include "base/logging.h"
|
| +#include "base/scoped_ptr.h"
|
| +#include "base/thread.h"
|
| +
|
| +#if defined(OS_POSIX)
|
| +#include "base/global_descriptors_posix.h"
|
| +#endif
|
| +
|
| +namespace base {
|
| +
|
| +// Having the functionality of MpChildProcessLauncher 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 MpChildProcessLauncher::Context
|
| + : public base::RefCountedThreadSafe<MpChildProcessLauncher::Context> {
|
| + public:
|
| + Context(MpChildProcessContext* process_context)
|
| + : process_context_(process_context),
|
| + starting_(true)
|
| +#if defined(OS_LINUX)
|
| + , zygote_(false)
|
| +#endif
|
| + {
|
| + }
|
| +
|
| + void Launch(
|
| +#if defined(OS_WIN)
|
| + const FilePath& exposed_dir,
|
| +#elif defined(OS_POSIX)
|
| + bool use_zygote,
|
| + const base::environment_vector& environ,
|
| + int ipcfd,
|
| +#endif
|
| + CommandLine* cmd_line,
|
| + Client* client) {
|
| + client_ = client;
|
| +
|
| + CHECK(process_context_->GetCurrentThreadIdentifier(&client_thread_id_));
|
| +
|
| + process_context_->PostProcessLauncherTask(
|
| + FROM_HERE,
|
| + NewRunnableMethod(
|
| + this,
|
| + &Context::LaunchInternal,
|
| +#if defined(OS_WIN)
|
| + exposed_dir,
|
| +#elif defined(POSIX)
|
| + use_zygote,
|
| + environ,
|
| + ipcfd,
|
| +#endif
|
| + cmd_line));
|
| + }
|
| +
|
| + void ResetClient() {
|
| + // No need for locking as this function gets called on the same thread that
|
| + // client_ would be used.
|
| + CHECK(process_context_->CurrentlyOnThread(client_thread_id_));
|
| + client_ = NULL;
|
| + }
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<MpChildProcessLauncher::Context>;
|
| + friend class MpChildProcessLauncher;
|
| +
|
| + ~Context() {
|
| + Terminate();
|
| + }
|
| +
|
| + void LaunchInternal(
|
| +#if defined(OS_WIN)
|
| + const FilePath& exposed_dir,
|
| +#elif defined(OS_POSIX)
|
| + bool use_zygote,
|
| + const base::environment_vector& env,
|
| + int ipcfd,
|
| +#endif
|
| + CommandLine* cmd_line) {
|
| + scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
|
| + base::ProcessHandle handle = process_context_->StartProcess(
|
| +#if defined(OS_WIN)
|
| + exposed_dir,
|
| +#elif defined(OS_POSIX)
|
| + use_zygote,
|
| + cenv,
|
| + ipcfd,
|
| +#endif
|
| + cmd_line);
|
| +
|
| + process_context_->PostTask(
|
| + client_thread_id_,
|
| + FROM_HERE,
|
| + NewRunnableMethod(
|
| + this,
|
| + &MpChildProcessLauncher::Context::Notify,
|
| +#if defined(OS_LINUX)
|
| + use_zygote,
|
| +#endif
|
| + handle));
|
| + }
|
| +
|
| + void Notify(
|
| +#if defined(OS_LINUX)
|
| + bool zygote,
|
| +#endif
|
| + base::ProcessHandle handle) {
|
| + starting_ = false;
|
| + process_.set_handle(handle);
|
| +#if defined(OS_LINUX)
|
| + zygote_ = zygote;
|
| +#endif
|
| + if (client_) {
|
| + client_->OnProcessLaunched();
|
| + } else {
|
| + Terminate();
|
| + }
|
| + }
|
| +
|
| + void Terminate() {
|
| + if (!process_.handle())
|
| + return;
|
| +
|
| + // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
|
| + // don't this on the UI/IO threads.
|
| + process_context_->PostProcessLauncherTask(
|
| + FROM_HERE,
|
| + NewRunnableFunction(
|
| + &MpChildProcessLauncher::Context::TerminateInternal,
|
| +#if defined(OS_LINUX)
|
| + zygote_,
|
| +#endif
|
| + process_context_,
|
| + process_.handle()));
|
| + process_.set_handle(base::kNullProcessHandle);
|
| + }
|
| +
|
| + static void TerminateInternal(
|
| +#if defined(OS_LINUX)
|
| + bool zygote,
|
| +#endif
|
| + MpChildProcessContext* process_context,
|
| + base::ProcessHandle handle) {
|
| + base::Process process(handle);
|
| + // 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(process_context->GetNormalExitResultCode());
|
| + process_context->EnsureProcessTerminated(handle);
|
| + process.Close();
|
| + }
|
| +
|
| + MpChildProcessContext* process_context_;
|
| + Client* client_;
|
| + int client_thread_id_;
|
| + base::Process process_;
|
| + bool starting_;
|
| +
|
| +#if defined(OS_LINUX)
|
| + bool zygote_;
|
| +#endif
|
| +};
|
| +
|
| +
|
| +MpChildProcessLauncher::MpChildProcessLauncher(
|
| +#if defined(OS_WIN)
|
| + const FilePath& exposed_dir,
|
| +#elif defined(OS_POSIX)
|
| + bool use_zygote,
|
| + const base::environment_vector& environ,
|
| + int ipcfd,
|
| +#endif
|
| + CommandLine* cmd_line,
|
| + MpChildProcessContext* context,
|
| + Client* client) : process_context_(context) {
|
| + context_ = new Context(process_context_);
|
| + context_->Launch(
|
| +#if defined(OS_WIN)
|
| + exposed_dir,
|
| +#elif defined(OS_POSIX)
|
| + use_zygote,
|
| + environ,
|
| + ipcfd,
|
| +#endif
|
| + cmd_line,
|
| + client);
|
| +}
|
| +
|
| +MpChildProcessLauncher::~MpChildProcessLauncher() {
|
| + context_->ResetClient();
|
| +}
|
| +
|
| +bool MpChildProcessLauncher::IsStarting() {
|
| + return context_->starting_;
|
| +}
|
| +
|
| +base::ProcessHandle MpChildProcessLauncher::GetHandle() {
|
| + DCHECK(!context_->starting_);
|
| + return context_->process_.handle();
|
| +}
|
| +
|
| +bool MpChildProcessLauncher::DidProcessCrash() {
|
| + bool did_crash, child_exited;
|
| + base::ProcessHandle handle = context_->process_.handle();
|
| + did_crash = process_context_->CheckProcessCrash(
|
| + &child_exited,
|
| +#if defined(OS_POSIX)
|
| + context_->zygote_,
|
| +#endif
|
| + handle);
|
| +
|
| + // 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 here. Since
|
| + // DidProcessCrash called waitpid with WNOHANG, it'll reap the process.
|
| + // However, if DidProcessCrash didn't reap the child, we'll need to in
|
| + // Terminate via ProcessWatcher. So we can't close the handle here.
|
| + if (child_exited)
|
| + context_->process_.Close();
|
| +
|
| + return did_crash;
|
| +}
|
| +
|
| +void MpChildProcessLauncher::SetProcessBackgrounded(bool background) {
|
| + DCHECK(!context_->starting_);
|
| + context_->process_.SetProcessBackgrounded(background);
|
| +}
|
| +
|
| +} // namespace base
|
|
|