OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/mp/mp_child_process_launcher.h" |
| 6 |
| 7 #include "base/command_line.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/scoped_ptr.h" |
| 10 #include "base/thread.h" |
| 11 |
| 12 #if defined(OS_POSIX) |
| 13 #include "base/global_descriptors_posix.h" |
| 14 #endif |
| 15 |
| 16 namespace base { |
| 17 |
| 18 // Having the functionality of MpChildProcessLauncher be in an internal |
| 19 // ref counted object allows us to automatically terminate the process when the |
| 20 // parent class destructs, while still holding on to state that we need. |
| 21 class MpChildProcessLauncher::Context |
| 22 : public base::RefCountedThreadSafe<MpChildProcessLauncher::Context> { |
| 23 public: |
| 24 Context(MpChildProcessContext* process_context) |
| 25 : process_context_(process_context), |
| 26 starting_(true) |
| 27 #if defined(OS_LINUX) |
| 28 , zygote_(false) |
| 29 #endif |
| 30 { |
| 31 } |
| 32 |
| 33 void Launch( |
| 34 #if defined(OS_WIN) |
| 35 const FilePath& exposed_dir, |
| 36 #elif defined(OS_POSIX) |
| 37 bool use_zygote, |
| 38 const base::environment_vector& environ, |
| 39 int ipcfd, |
| 40 #endif |
| 41 CommandLine* cmd_line, |
| 42 Client* client) { |
| 43 client_ = client; |
| 44 |
| 45 CHECK(process_context_->GetCurrentThreadIdentifier(&client_thread_id_)); |
| 46 |
| 47 process_context_->PostProcessLauncherTask( |
| 48 FROM_HERE, |
| 49 NewRunnableMethod( |
| 50 this, |
| 51 &Context::LaunchInternal, |
| 52 #if defined(OS_WIN) |
| 53 exposed_dir, |
| 54 #elif defined(POSIX) |
| 55 use_zygote, |
| 56 environ, |
| 57 ipcfd, |
| 58 #endif |
| 59 cmd_line)); |
| 60 } |
| 61 |
| 62 void ResetClient() { |
| 63 // No need for locking as this function gets called on the same thread that |
| 64 // client_ would be used. |
| 65 CHECK(process_context_->CurrentlyOnThread(client_thread_id_)); |
| 66 client_ = NULL; |
| 67 } |
| 68 |
| 69 private: |
| 70 friend class base::RefCountedThreadSafe<MpChildProcessLauncher::Context>; |
| 71 friend class MpChildProcessLauncher; |
| 72 |
| 73 ~Context() { |
| 74 Terminate(); |
| 75 } |
| 76 |
| 77 void LaunchInternal( |
| 78 #if defined(OS_WIN) |
| 79 const FilePath& exposed_dir, |
| 80 #elif defined(OS_POSIX) |
| 81 bool use_zygote, |
| 82 const base::environment_vector& env, |
| 83 int ipcfd, |
| 84 #endif |
| 85 CommandLine* cmd_line) { |
| 86 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); |
| 87 base::ProcessHandle handle = process_context_->StartProcess( |
| 88 #if defined(OS_WIN) |
| 89 exposed_dir, |
| 90 #elif defined(OS_POSIX) |
| 91 use_zygote, |
| 92 cenv, |
| 93 ipcfd, |
| 94 #endif |
| 95 cmd_line); |
| 96 |
| 97 process_context_->PostTask( |
| 98 client_thread_id_, |
| 99 FROM_HERE, |
| 100 NewRunnableMethod( |
| 101 this, |
| 102 &MpChildProcessLauncher::Context::Notify, |
| 103 #if defined(OS_LINUX) |
| 104 use_zygote, |
| 105 #endif |
| 106 handle)); |
| 107 } |
| 108 |
| 109 void Notify( |
| 110 #if defined(OS_LINUX) |
| 111 bool zygote, |
| 112 #endif |
| 113 base::ProcessHandle handle) { |
| 114 starting_ = false; |
| 115 process_.set_handle(handle); |
| 116 #if defined(OS_LINUX) |
| 117 zygote_ = zygote; |
| 118 #endif |
| 119 if (client_) { |
| 120 client_->OnProcessLaunched(); |
| 121 } else { |
| 122 Terminate(); |
| 123 } |
| 124 } |
| 125 |
| 126 void Terminate() { |
| 127 if (!process_.handle()) |
| 128 return; |
| 129 |
| 130 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So |
| 131 // don't this on the UI/IO threads. |
| 132 process_context_->PostProcessLauncherTask( |
| 133 FROM_HERE, |
| 134 NewRunnableFunction( |
| 135 &MpChildProcessLauncher::Context::TerminateInternal, |
| 136 #if defined(OS_LINUX) |
| 137 zygote_, |
| 138 #endif |
| 139 process_context_, |
| 140 process_.handle())); |
| 141 process_.set_handle(base::kNullProcessHandle); |
| 142 } |
| 143 |
| 144 static void TerminateInternal( |
| 145 #if defined(OS_LINUX) |
| 146 bool zygote, |
| 147 #endif |
| 148 MpChildProcessContext* process_context, |
| 149 base::ProcessHandle handle) { |
| 150 base::Process process(handle); |
| 151 // Client has gone away, so just kill the process. Using exit code 0 |
| 152 // means that UMA won't treat this as a crash. |
| 153 process.Terminate(process_context->GetNormalExitResultCode()); |
| 154 process_context->EnsureProcessTerminated(handle); |
| 155 process.Close(); |
| 156 } |
| 157 |
| 158 MpChildProcessContext* process_context_; |
| 159 Client* client_; |
| 160 int client_thread_id_; |
| 161 base::Process process_; |
| 162 bool starting_; |
| 163 |
| 164 #if defined(OS_LINUX) |
| 165 bool zygote_; |
| 166 #endif |
| 167 }; |
| 168 |
| 169 |
| 170 MpChildProcessLauncher::MpChildProcessLauncher( |
| 171 #if defined(OS_WIN) |
| 172 const FilePath& exposed_dir, |
| 173 #elif defined(OS_POSIX) |
| 174 bool use_zygote, |
| 175 const base::environment_vector& environ, |
| 176 int ipcfd, |
| 177 #endif |
| 178 CommandLine* cmd_line, |
| 179 MpChildProcessContext* context, |
| 180 Client* client) : process_context_(context) { |
| 181 context_ = new Context(process_context_); |
| 182 context_->Launch( |
| 183 #if defined(OS_WIN) |
| 184 exposed_dir, |
| 185 #elif defined(OS_POSIX) |
| 186 use_zygote, |
| 187 environ, |
| 188 ipcfd, |
| 189 #endif |
| 190 cmd_line, |
| 191 client); |
| 192 } |
| 193 |
| 194 MpChildProcessLauncher::~MpChildProcessLauncher() { |
| 195 context_->ResetClient(); |
| 196 } |
| 197 |
| 198 bool MpChildProcessLauncher::IsStarting() { |
| 199 return context_->starting_; |
| 200 } |
| 201 |
| 202 base::ProcessHandle MpChildProcessLauncher::GetHandle() { |
| 203 DCHECK(!context_->starting_); |
| 204 return context_->process_.handle(); |
| 205 } |
| 206 |
| 207 bool MpChildProcessLauncher::DidProcessCrash() { |
| 208 bool did_crash, child_exited; |
| 209 base::ProcessHandle handle = context_->process_.handle(); |
| 210 did_crash = process_context_->CheckProcessCrash( |
| 211 &child_exited, |
| 212 #if defined(OS_POSIX) |
| 213 context_->zygote_, |
| 214 #endif |
| 215 handle); |
| 216 |
| 217 // POSIX: If the process crashed, then the kernel closed the socket for it |
| 218 // and so the child has already died by the time we get here. Since |
| 219 // DidProcessCrash called waitpid with WNOHANG, it'll reap the process. |
| 220 // However, if DidProcessCrash didn't reap the child, we'll need to in |
| 221 // Terminate via ProcessWatcher. So we can't close the handle here. |
| 222 if (child_exited) |
| 223 context_->process_.Close(); |
| 224 |
| 225 return did_crash; |
| 226 } |
| 227 |
| 228 void MpChildProcessLauncher::SetProcessBackgrounded(bool background) { |
| 229 DCHECK(!context_->starting_); |
| 230 context_->process_.SetProcessBackgrounded(background); |
| 231 } |
| 232 |
| 233 } // namespace base |
OLD | NEW |