1 // Copyright (c) 2006-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 "net/disk_cache/mem_backend_impl.h" 6 7 #include "base/logging.h" 8 #include "base/sys_info.h" 9 #include "net/base/net_errors.h" 10 #include "net/disk_cache/cache_util.h" 11 #include "net/disk_cache/mem_entry_impl.h" 12 13 using base::Time; 14 15 namespace { 16 17 const int kDefaultCacheSize = 10 * 1024 * 1024; 18 const int kCleanUpMargin = 1024 * 1024; 19 20 int LowWaterAdjust(int high_water) { 21 if (high_water < kCleanUpMargin) 22 return 0; 23 24 return high_water - kCleanUpMargin; 25 } 26 27 } // namespace 28 29 namespace disk_cache { 30 31 Backend* CreateInMemoryCacheBackend(int max_bytes) { 32 MemBackendImpl* cache = new MemBackendImpl(); 33 cache->SetMaxSize(max_bytes); 34 if (cache->Init()) 35 return cache; 36 37 delete cache; 38 LOG(ERROR) << "Unable to create cache"; 39 return NULL; 40 } 41 42 // ------------------------------------------------------------------------ 43 44 bool MemBackendImpl::Init() { 45 if (max_size_) 46 return true; 47 48 int64 total_memory = base::SysInfo::AmountOfPhysicalMemory(); 49 50 if (total_memory <= 0) { 51 max_size_ = kDefaultCacheSize; 52 return true; 53 } 54 55 // We want to use up to 2% of the computer's memory, with a limit of 50 MB, 56 // reached on systemd with more than 2.5 GB of RAM. 57 total_memory = total_memory * 2 / 100; 58 if (total_memory > kDefaultCacheSize * 5) 59 max_size_ = kDefaultCacheSize * 5; 60 else 61 max_size_ = static_cast<int32>(total_memory); 62 63 return true; 64 } 65 66 MemBackendImpl::~MemBackendImpl() { 67 EntryMap::iterator it = entries_.begin(); 68 while (it != entries_.end()) { 69 it->second->Doom(); 70 it = entries_.begin(); 71 } 72 DCHECK(!current_size_); 73 } 74 75 bool MemBackendImpl::SetMaxSize(int max_bytes) { 76 COMPILE_ASSERT(sizeof(max_bytes) == sizeof(max_size_), unsupported_int_model); 77 if (max_bytes < 0) 78 return false; 79 80 // Zero size means use the default. 81 if (!max_bytes) 82 return true; 83 84 max_size_ = max_bytes; 85 return true; 86 } 87 88 int32 MemBackendImpl::GetEntryCount() const { 89 return static_cast<int32>(entries_.size()); 90 } 91 92 bool MemBackendImpl::OpenEntry(const std::string& key, Entry** entry) { 93 EntryMap::iterator it = entries_.find(key); 94 if (it == entries_.end()) 95 return false; 96 97 it->second->Open(); 98 99 *entry = it->second; 100 return true; 101 } 102 103 int MemBackendImpl::OpenEntry(const std::string& key, Entry** entry, 104 CompletionCallback* callback) { 105 if (OpenEntry(key, entry)) 106 return net::OK; 107 108 return net::ERR_FAILED; 109 } 110 111 bool MemBackendImpl::CreateEntry(const std::string& key, Entry** entry) { 112 EntryMap::iterator it = entries_.find(key); 113 if (it != entries_.end()) 114 return false; 115 116 MemEntryImpl* cache_entry = new MemEntryImpl(this); 117 if (!cache_entry->CreateEntry(key)) { 118 delete entry; 119 return false; 120 } 121 122 rankings_.Insert(cache_entry); 123 entries_[key] = cache_entry; 124 125 *entry = cache_entry; 126 return true; 127 } 128 129 int MemBackendImpl::CreateEntry(const std::string& key, Entry** entry, 130 CompletionCallback* callback) { 131 if (CreateEntry(key, entry)) 132 return net::OK; 133 134 return net::ERR_FAILED; 135 } 136 137 bool MemBackendImpl::DoomEntry(const std::string& key) { 138 Entry* entry; 139 if (!OpenEntry(key, &entry)) 140 return false; 141 142 entry->Doom(); 143 entry->Close(); 144 return true; 145 } 146 147 int MemBackendImpl::DoomEntry(const std::string& key, 148 CompletionCallback* callback) { 149 if (DoomEntry(key)) 150 return net::OK; 151 152 return net::ERR_FAILED; 153 } 154 155 void MemBackendImpl::InternalDoomEntry(MemEntryImpl* entry) { 156 // Only parent entries can be passed into this method. 157 DCHECK(entry->type() == MemEntryImpl::kParentEntry); 158 159 rankings_.Remove(entry); 160 EntryMap::iterator it = entries_.find(entry->GetKey()); 161 if (it != entries_.end()) 162 entries_.erase(it); 163 else 164 NOTREACHED(); 165 166 entry->InternalDoom(); 167 } 168 169 bool MemBackendImpl::DoomAllEntries() { 170 TrimCache(true); 171 return true; 172 } 173 174 int MemBackendImpl::DoomAllEntries(CompletionCallback* callback) { 175 if (DoomAllEntries()) 176 return net::OK; 177 178 return net::ERR_FAILED; 179 } 180 181 bool MemBackendImpl::DoomEntriesBetween(const Time initial_time, 182 const Time end_time) { 183 if (end_time.is_null()) 184 return DoomEntriesSince(initial_time); 185 186 DCHECK(end_time >= initial_time); 187 188 MemEntryImpl* next = rankings_.GetNext(NULL); 189 190 // rankings_ is ordered by last used, this will descend through the cache 191 // and start dooming items before the end_time, and will stop once it reaches 192 // an item used before the initial time. 193 while (next) { 194 MemEntryImpl* node = next; 195 next = rankings_.GetNext(next); 196 197 if (node->GetLastUsed() < initial_time) 198 break; 199 200 if (node->GetLastUsed() < end_time) 201 node->Doom(); 202 } 203 204 return true; 205 } 206 207 int MemBackendImpl::DoomEntriesBetween(const base::Time initial_time, 208 const base::Time end_time, 209 CompletionCallback* callback) { 210 if (DoomEntriesBetween(initial_time, end_time)) 211 return net::OK; 212 213 return net::ERR_FAILED; 214 } 215 216 bool MemBackendImpl::DoomEntriesSince(const Time initial_time) { 217 for (;;) { 218 // Get the entry in the front. 219 Entry* entry = rankings_.GetNext(NULL); 220 221 // Break the loop when there are no more entries or the entry is too old. 222 if (!entry || entry->GetLastUsed() < initial_time) 223 return true; 224 entry->Doom(); 225 } 226 } 227 228 int MemBackendImpl::DoomEntriesSince(const base::Time initial_time, 229 CompletionCallback* callback) { 230 if (DoomEntriesSince(initial_time)) 231 return net::OK; 232 233 return net::ERR_FAILED; 234 } 235 236 bool MemBackendImpl::OpenNextEntry(void** iter, Entry** next_entry) { 237 MemEntryImpl* current = reinterpret_cast<MemEntryImpl*>(*iter); 238 MemEntryImpl* node = rankings_.GetNext(current); 239 // We should never return a child entry so iterate until we hit a parent 240 // entry. 241 while (node && node->type() != MemEntryImpl::kParentEntry) { 242 node = rankings_.GetNext(node); 243 } 244 *next_entry = node; 245 *iter = node; 246 247 if (node) 248 node->Open(); 249 250 return NULL != node; 251 } 252 253 int MemBackendImpl::OpenNextEntry(void** iter, Entry** next_entry, 254 CompletionCallback* callback) { 255 if (OpenNextEntry(iter, next_entry)) 256 return net::OK; 257 258 return net::ERR_FAILED; 259 } 260 261 void MemBackendImpl::EndEnumeration(void** iter) { 262 *iter = NULL; 263 } 264 265 void MemBackendImpl::TrimCache(bool empty) { 266 MemEntryImpl* next = rankings_.GetPrev(NULL); 267 268 DCHECK(next); 269 270 int target_size = empty ? 0 : LowWaterAdjust(max_size_); 271 while (current_size_ > target_size && next) { 272 MemEntryImpl* node = next; 273 next = rankings_.GetPrev(next); 274 if (!node->InUse() || empty) { 275 node->Doom(); 276 } 277 } 278 279 return; 280 } 281 282 void MemBackendImpl::AddStorageSize(int32 bytes) { 283 current_size_ += bytes; 284 DCHECK(current_size_ >= 0); 285 286 if (current_size_ > max_size_) 287 TrimCache(false); 288 } 289 290 void MemBackendImpl::SubstractStorageSize(int32 bytes) { 291 current_size_ -= bytes; 292 DCHECK(current_size_ >= 0); 293 } 294 295 void MemBackendImpl::ModifyStorageSize(int32 old_size, int32 new_size) { 296 if (old_size >= new_size) 297 SubstractStorageSize(old_size - new_size); 298 else 299 AddStorageSize(new_size - old_size); 300 } 301 302 void MemBackendImpl::UpdateRank(MemEntryImpl* node) { 303 rankings_.UpdateRank(node); 304 } 305 306 int MemBackendImpl::MaxFileSize() const { 307 return max_size_ / 8; 308 } 309 310 void MemBackendImpl::InsertIntoRankingList(MemEntryImpl* entry) { 311 rankings_.Insert(entry); 312 } 313 314 void MemBackendImpl::RemoveFromRankingList(MemEntryImpl* entry) { 315 rankings_.Remove(entry); 316 } 317 318 } // namespace disk_cache 319