
Only content/ now has the ability to create BrowserThread objects, with the exception that tests can create the content::TestBrowserThread subclass, and (temporarily) code in chrome/ can create the DeprecatedBrowserThread subclass. A follow-up change will make content/ take care of its own thread creation, remove DeprecatedBrowserThread, and move all state and non-trivial constructors from BrowserThread down to BrowserThreadImpl. Also moved BrowserProcessSubThread into content/ namespace. As part of follow-up cleanup, chrome/ will stop using this class. BUG=98716 TEST=existing Review URL: http://codereview.chromium.org/8392042 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@107718 0039d316-1c4b-4281-b951-d872f2087c98
262 lines
8.4 KiB
C++
262 lines
8.4 KiB
C++
// Copyright (c) 2011 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/browser_child_process_host.h"
|
|
|
|
#include "base/command_line.h"
|
|
#include "base/file_path.h"
|
|
#include "base/lazy_instance.h"
|
|
#include "base/logging.h"
|
|
#include "base/metrics/histogram.h"
|
|
#include "base/path_service.h"
|
|
#include "base/process_util.h"
|
|
#include "base/stl_util.h"
|
|
#include "base/string_util.h"
|
|
#include "content/browser/renderer_host/resource_message_filter.h"
|
|
#include "content/browser/trace_message_filter.h"
|
|
#include "content/common/plugin_messages.h"
|
|
#include "content/common/process_watcher.h"
|
|
#include "content/public/browser/browser_thread.h"
|
|
#include "content/public/browser/content_browser_client.h"
|
|
#include "content/public/browser/notification_service.h"
|
|
#include "content/public/browser/notification_types.h"
|
|
#include "content/public/common/content_switches.h"
|
|
#include "content/public/common/result_codes.h"
|
|
|
|
#if defined(OS_WIN)
|
|
#include "base/synchronization/waitable_event.h"
|
|
#endif // OS_WIN
|
|
|
|
namespace {
|
|
|
|
typedef std::list<BrowserChildProcessHost*> ChildProcessList;
|
|
static base::LazyInstance<ChildProcessList> g_child_process_list(
|
|
base::LINKER_INITIALIZED);
|
|
|
|
// The NotificationTask is used to notify about plugin process connection/
|
|
// disconnection. It is needed because the notifications in the
|
|
// NotificationService must happen in the main thread.
|
|
class ChildNotificationTask : public Task {
|
|
public:
|
|
ChildNotificationTask(
|
|
int notification_type, ChildProcessInfo* info)
|
|
: notification_type_(notification_type), info_(*info) { }
|
|
|
|
virtual void Run() {
|
|
content::NotificationService::current()->
|
|
Notify(notification_type_, content::NotificationService::AllSources(),
|
|
content::Details<ChildProcessInfo>(&info_));
|
|
}
|
|
|
|
private:
|
|
int notification_type_;
|
|
ChildProcessInfo info_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
BrowserChildProcessHost::BrowserChildProcessHost(
|
|
ChildProcessInfo::ProcessType type)
|
|
: ChildProcessInfo(type, -1),
|
|
ALLOW_THIS_IN_INITIALIZER_LIST(client_(this)),
|
|
disconnect_was_alive_(false) {
|
|
AddFilter(new TraceMessageFilter);
|
|
|
|
g_child_process_list.Get().push_back(this);
|
|
}
|
|
|
|
BrowserChildProcessHost::~BrowserChildProcessHost() {
|
|
g_child_process_list.Get().remove(this);
|
|
}
|
|
|
|
// static
|
|
void BrowserChildProcessHost::TerminateAll() {
|
|
// Make a copy since the ChildProcessHost dtor mutates the original list.
|
|
ChildProcessList copy = g_child_process_list.Get();
|
|
STLDeleteElements(©);
|
|
}
|
|
|
|
void BrowserChildProcessHost::Launch(
|
|
#if defined(OS_WIN)
|
|
const FilePath& exposed_dir,
|
|
#elif defined(OS_POSIX)
|
|
bool use_zygote,
|
|
const base::environment_vector& environ,
|
|
#endif
|
|
CommandLine* cmd_line) {
|
|
|
|
content::GetContentClient()->browser()->AppendExtraCommandLineSwitches(
|
|
cmd_line, id());
|
|
|
|
child_process_.reset(new ChildProcessLauncher(
|
|
#if defined(OS_WIN)
|
|
exposed_dir,
|
|
#elif defined(OS_POSIX)
|
|
use_zygote,
|
|
environ,
|
|
channel()->TakeClientFileDescriptor(),
|
|
#endif
|
|
cmd_line,
|
|
&client_));
|
|
}
|
|
|
|
base::ProcessHandle BrowserChildProcessHost::GetChildProcessHandle() const {
|
|
DCHECK(child_process_.get())
|
|
<< "Requesting a child process handle before launching.";
|
|
DCHECK(child_process_->GetHandle())
|
|
<< "Requesting a child process handle before launch has completed OK.";
|
|
return child_process_->GetHandle();
|
|
}
|
|
|
|
void BrowserChildProcessHost::ForceShutdown() {
|
|
g_child_process_list.Get().remove(this);
|
|
ChildProcessHost::ForceShutdown();
|
|
}
|
|
|
|
void BrowserChildProcessHost::SetTerminateChildOnShutdown(
|
|
bool terminate_on_shutdown) {
|
|
child_process_->SetTerminateChildOnShutdown(terminate_on_shutdown);
|
|
}
|
|
|
|
void BrowserChildProcessHost::Notify(int type) {
|
|
BrowserThread::PostTask(
|
|
BrowserThread::UI, FROM_HERE, new ChildNotificationTask(type, this));
|
|
}
|
|
|
|
base::TerminationStatus BrowserChildProcessHost::GetChildTerminationStatus(
|
|
int* exit_code) {
|
|
return child_process_->GetChildTerminationStatus(exit_code);
|
|
}
|
|
|
|
void BrowserChildProcessHost::OnChannelConnected(int32 peer_pid) {
|
|
Notify(content::NOTIFICATION_CHILD_PROCESS_HOST_CONNECTED);
|
|
}
|
|
|
|
// The ChildProcessHost default implementation calls OnChildDied() always
|
|
// but at this layer and below we need to have the final child process exit
|
|
// code to properly bucket crashes vs kills. At least on Windows we can do
|
|
// this if we wait until the process handle is signaled, however this means
|
|
// that this function can be called twice: once from the actual channel error
|
|
// and once from OnWaitableEventSignaled().
|
|
void BrowserChildProcessHost::OnChildDisconnected() {
|
|
DCHECK(handle() != base::kNullProcessHandle);
|
|
int exit_code;
|
|
base::TerminationStatus status = GetChildTerminationStatus(&exit_code);
|
|
switch (status) {
|
|
case base::TERMINATION_STATUS_PROCESS_CRASHED:
|
|
case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: {
|
|
OnProcessCrashed(exit_code);
|
|
// Report that this child process crashed.
|
|
Notify(content::NOTIFICATION_CHILD_PROCESS_CRASHED);
|
|
UMA_HISTOGRAM_ENUMERATION("ChildProcess.Crashed",
|
|
this->type(), MAX_PROCESS);
|
|
if (disconnect_was_alive_) {
|
|
UMA_HISTOGRAM_ENUMERATION("ChildProcess.CrashedWasAlive",
|
|
this->type(), MAX_PROCESS);
|
|
}
|
|
break;
|
|
}
|
|
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: {
|
|
OnProcessWasKilled(exit_code);
|
|
// Report that this child process was killed.
|
|
Notify(content::NOTIFICATION_CHILD_PROCESS_WAS_KILLED);
|
|
UMA_HISTOGRAM_ENUMERATION("ChildProcess.Killed",
|
|
this->type(), MAX_PROCESS);
|
|
if (disconnect_was_alive_) {
|
|
UMA_HISTOGRAM_ENUMERATION("ChildProcess.KilledWasAlive",
|
|
this->type(), MAX_PROCESS);
|
|
}
|
|
break;
|
|
}
|
|
case base::TERMINATION_STATUS_STILL_RUNNING: {
|
|
// exit code not yet available.
|
|
disconnect_was_alive_ = true;
|
|
#if defined(OS_WIN)
|
|
child_watcher_.StartWatching(new base::WaitableEvent(handle()), this);
|
|
return;
|
|
#else
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
UMA_HISTOGRAM_ENUMERATION("ChildProcess.Disconnected",
|
|
this->type(), MAX_PROCESS);
|
|
// Notify in the main loop of the disconnection.
|
|
Notify(content::NOTIFICATION_CHILD_PROCESS_HOST_DISCONNECTED);
|
|
OnChildDied();
|
|
}
|
|
|
|
// The child process handle has been signaled so the exit code is finally
|
|
// available. Unfortunately STILL_ACTIVE (0x103) is a valid exit code in
|
|
// which case we should not call OnChildDisconnected() or else we will be
|
|
// waiting forever.
|
|
void BrowserChildProcessHost::OnWaitableEventSignaled(
|
|
base::WaitableEvent* waitable_event) {
|
|
#if defined (OS_WIN)
|
|
unsigned long exit_code = 0;
|
|
GetExitCodeProcess(waitable_event->Release(), &exit_code);
|
|
delete waitable_event;
|
|
if (exit_code == STILL_ACTIVE)
|
|
OnChildDied();
|
|
BrowserChildProcessHost::OnChildDisconnected();
|
|
#endif
|
|
}
|
|
|
|
void BrowserChildProcessHost::ShutdownStarted() {
|
|
// Must remove the process from the list now, in case it gets used for a
|
|
// new instance before our watcher tells us that the process terminated.
|
|
g_child_process_list.Get().remove(this);
|
|
}
|
|
|
|
BrowserChildProcessHost::ClientHook::ClientHook(BrowserChildProcessHost* host)
|
|
: host_(host) {
|
|
}
|
|
|
|
void BrowserChildProcessHost::ClientHook::OnProcessLaunched() {
|
|
if (!host_->child_process_->GetHandle()) {
|
|
host_->OnChildDied();
|
|
return;
|
|
}
|
|
host_->set_handle(host_->child_process_->GetHandle());
|
|
host_->OnProcessLaunched();
|
|
}
|
|
|
|
BrowserChildProcessHost::Iterator::Iterator()
|
|
: all_(true), type_(UNKNOWN_PROCESS) {
|
|
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) <<
|
|
"ChildProcessInfo::Iterator must be used on the IO thread.";
|
|
iterator_ = g_child_process_list.Get().begin();
|
|
}
|
|
|
|
BrowserChildProcessHost::Iterator::Iterator(ChildProcessInfo::ProcessType type)
|
|
: all_(false), type_(type) {
|
|
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)) <<
|
|
"ChildProcessInfo::Iterator must be used on the IO thread.";
|
|
iterator_ = g_child_process_list.Get().begin();
|
|
if (!Done() && (*iterator_)->type() != type_)
|
|
++(*this);
|
|
}
|
|
|
|
BrowserChildProcessHost* BrowserChildProcessHost::Iterator::operator++() {
|
|
do {
|
|
++iterator_;
|
|
if (Done())
|
|
break;
|
|
|
|
if (!all_ && (*iterator_)->type() != type_)
|
|
continue;
|
|
|
|
return *iterator_;
|
|
} while (true);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool BrowserChildProcessHost::Iterator::Done() {
|
|
return iterator_ == g_child_process_list.Get().end();
|
|
}
|