Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/child_process_launcher.h" | 5 #include "content/browser/child_process_launcher.h" |
| 6 | 6 |
| 7 #include <utility> // For std::pair. | 7 #include <utility> // For std::pair. |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 68 terminate_child_on_shutdown_(false) | 68 terminate_child_on_shutdown_(false) |
| 69 #else | 69 #else |
| 70 terminate_child_on_shutdown_(true) | 70 terminate_child_on_shutdown_(true) |
| 71 #endif | 71 #endif |
| 72 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 72 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 73 , zygote_(false) | 73 , zygote_(false) |
| 74 #endif | 74 #endif |
| 75 { | 75 { |
| 76 } | 76 } |
| 77 | 77 |
| 78 void Launch( | 78 void Launch(SandboxedProcessLauncherDelegate* delegate, |
| 79 SandboxedProcessLauncherDelegate* delegate, | 79 base::CommandLine* cmd_line, |
| 80 base::CommandLine* cmd_line, | 80 int child_process_id, |
| 81 int child_process_id, | 81 Client* client) { |
| 82 Client* client) { | |
| 83 client_ = client; | 82 client_ = client; |
| 84 | 83 |
| 85 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); | 84 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); |
| 86 | 85 |
| 87 #if defined(OS_ANDROID) | 86 #if defined(OS_ANDROID) |
| 88 // We need to close the client end of the IPC channel to reliably detect | 87 // We need to close the client end of the IPC channel to reliably detect |
| 89 // child termination. We will close this fd after we create the child | 88 // child termination. We will close this fd after we create the child |
| 90 // process which is asynchronous on Android. | 89 // process which is asynchronous on Android. |
| 91 ipcfd_ = delegate->GetIpcFd(); | 90 ipcfd_ = delegate->GetIpcFd(); |
| 92 #endif | 91 #endif |
| 93 BrowserThread::PostTask( | 92 BrowserThread::PostTask( |
| 94 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 93 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
| 95 base::Bind( | 94 base::Bind(&Context::LaunchInternal, |
| 96 &Context::LaunchInternal, | 95 make_scoped_refptr(this), |
| 97 make_scoped_refptr(this), | 96 client_thread_id_, |
| 98 client_thread_id_, | 97 child_process_id, |
| 99 child_process_id, | 98 delegate, |
| 100 delegate, | 99 cmd_line)); |
| 101 cmd_line)); | |
| 102 } | 100 } |
| 103 | 101 |
| 104 #if defined(OS_ANDROID) | 102 #if defined(OS_ANDROID) |
| 105 static void OnChildProcessStarted( | 103 static void OnChildProcessStarted( |
| 106 // |this_object| is NOT thread safe. Only use it to post a task back. | 104 // |this_object| is NOT thread safe. Only use it to post a task back. |
| 107 scoped_refptr<Context> this_object, | 105 scoped_refptr<Context> this_object, |
| 108 BrowserThread::ID client_thread_id, | 106 BrowserThread::ID client_thread_id, |
| 109 const base::TimeTicks begin_launch_time, | 107 const base::TimeTicks begin_launch_time, |
| 110 base::ProcessHandle handle) { | 108 base::ProcessHandle handle) { |
| 111 RecordHistograms(begin_launch_time); | 109 RecordHistograms(begin_launch_time); |
| 112 if (BrowserThread::CurrentlyOn(client_thread_id)) { | 110 if (BrowserThread::CurrentlyOn(client_thread_id)) { |
| 113 // This is always invoked on the UI thread which is commonly the | 111 // This is always invoked on the UI thread which is commonly the |
| 114 // |client_thread_id| so we can shortcut one PostTask. | 112 // |client_thread_id| so we can shortcut one PostTask. |
| 115 this_object->Notify(handle); | 113 this_object->Notify(base::ProcessObject(handle)); |
|
scottmg
2014/10/14 18:51:03
I'm a little worried that it's going to be difficu
rvargas (doing something else)
2014/10/14 19:56:54
The problem is that restricting the behavior impli
| |
| 116 } else { | 114 } else { |
| 117 BrowserThread::PostTask( | 115 BrowserThread::PostTask( |
| 118 client_thread_id, FROM_HERE, | 116 client_thread_id, FROM_HERE, |
| 119 base::Bind( | 117 base::Bind( |
| 120 &ChildProcessLauncher::Context::Notify, | 118 &ChildProcessLauncher::Context::Notify, |
| 121 this_object, | 119 this_object, |
| 122 handle)); | 120 base::Passed(base::ProcessObject(handle)))); |
| 123 } | 121 } |
| 124 } | 122 } |
| 125 #endif | 123 #endif |
| 126 | 124 |
| 127 void ResetClient() { | 125 void ResetClient() { |
| 128 // No need for locking as this function gets called on the same thread that | 126 // No need for locking as this function gets called on the same thread that |
| 129 // client_ would be used. | 127 // client_ would be used. |
| 130 CHECK(BrowserThread::CurrentlyOn(client_thread_id_)); | 128 CHECK(BrowserThread::CurrentlyOn(client_thread_id_)); |
| 131 client_ = NULL; | 129 client_ = NULL; |
| 132 } | 130 } |
| 133 | 131 |
| 134 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) { | 132 void set_terminate_child_on_shutdown(bool terminate_on_shutdown) { |
| 135 terminate_child_on_shutdown_ = terminate_on_shutdown; | 133 terminate_child_on_shutdown_ = terminate_on_shutdown; |
| 136 } | 134 } |
| 137 | 135 |
| 136 void GetTerminationStatus() { | |
| 137 termination_status_ = | |
| 138 base::GetTerminationStatus(process_.Handle(), &exit_code_); | |
| 139 } | |
| 140 | |
| 141 void SetProcessBackgrounded(bool background) { | |
| 142 base::ProcessObject to_pass = process_.Duplicate(); | |
| 143 BrowserThread::PostTask( | |
| 144 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 145 base::Bind(&Context::SetProcessBackgroundedInternal, | |
| 146 base::Passed(&to_pass), background)); | |
| 147 } | |
| 148 | |
| 138 private: | 149 private: |
| 139 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>; | 150 friend class base::RefCountedThreadSafe<ChildProcessLauncher::Context>; |
| 140 friend class ChildProcessLauncher; | 151 friend class ChildProcessLauncher; |
| 141 | 152 |
| 142 ~Context() { | 153 ~Context() { |
| 143 Terminate(); | 154 Terminate(); |
| 144 } | 155 } |
| 145 | 156 |
| 146 static void RecordHistograms(const base::TimeTicks begin_launch_time) { | 157 static void RecordHistograms(const base::TimeTicks begin_launch_time) { |
| 147 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; | 158 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 316 if (handle) | 327 if (handle) |
| 317 RecordHistograms(begin_launch_time); | 328 RecordHistograms(begin_launch_time); |
| 318 BrowserThread::PostTask( | 329 BrowserThread::PostTask( |
| 319 client_thread_id, FROM_HERE, | 330 client_thread_id, FROM_HERE, |
| 320 base::Bind( | 331 base::Bind( |
| 321 &Context::Notify, | 332 &Context::Notify, |
| 322 this_object.get(), | 333 this_object.get(), |
| 323 #if defined(OS_POSIX) && !defined(OS_MACOSX) | 334 #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| 324 use_zygote, | 335 use_zygote, |
| 325 #endif | 336 #endif |
| 326 handle)); | 337 base::Passed(base::ProcessObject(handle)))); |
| 327 #endif // !defined(OS_ANDROID) | 338 #endif // !defined(OS_ANDROID) |
| 328 } | 339 } |
| 329 | 340 |
| 330 void Notify( | 341 void Notify( |
| 331 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 342 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 332 bool zygote, | 343 bool zygote, |
| 333 #endif | 344 #endif |
| 334 base::ProcessHandle handle) { | 345 base::ProcessObject process) { |
| 335 #if defined(OS_ANDROID) | 346 #if defined(OS_ANDROID) |
| 336 // Finally close the ipcfd | 347 // Finally close the ipcfd |
| 337 base::ScopedFD ipcfd_closer(ipcfd_); | 348 base::ScopedFD ipcfd_closer(ipcfd_); |
| 338 #endif | 349 #endif |
| 339 starting_ = false; | 350 starting_ = false; |
| 340 process_.set_handle(handle); | 351 process_ = process.Pass(); |
| 341 if (!handle) | 352 if (!process_.IsValid()) |
| 342 LOG(ERROR) << "Failed to launch child process"; | 353 LOG(ERROR) << "Failed to launch child process"; |
| 343 | 354 |
| 344 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 355 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 345 zygote_ = zygote; | 356 zygote_ = zygote; |
| 346 #endif | 357 #endif |
| 347 if (client_) { | 358 if (client_) { |
| 348 if (handle) { | 359 if (process_.IsValid()) { |
| 349 client_->OnProcessLaunched(); | 360 client_->OnProcessLaunched(); |
| 350 } else { | 361 } else { |
| 351 client_->OnProcessLaunchFailed(); | 362 client_->OnProcessLaunchFailed(); |
| 352 } | 363 } |
| 353 } else { | 364 } else { |
| 354 Terminate(); | 365 Terminate(); |
| 355 } | 366 } |
| 356 } | 367 } |
| 357 | 368 |
| 358 void Terminate() { | 369 void Terminate() { |
| 359 if (!process_.handle()) | 370 if (!process_.IsValid()) |
| 360 return; | 371 return; |
| 361 | 372 |
| 362 if (!terminate_child_on_shutdown_) | 373 if (!terminate_child_on_shutdown_) |
| 363 return; | 374 return; |
| 364 | 375 |
| 365 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So | 376 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So |
| 366 // don't this on the UI/IO threads. | 377 // don't this on the UI/IO threads. |
| 367 BrowserThread::PostTask( | 378 BrowserThread::PostTask( |
| 368 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 379 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
| 369 base::Bind( | 380 base::Bind( |
| 370 &Context::TerminateInternal, | 381 &Context::TerminateInternal, |
| 371 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 382 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 372 zygote_, | 383 zygote_, |
| 373 #endif | 384 #endif |
| 374 process_.handle())); | 385 base::Passed(&process_))); |
| 375 process_.set_handle(base::kNullProcessHandle); | |
| 376 } | 386 } |
| 377 | 387 |
| 378 static void SetProcessBackgrounded(base::ProcessHandle handle, | 388 static void SetProcessBackgroundedInternal(base::ProcessObject process, |
| 379 bool background) { | 389 bool background) { |
| 380 base::Process process(handle); | |
| 381 process.SetProcessBackgrounded(background); | 390 process.SetProcessBackgrounded(background); |
| 382 #if defined(OS_ANDROID) | 391 #if defined(OS_ANDROID) |
| 383 SetChildProcessInForeground(handle, !background); | 392 SetChildProcessInForeground(process.Handle(), !background); |
| 384 #endif | 393 #endif |
| 385 } | 394 } |
| 386 | 395 |
| 387 static void TerminateInternal( | 396 static void TerminateInternal( |
| 388 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 397 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 389 bool zygote, | 398 bool zygote, |
| 390 #endif | 399 #endif |
| 391 base::ProcessHandle handle) { | 400 base::ProcessObject process) { |
| 392 #if defined(OS_ANDROID) | 401 #if defined(OS_ANDROID) |
| 393 VLOG(0) << "ChromeProcess: Stopping process with handle " << handle; | 402 VLOG(1) << "ChromeProcess: Stopping process with handle " |
| 394 StopChildProcess(handle); | 403 << process.Handle(); |
| 404 StopChildProcess(process.Handle()); | |
| 395 #else | 405 #else |
| 396 base::Process process(handle); | |
| 397 // Client has gone away, so just kill the process. Using exit code 0 | 406 // Client has gone away, so just kill the process. Using exit code 0 |
| 398 // means that UMA won't treat this as a crash. | 407 // means that UMA won't treat this as a crash. |
| 399 process.Terminate(RESULT_CODE_NORMAL_EXIT); | 408 process.Terminate(RESULT_CODE_NORMAL_EXIT); |
| 400 // On POSIX, we must additionally reap the child. | 409 // On POSIX, we must additionally reap the child. |
| 401 #if defined(OS_POSIX) | 410 #if defined(OS_POSIX) |
| 402 #if !defined(OS_MACOSX) | 411 #if !defined(OS_MACOSX) |
| 403 if (zygote) { | 412 if (zygote) { |
| 404 // If the renderer was created via a zygote, we have to proxy the reaping | 413 // If the renderer was created via a zygote, we have to proxy the reaping |
| 405 // through the zygote process. | 414 // through the zygote process. |
| 406 ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(handle); | 415 ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(process.Handle()); |
| 407 } else | 416 } else |
| 408 #endif // !OS_MACOSX | 417 #endif // !OS_MACOSX |
| 409 { | 418 { |
| 410 base::EnsureProcessTerminated(handle); | 419 base::EnsureProcessTerminated(process.Handle()); |
| 411 } | 420 } |
| 412 #endif // OS_POSIX | 421 #endif // OS_POSIX |
| 413 process.Close(); | |
| 414 #endif // defined(OS_ANDROID) | 422 #endif // defined(OS_ANDROID) |
| 415 } | 423 } |
| 416 | 424 |
| 417 Client* client_; | 425 Client* client_; |
| 418 BrowserThread::ID client_thread_id_; | 426 BrowserThread::ID client_thread_id_; |
| 419 base::Process process_; | 427 base::ProcessObject process_; |
| 420 base::TerminationStatus termination_status_; | 428 base::TerminationStatus termination_status_; |
| 421 int exit_code_; | 429 int exit_code_; |
| 422 bool starting_; | 430 bool starting_; |
| 423 // Controls whether the child process should be terminated on browser | 431 // Controls whether the child process should be terminated on browser |
| 424 // shutdown. Default behavior is to terminate the child. | 432 // shutdown. Default behavior is to terminate the child. |
| 425 bool terminate_child_on_shutdown_; | 433 bool terminate_child_on_shutdown_; |
| 426 #if defined(OS_ANDROID) | 434 #if defined(OS_ANDROID) |
| 427 // The fd to close after creating the process. | 435 // The fd to close after creating the process. |
| 428 int ipcfd_; | 436 int ipcfd_; |
| 429 #elif defined(OS_POSIX) && !defined(OS_MACOSX) | 437 #elif defined(OS_POSIX) && !defined(OS_MACOSX) |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 446 } | 454 } |
| 447 | 455 |
| 448 ChildProcessLauncher::~ChildProcessLauncher() { | 456 ChildProcessLauncher::~ChildProcessLauncher() { |
| 449 context_->ResetClient(); | 457 context_->ResetClient(); |
| 450 } | 458 } |
| 451 | 459 |
| 452 bool ChildProcessLauncher::IsStarting() { | 460 bool ChildProcessLauncher::IsStarting() { |
| 453 return context_->starting_; | 461 return context_->starting_; |
| 454 } | 462 } |
| 455 | 463 |
| 456 base::ProcessHandle ChildProcessLauncher::GetHandle() { | 464 const base::ProcessObject& ChildProcessLauncher::GetProcess() const { |
| 457 DCHECK(!context_->starting_); | 465 DCHECK(!context_->starting_); |
| 458 return context_->process_.handle(); | 466 return context_->process_; |
| 459 } | 467 } |
| 460 | 468 |
| 461 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( | 469 base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus( |
| 462 bool known_dead, | 470 bool known_dead, |
| 463 int* exit_code) { | 471 int* exit_code) { |
| 464 base::ProcessHandle handle = context_->process_.handle(); | 472 if (!context_->process_.IsValid()) { |
| 465 if (handle == base::kNullProcessHandle) { | |
| 466 // Process is already gone, so return the cached termination status. | 473 // Process is already gone, so return the cached termination status. |
| 467 if (exit_code) | 474 if (exit_code) |
| 468 *exit_code = context_->exit_code_; | 475 *exit_code = context_->exit_code_; |
| 469 return context_->termination_status_; | 476 return context_->termination_status_; |
| 470 } | 477 } |
| 471 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 478 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 472 if (context_->zygote_) { | 479 if (context_->zygote_) { |
| 473 context_->termination_status_ = ZygoteHostImpl::GetInstance()-> | 480 context_->termination_status_ = ZygoteHostImpl::GetInstance()-> |
| 474 GetTerminationStatus(handle, known_dead, &context_->exit_code_); | 481 GetTerminationStatus(context_->process_.Handle(), known_dead, |
| 482 &context_->exit_code_); | |
| 475 } else if (known_dead) { | 483 } else if (known_dead) { |
| 476 context_->termination_status_ = | 484 context_->termination_status_ = |
| 477 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); | 485 base::GetKnownDeadTerminationStatus(context_->process_.Handle(), |
| 486 &context_->exit_code_); | |
| 478 } else { | 487 } else { |
| 479 #elif defined(OS_MACOSX) | 488 #elif defined(OS_MACOSX) |
| 480 if (known_dead) { | 489 if (known_dead) { |
| 481 context_->termination_status_ = | 490 context_->termination_status_ = |
| 482 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); | 491 base::GetKnownDeadTerminationStatus(context_->process_.Handle(), |
| 492 &context_->exit_code_); | |
| 483 } else { | 493 } else { |
| 484 #elif defined(OS_ANDROID) | 494 #elif defined(OS_ANDROID) |
| 485 if (IsChildProcessOomProtected(handle)) { | 495 if (IsChildProcessOomProtected(context_->process_.Handle())) { |
| 486 context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED; | 496 context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED; |
| 487 } else { | 497 } else { |
| 488 #else | 498 #else |
| 489 { | 499 { |
| 490 #endif | 500 #endif |
| 491 context_->termination_status_ = | 501 context_->GetTerminationStatus(); |
| 492 base::GetTerminationStatus(handle, &context_->exit_code_); | |
| 493 } | 502 } |
| 494 | 503 |
| 495 if (exit_code) | 504 if (exit_code) |
| 496 *exit_code = context_->exit_code_; | 505 *exit_code = context_->exit_code_; |
| 497 | 506 |
| 498 // POSIX: If the process crashed, then the kernel closed the socket | 507 // POSIX: If the process crashed, then the kernel closed the socket |
| 499 // for it and so the child has already died by the time we get | 508 // for it and so the child has already died by the time we get |
| 500 // here. Since GetTerminationStatus called waitpid with WNOHANG, | 509 // here. Since GetTerminationStatus called waitpid with WNOHANG, |
| 501 // it'll reap the process. However, if GetTerminationStatus didn't | 510 // it'll reap the process. However, if GetTerminationStatus didn't |
| 502 // reap the child (because it was still running), we'll need to | 511 // reap the child (because it was still running), we'll need to |
| 503 // Terminate via ProcessWatcher. So we can't close the handle here. | 512 // Terminate via ProcessWatcher. So we can't close the handle here. |
| 504 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) | 513 if (context_->termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING) |
| 505 context_->process_.Close(); | 514 context_->process_.Close(); |
| 506 | 515 |
| 507 return context_->termination_status_; | 516 return context_->termination_status_; |
| 508 } | 517 } |
| 509 | 518 |
| 510 void ChildProcessLauncher::SetProcessBackgrounded(bool background) { | 519 void ChildProcessLauncher::SetProcessBackgrounded(bool background) { |
| 511 BrowserThread::PostTask( | 520 context_->process_.SetProcessBackgrounded(background); |
| 512 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | |
| 513 base::Bind( | |
| 514 &ChildProcessLauncher::Context::SetProcessBackgrounded, | |
| 515 GetHandle(), background)); | |
| 516 } | 521 } |
| 517 | 522 |
| 518 void ChildProcessLauncher::SetTerminateChildOnShutdown( | 523 void ChildProcessLauncher::SetTerminateChildOnShutdown( |
| 519 bool terminate_on_shutdown) { | 524 bool terminate_on_shutdown) { |
| 520 if (context_.get()) | 525 if (context_.get()) |
| 521 context_->set_terminate_child_on_shutdown(terminate_on_shutdown); | 526 context_->set_terminate_child_on_shutdown(terminate_on_shutdown); |
| 522 } | 527 } |
| 523 | 528 |
| 524 } // namespace content | 529 } // namespace content |
| OLD | NEW |