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