1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/renderer_host/web_cache_manager.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/compiler_specific.h"
10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/singleton.h"
11731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/metrics/histogram.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/sys_info.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/time.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_process.h"
153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/prefs/pref_service.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_constants.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/pref_names.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/render_messages.h"
19dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_process_host.h"
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_service.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing base::Time;
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing base::TimeDelta;
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing WebKit::WebCache;
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const unsigned int kReviseAllocationDelayMS = 200 /* milliseconds */;
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The default size limit of the in-memory cache is 8 MB
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kDefaultMemoryCacheSize = 8 * 1024 * 1024;
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint GetDefaultCacheSize() {
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Start off with a modest default
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int default_cache_size = kDefaultMemoryCacheSize;
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Check how much physical memory the OS has
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int mem_size_mb = base::SysInfo::AmountOfPhysicalMemoryMB();
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (mem_size_mb >= 1000)  // If we have a GB of memory, set a larger default.
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default_cache_size *= 4;
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else if (mem_size_mb >= 512)  // With 512 MB, set a slightly larger default.
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default_cache_size *= 2;
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_MEMORY_MB("Cache.MaxCacheSizeMB",
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          default_cache_size / 1024 / 1024);
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return default_cache_size;
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // anonymous namespace
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::RegisterPrefs(PrefService* prefs) {
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  prefs->RegisterIntegerPref(prefs::kMemoryCacheSize, GetDefaultCacheSize());
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochWebCacheManager* WebCacheManager::GetInstance() {
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return Singleton<WebCacheManager>::get();
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochWebCacheManager::WebCacheManager()
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : global_size_limit_(GetDefaultGlobalSizeLimit()),
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ALLOW_THIS_IN_INITIALIZER_LIST(revise_allocation_factory_(this)) {
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochWebCacheManager::~WebCacheManager() {
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::Add(int renderer_id) {
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(inactive_renderers_.count(renderer_id) == 0);
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // It is tempting to make the following DCHECK here, but it fails when a new
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // tab is created as we observe activity from that tab because the
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // RenderProcessHost is recreated and adds itself.
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //   DCHECK(active_renderers_.count(renderer_id) == 0);
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // However, there doesn't seem to be much harm in receiving the calls in this
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // order.
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_renderers_.insert(renderer_id);
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  RendererInfo* stats = &(stats_[renderer_id]);
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  memset(stats, 0, sizeof(*stats));
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  stats->access = Time::Now();
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Revise our allocation strategy to account for this new renderer.
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ReviseAllocationStrategyLater();
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::Remove(int renderer_id) {
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Erase all knowledge of this renderer
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_renderers_.erase(renderer_id);
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  inactive_renderers_.erase(renderer_id);
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  stats_.erase(renderer_id);
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Reallocate the resources used by this renderer
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ReviseAllocationStrategyLater();
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::ObserveActivity(int renderer_id) {
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StatsMap::iterator item = stats_.find(renderer_id);
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (item == stats_.end())
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;  // We might see stats for a renderer that has been destroyed.
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Record activity.
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_renderers_.insert(renderer_id);
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  item->second.access = Time::Now();
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::set<int>::iterator elmt = inactive_renderers_.find(renderer_id);
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (elmt != inactive_renderers_.end()) {
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    inactive_renderers_.erase(elmt);
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // A renderer that was inactive, just became active.  We should make sure
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // it is given a fair cache allocation, but we defer this for a bit in
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // order to make this function call cheap.
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ReviseAllocationStrategyLater();
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::ObserveStats(int renderer_id,
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   const WebCache::UsageStats& stats) {
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StatsMap::iterator entry = stats_.find(renderer_id);
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (entry == stats_.end())
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;  // We might see stats for a renderer that has been destroyed.
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Record the updated stats.
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->second.capacity = stats.capacity;
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->second.deadSize = stats.deadSize;
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->second.liveSize = stats.liveSize;
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->second.maxDeadCapacity = stats.maxDeadCapacity;
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->second.minDeadCapacity = stats.minDeadCapacity;
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // trigger notification
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  WebCache::UsageStats stats_details(stats);
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // &stats_details is only valid during the notification.
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // See notification_types.h.
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NotificationService::current()->Notify(
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NotificationType::WEB_CACHE_STATS_OBSERVED,
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Source<RenderProcessHost>(RenderProcessHost::FromID(renderer_id)),
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Details<WebCache::UsageStats>(&stats_details));
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::SetGlobalSizeLimit(size_t bytes) {
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  global_size_limit_ = bytes;
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ReviseAllocationStrategyLater();
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
150731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid WebCacheManager::ClearCache() {
151731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Tell each renderer process to clear the cache.
152731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ClearRendederCache(active_renderers_);
153731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ClearRendederCache(inactive_renderers_);
154731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
155731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochsize_t WebCacheManager::GetDefaultGlobalSizeLimit() {
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PrefService* perf_service = g_browser_process->local_state();
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (perf_service)
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return perf_service->GetInteger(prefs::kMemoryCacheSize);
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return GetDefaultCacheSize();
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::GatherStats(const std::set<int>& renderers,
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  WebCache::UsageStats* stats) {
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(stats);
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  memset(stats, 0, sizeof(WebCache::UsageStats));
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::set<int>::const_iterator iter = renderers.begin();
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (iter != renderers.end()) {
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StatsMap::iterator elmt = stats_.find(*iter);
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (elmt != stats_.end()) {
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      stats->minDeadCapacity += elmt->second.minDeadCapacity;
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      stats->maxDeadCapacity += elmt->second.maxDeadCapacity;
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      stats->capacity += elmt->second.capacity;
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      stats->liveSize += elmt->second.liveSize;
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      stats->deadSize += elmt->second.deadSize;
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ++iter;
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochsize_t WebCacheManager::GetSize(AllocationTactic tactic,
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                const WebCache::UsageStats& stats) {
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (tactic) {
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  case DIVIDE_EVENLY:
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We aren't going to reserve any space for existing objects.
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  case KEEP_CURRENT_WITH_HEADROOM:
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We need enough space for our current objects, plus some headroom.
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 3 * GetSize(KEEP_CURRENT, stats) / 2;
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  case KEEP_CURRENT:
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We need enough space to keep our current objects.
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return stats.liveSize + stats.deadSize;
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  case KEEP_LIVE_WITH_HEADROOM:
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We need enough space to keep out live resources, plus some headroom.
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 3 * GetSize(KEEP_LIVE, stats) / 2;
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  case KEEP_LIVE:
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We need enough space to keep our live resources.
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return stats.liveSize;
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  default:
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << "Unknown cache allocation tactic";
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool WebCacheManager::AttemptTactic(
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AllocationTactic active_tactic,
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const WebCache::UsageStats& active_stats,
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AllocationTactic inactive_tactic,
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const WebCache::UsageStats& inactive_stats,
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AllocationStrategy* strategy) {
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(strategy);
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_t active_size = GetSize(active_tactic, active_stats);
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_t inactive_size = GetSize(inactive_tactic, inactive_stats);
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Give up if we don't have enough space to use this tactic.
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (global_size_limit_ < active_size + inactive_size)
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Compute the unreserved space available.
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_t total_extra = global_size_limit_ - (active_size + inactive_size);
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The plan for the extra space is to divide it evenly amoung the active
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // renderers.
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_t shares = active_renderers_.size();
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The inactive renderers get one share of the extra memory to be divided
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // among themselves.
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_t inactive_extra = 0;
235dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (!inactive_renderers_.empty()) {
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ++shares;
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    inactive_extra = total_extra / shares;
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The remaining memory is allocated to the active renderers.
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_t active_extra = total_extra - inactive_extra;
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Actually compute the allocations for each renderer.
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AddToStrategy(active_renderers_, active_tactic, active_extra, strategy);
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AddToStrategy(inactive_renderers_, inactive_tactic, inactive_extra, strategy);
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We succeeded in computing an allocation strategy.
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
251513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid WebCacheManager::AddToStrategy(const std::set<int>& renderers,
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    AllocationTactic tactic,
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    size_t extra_bytes_to_allocate,
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    AllocationStrategy* strategy) {
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(strategy);
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Nothing to do if there are no renderers.  It is common for there to be no
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // inactive renderers if there is a single active tab.
259dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (renderers.empty())
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Divide the extra memory evenly among the renderers.
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_t extra_each = extra_bytes_to_allocate / renderers.size();
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::set<int>::const_iterator iter = renderers.begin();
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (iter != renderers.end()) {
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    size_t cache_size = extra_each;
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Add in the space required to implement |tactic|.
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StatsMap::iterator elmt = stats_.find(*iter);
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (elmt != stats_.end())
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      cache_size += GetSize(tactic, elmt->second);
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Record the allocation in our strategy.
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    strategy->push_back(Allocation(*iter, cache_size));
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ++iter;
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::EnactStrategy(const AllocationStrategy& strategy) {
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Inform each render process of its cache allocation.
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AllocationStrategy::const_iterator allocation = strategy.begin();
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (allocation != strategy.end()) {
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    RenderProcessHost* host = RenderProcessHost::FromID(allocation->first);
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (host) {
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // This is the capacity this renderer has been allocated.
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      size_t capacity = allocation->second;
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // We don't reserve any space for dead objects in the cache.  Instead, we
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // prefer to keep live objects around.  There is probably some performance
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // tuning to be done here.
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      size_t min_dead_capacity = 0;
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // We allow the dead objects to consume all of the cache, if the renderer
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // so desires.  If we wanted this memory, we would have set the total
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // capacity lower.
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      size_t max_dead_capacity = capacity;
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      host->Send(new ViewMsg_SetCacheCapacities(min_dead_capacity,
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                max_dead_capacity,
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                capacity));
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ++allocation;
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
307513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid WebCacheManager::ClearRendederCache(const std::set<int>& renderers) {
308731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::set<int>::const_iterator iter = renderers.begin();
309731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  for (; iter != renderers.end(); ++iter) {
310731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    RenderProcessHost* host = RenderProcessHost::FromID(*iter);
311731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (host)
312731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      host->Send(new ViewMsg_ClearCache());
313731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
314731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
315731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::ReviseAllocationStrategy() {
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(stats_.size() <=
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      active_renderers_.size() + inactive_renderers_.size());
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Check if renderers have gone inactive.
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FindInactiveRenderers();
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Gather statistics
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  WebCache::UsageStats active;
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  WebCache::UsageStats inactive;
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GatherStats(active_renderers_, &active);
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GatherStats(inactive_renderers_, &inactive);
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_COUNTS_100("Cache.ActiveTabs", active_renderers_.size());
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_COUNTS_100("Cache.InactiveTabs", inactive_renderers_.size());
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveCapacityMB",
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          active.capacity / 1024 / 1024);
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveDeadSizeMB",
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          active.deadSize / 1024 / 1024);
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveLiveSizeMB",
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          active.liveSize / 1024 / 1024);
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveCapacityMB",
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          inactive.capacity / 1024 / 1024);
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveDeadSizeMB",
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          inactive.deadSize / 1024 / 1024);
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveLiveSizeMB",
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          inactive.liveSize / 1024 / 1024);
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Compute an allocation strategy.
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We attempt various tactics in order of preference.  Our first preference
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // is not to evict any objects.  If we don't have enough resources, we'll
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // first try to evict dead data only.  If that fails, we'll just divide the
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // resources we have evenly.
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We always try to give the active renderers some head room in their
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // allocations so they can take memory away from an inactive renderer with
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // a large cache allocation.
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Notice the early exit will prevent attempting less desirable tactics once
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // we've found a workable strategy.
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AllocationStrategy strategy;
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (  // Ideally, we'd like to give the active renderers some headroom and
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // keep all our current objects.
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active,
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    KEEP_CURRENT, inactive, &strategy) ||
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // If we can't have that, then we first try to evict the dead objects in
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // the caches of inactive renderers.
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active,
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    KEEP_LIVE, inactive, &strategy) ||
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Next, we try to keep the live objects in the active renders (with some
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // room for new objects) and give whatever is left to the inactive
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // renderers.
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      AttemptTactic(KEEP_LIVE_WITH_HEADROOM, active,
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    DIVIDE_EVENLY, inactive, &strategy) ||
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // If we've gotten this far, then we are very tight on memory.  Let's try
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // to at least keep around the live objects for the active renderers.
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      AttemptTactic(KEEP_LIVE, active, DIVIDE_EVENLY, inactive, &strategy) ||
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // We're basically out of memory.  The best we can do is just divide up
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // what we have and soldier on.
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      AttemptTactic(DIVIDE_EVENLY, active, DIVIDE_EVENLY, inactive,
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    &strategy)) {
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Having found a workable strategy, we enact it.
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    EnactStrategy(strategy);
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // DIVIDE_EVENLY / DIVIDE_EVENLY should always succeed.
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << "Unable to find a cache allocation";
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::ReviseAllocationStrategyLater() {
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Ask to be called back in a few milliseconds to actually recompute our
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // allocation.
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MessageLoop::current()->PostDelayedTask(FROM_HERE,
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      revise_allocation_factory_.NewRunnableMethod(
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          &WebCacheManager::ReviseAllocationStrategy),
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kReviseAllocationDelayMS);
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid WebCacheManager::FindInactiveRenderers() {
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::set<int>::const_iterator iter = active_renderers_.begin();
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (iter != active_renderers_.end()) {
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StatsMap::iterator elmt = stats_.find(*iter);
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK(elmt != stats_.end());
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TimeDelta idle = Time::Now() - elmt->second.access;
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (idle >= TimeDelta::FromMinutes(kRendererInactiveThresholdMinutes)) {
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Moved to inactive status.  This invalidates our iterator.
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      inactive_renderers_.insert(*iter);
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      active_renderers_.erase(*iter);
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      iter = active_renderers_.begin();
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ++iter;
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
411