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 |