blob: a2c328c24061c68d2ba84d442b3c65ee99f7dbac [file] [log] [blame]
// Copyright (c) 2010 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 "chrome/browser/renderer_host/render_process_host.h"
#include "base/rand_util.h"
#include "base/sys_info.h"
#include "chrome/browser/child_process_security_policy.h"
#include "chrome/common/child_process_info.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/notification_service.h"
namespace {
size_t max_renderer_count_override = 0;
size_t GetMaxRendererProcessCount() {
if (max_renderer_count_override)
return max_renderer_count_override;
// Defines the maximum number of renderer processes according to the
// amount of installed memory as reported by the OS. The table
// values are calculated by assuming that you want the renderers to
// use half of the installed ram and assuming that each tab uses
// ~40MB, however the curve is not linear but piecewise linear with
// interleaved slopes of 3 and 2.
// If you modify this table you need to adjust browser\browser_uitest.cc
// to match the expected number of processes.
static const size_t kMaxRenderersByRamTier[] = {
3, // less than 256MB
6, // 256MB
9, // 512MB
12, // 768MB
14, // 1024MB
18, // 1280MB
20, // 1536MB
22, // 1792MB
24, // 2048MB
26, // 2304MB
29, // 2560MB
32, // 2816MB
35, // 3072MB
38, // 3328MB
40 // 3584MB
};
static size_t max_count = 0;
if (!max_count) {
size_t memory_tier = base::SysInfo::AmountOfPhysicalMemoryMB() / 256;
if (memory_tier >= arraysize(kMaxRenderersByRamTier))
max_count = chrome::kMaxRendererProcessCount;
else
max_count = kMaxRenderersByRamTier[memory_tier];
}
return max_count;
}
// Returns true if the given host is suitable for launching a new view
// associated with the given profile.
static bool IsSuitableHost(RenderProcessHost* host, Profile* profile,
RenderProcessHost::Type type) {
if (host->profile() != profile)
return false;
RenderProcessHost::Type host_type = RenderProcessHost::TYPE_NORMAL;
if (ChildProcessSecurityPolicy::GetInstance()->HasDOMUIBindings(host->id()))
host_type = RenderProcessHost::TYPE_DOMUI;
if (ChildProcessSecurityPolicy::GetInstance()->
HasExtensionBindings(host->id()))
host_type = RenderProcessHost::TYPE_EXTENSION;
return host_type == type;
}
// the global list of all renderer processes
IDMap<RenderProcessHost> all_hosts;
} // namespace
extern bool g_log_bug53991;
// static
bool RenderProcessHost::run_renderer_in_process_ = false;
// static
void RenderProcessHost::SetMaxRendererProcessCount(size_t count) {
max_renderer_count_override = count;
}
RenderProcessHost::RenderProcessHost(Profile* profile)
: max_page_id_(-1),
fast_shutdown_started_(false),
deleting_soon_(false),
id_(ChildProcessInfo::GenerateChildProcessUniqueId()),
profile_(profile),
sudden_termination_allowed_(true),
ignore_input_events_(false) {
all_hosts.AddWithID(this, id());
all_hosts.set_check_on_null_data(true);
// Initialize |child_process_activity_time_| to a reasonable value.
mark_child_process_activity_time();
}
RenderProcessHost::~RenderProcessHost() {
// In unit tests, Release() might not have been called.
if (all_hosts.Lookup(id()))
all_hosts.Remove(id());
}
void RenderProcessHost::Attach(IPC::Channel::Listener* listener,
int routing_id) {
VLOG_IF(1, g_log_bug53991) << "AddListener: (" << this << "): " << routing_id;
listeners_.AddWithID(listener, routing_id);
}
void RenderProcessHost::Release(int listener_id) {
VLOG_IF(1, g_log_bug53991) << "RemListener: (" << this << "): "
<< listener_id;
DCHECK(listeners_.Lookup(listener_id) != NULL);
listeners_.Remove(listener_id);
// Make sure that all associated resource requests are stopped.
CancelResourceRequests(listener_id);
// When no other owners of this object, we can delete ourselves
if (listeners_.IsEmpty()) {
NotificationService::current()->Notify(
NotificationType::RENDERER_PROCESS_TERMINATED,
Source<RenderProcessHost>(this), NotificationService::NoDetails());
MessageLoop::current()->DeleteSoon(FROM_HERE, this);
deleting_soon_ = true;
// Remove ourself from the list of renderer processes so that we can't be
// reused in between now and when the Delete task runs.
all_hosts.Remove(id());
}
}
void RenderProcessHost::ReportExpectingClose(int32 listener_id) {
listeners_expecting_close_.insert(listener_id);
}
void RenderProcessHost::UpdateMaxPageID(int32 page_id) {
if (page_id > max_page_id_)
max_page_id_ = page_id;
}
bool RenderProcessHost::FastShutdownForPageCount(size_t count) {
if (listeners_.size() == count)
return FastShutdownIfPossible();
return false;
}
// static
RenderProcessHost::iterator RenderProcessHost::AllHostsIterator() {
return iterator(&all_hosts);
}
// static
RenderProcessHost* RenderProcessHost::FromID(int render_process_id) {
return all_hosts.Lookup(render_process_id);
}
// static
bool RenderProcessHost::ShouldTryToUseExistingProcessHost() {
size_t renderer_process_count = all_hosts.size();
// NOTE: Sometimes it's necessary to create more render processes than
// GetMaxRendererProcessCount(), for instance when we want to create
// a renderer process for a profile that has no existing renderers.
// This is OK in moderation, since the GetMaxRendererProcessCount()
// is conservative.
return run_renderer_in_process() ||
(renderer_process_count >= GetMaxRendererProcessCount());
}
// static
RenderProcessHost* RenderProcessHost::GetExistingProcessHost(Profile* profile,
Type type) {
// First figure out which existing renderers we can use.
std::vector<RenderProcessHost*> suitable_renderers;
suitable_renderers.reserve(all_hosts.size());
iterator iter(AllHostsIterator());
while (!iter.IsAtEnd()) {
if (run_renderer_in_process() ||
IsSuitableHost(iter.GetCurrentValue(), profile, type))
suitable_renderers.push_back(iter.GetCurrentValue());
iter.Advance();
}
// Now pick a random suitable renderer, if we have any.
if (!suitable_renderers.empty()) {
int suitable_count = static_cast<int>(suitable_renderers.size());
int random_index = base::RandInt(0, suitable_count - 1);
return suitable_renderers[random_index];
}
return NULL;
}