blob: 0ba2d3fef1c590e919289056b3fb063f8fb452a6 [file] [log] [blame]
// Copyright (c) 2012 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 "net/disk_cache/memory/mem_backend_impl.h"
#include <algorithm>
#include <functional>
#include <utility>
#include "base/logging.h"
#include "base/sys_info.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/cache_util.h"
#include "net/disk_cache/memory/mem_entry_impl.h"
using base::Time;
namespace disk_cache {
namespace {
const int kDefaultInMemoryCacheSize = 10 * 1024 * 1024;
const int kDefaultEvictionSize = kDefaultInMemoryCacheSize / 10;
bool CheckLRUListOrder(const base::LinkedList<MemEntryImpl>& lru_list) {
// TODO(gavinp): Check MemBackendImpl::current_size_ here as well.
base::Time previous_last_use_time;
for (base::LinkNode<MemEntryImpl>* node = lru_list.head();
node != lru_list.end(); node = node->next()) {
if (node->value()->GetLastUsed() < previous_last_use_time)
return false;
previous_last_use_time = node->value()->GetLastUsed();
}
return true;
}
} // namespace
MemBackendImpl::MemBackendImpl(net::NetLog* net_log)
: max_size_(0), current_size_(0), net_log_(net_log), weak_factory_(this) {
}
MemBackendImpl::~MemBackendImpl() {
DCHECK(CheckLRUListOrder(lru_list_));
while (!entries_.empty())
entries_.begin()->second->Doom();
DCHECK(!current_size_);
}
// static
std::unique_ptr<Backend> MemBackendImpl::CreateBackend(int max_bytes,
net::NetLog* net_log) {
std::unique_ptr<MemBackendImpl> cache(new MemBackendImpl(net_log));
cache->SetMaxSize(max_bytes);
if (cache->Init())
return std::move(cache);
LOG(ERROR) << "Unable to create cache";
return nullptr;
}
bool MemBackendImpl::Init() {
if (max_size_)
return true;
int64_t total_memory = base::SysInfo::AmountOfPhysicalMemory();
if (total_memory <= 0) {
max_size_ = kDefaultInMemoryCacheSize;
return true;
}
// We want to use up to 2% of the computer's memory, with a limit of 50 MB,
// reached on system with more than 2.5 GB of RAM.
total_memory = total_memory * 2 / 100;
if (total_memory > kDefaultInMemoryCacheSize * 5)
max_size_ = kDefaultInMemoryCacheSize * 5;
else
max_size_ = static_cast<int32_t>(total_memory);
return true;
}
bool MemBackendImpl::SetMaxSize(int max_bytes) {
static_assert(sizeof(max_bytes) == sizeof(max_size_),
"unsupported int model");
if (max_bytes < 0)
return false;
// Zero size means use the default.
if (!max_bytes)
return true;
max_size_ = max_bytes;
return true;
}
int MemBackendImpl::MaxFileSize() const {
return max_size_ / 8;
}
void MemBackendImpl::OnEntryInserted(MemEntryImpl* entry) {
lru_list_.Append(entry);
}
void MemBackendImpl::OnEntryUpdated(MemEntryImpl* entry) {
DCHECK(CheckLRUListOrder(lru_list_));
// LinkedList<>::RemoveFromList() removes |entry| from |lru_list_|.
entry->RemoveFromList();
lru_list_.Append(entry);
}
void MemBackendImpl::OnEntryDoomed(MemEntryImpl* entry) {
DCHECK(CheckLRUListOrder(lru_list_));
if (entry->type() == MemEntryImpl::PARENT_ENTRY)
entries_.erase(entry->key());
// LinkedList<>::RemoveFromList() removes |entry| from |lru_list_|.
entry->RemoveFromList();
}
void MemBackendImpl::ModifyStorageSize(int32_t delta) {
current_size_ += delta;
if (delta > 0)
EvictIfNeeded();
}
net::CacheType MemBackendImpl::GetCacheType() const {
return net::MEMORY_CACHE;
}
int32_t MemBackendImpl::GetEntryCount() const {
return static_cast<int32_t>(entries_.size());
}
int MemBackendImpl::OpenEntry(const std::string& key,
Entry** entry,
const CompletionCallback& callback) {
EntryMap::iterator it = entries_.find(key);
if (it == entries_.end())
return net::ERR_FAILED;
it->second->Open();
*entry = it->second;
return net::OK;
}
int MemBackendImpl::CreateEntry(const std::string& key,
Entry** entry,
const CompletionCallback& callback) {
std::pair<EntryMap::iterator, bool> create_result =
entries_.insert(EntryMap::value_type(key, nullptr));
const bool did_insert = create_result.second;
if (!did_insert)
return net::ERR_FAILED;
MemEntryImpl* cache_entry = new MemEntryImpl(this, key, net_log_);
create_result.first->second = cache_entry;
*entry = cache_entry;
return net::OK;
}
int MemBackendImpl::DoomEntry(const std::string& key,
const CompletionCallback& callback) {
EntryMap::iterator it = entries_.find(key);
if (it == entries_.end())
return net::ERR_FAILED;
it->second->Doom();
return net::OK;
}
int MemBackendImpl::DoomAllEntries(const CompletionCallback& callback) {
return DoomEntriesBetween(Time(), Time(), callback);
}
int MemBackendImpl::DoomEntriesBetween(Time initial_time,
Time end_time,
const CompletionCallback& callback) {
if (end_time.is_null())
end_time = Time::Max();
DCHECK_GE(end_time, initial_time);
base::LinkNode<MemEntryImpl>* node = lru_list_.head();
while (node != lru_list_.end() && node->value()->GetLastUsed() < initial_time)
node = node->next();
while (node != lru_list_.end() && node->value()->GetLastUsed() < end_time) {
MemEntryImpl* to_doom = node->value();
node = node->next();
to_doom->Doom();
}
return net::OK;
}
int MemBackendImpl::DoomEntriesSince(Time initial_time,
const CompletionCallback& callback) {
return DoomEntriesBetween(initial_time, Time::Max(), callback);
}
int MemBackendImpl::CalculateSizeOfAllEntries(
const CompletionCallback& callback) {
return current_size_;
}
class MemBackendImpl::MemIterator final : public Backend::Iterator {
public:
explicit MemIterator(base::WeakPtr<MemBackendImpl> backend)
: backend_(backend), current_(nullptr) {}
int OpenNextEntry(Entry** next_entry,
const CompletionCallback& callback) override {
if (!backend_)
return net::ERR_FAILED;
// Iterate using |lru_list_|, from most recently used to least recently
// used, for compatibility with the unit tests that assume this behaviour.
// Consider the last element if we are beginning an iteration, otherwise
// progressively move earlier in the LRU list.
current_ = current_ ? current_->previous() : backend_->lru_list_.tail();
// We should never return a child entry so iterate until we hit a parent
// entry.
while (current_ != backend_->lru_list_.end() &&
current_->value()->type() != MemEntryImpl::PARENT_ENTRY) {
current_ = current_->previous();
}
if (current_ == backend_->lru_list_.end()) {
*next_entry = nullptr;
return net::ERR_FAILED;
}
current_->value()->Open();
*next_entry = current_->value();
return net::OK;
}
private:
base::WeakPtr<MemBackendImpl> backend_;
base::LinkNode<MemEntryImpl>* current_;
};
std::unique_ptr<Backend::Iterator> MemBackendImpl::CreateIterator() {
return std::unique_ptr<Backend::Iterator>(
new MemIterator(weak_factory_.GetWeakPtr()));
}
void MemBackendImpl::OnExternalCacheHit(const std::string& key) {
EntryMap::iterator it = entries_.find(key);
if (it != entries_.end())
it->second->UpdateStateOnUse(MemEntryImpl::ENTRY_WAS_NOT_MODIFIED);
}
void MemBackendImpl::EvictIfNeeded() {
if (current_size_ <= max_size_)
return;
int target_size = std::max(0, max_size_ - kDefaultEvictionSize);
base::LinkNode<MemEntryImpl>* entry = lru_list_.head();
while (current_size_ > target_size && entry != lru_list_.end()) {
MemEntryImpl* to_doom = entry->value();
entry = entry->next();
if (!to_doom->InUse())
to_doom->Doom();
}
}
} // namespace disk_cache