Import chromium-64.0.3282.119

This commit is contained in:
klzgrad
2018-01-28 13:32:06 -05:00
commit 84ba45923e
19589 changed files with 4029211 additions and 0 deletions

View File

@@ -0,0 +1,341 @@
// 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 <memory>
#include <utility>
#include "base/logging.h"
#include "base/sys_info.h"
#include "base/task_scheduler/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/memory_usage_estimator.h"
#include "base/trace_event/process_memory_dump.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_EQ(0, current_size_);
if (!post_cleanup_callback_.is_null())
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, std::move(post_cleanup_callback_));
}
// static
std::unique_ptr<MemBackendImpl> MemBackendImpl::CreateBackend(
int max_bytes,
net::NetLog* net_log) {
std::unique_ptr<MemBackendImpl> cache(
std::make_unique<MemBackendImpl>(net_log));
cache->SetMaxSize(max_bytes);
if (cache->Init())
return 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();
}
bool MemBackendImpl::HasExceededStorageSize() const {
return current_size_ > max_size_;
}
void MemBackendImpl::SetPostCleanupCallback(base::OnceClosure cb) {
DCHECK(post_cleanup_callback_.is_null());
post_cleanup_callback_ = std::move(cb);
}
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_;
}
int MemBackendImpl::CalculateSizeOfEntriesBetween(
base::Time initial_time,
base::Time end_time,
const CompletionCallback& callback) {
if (end_time.is_null())
end_time = Time::Max();
DCHECK_GE(end_time, initial_time);
int size = 0;
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* entry = node->value();
size += entry->GetStorageSize();
node = node->next();
}
return size;
}
class MemBackendImpl::MemIterator final : public Backend::Iterator {
public:
explicit MemIterator(base::WeakPtr<MemBackendImpl> backend)
: backend_(backend) {}
int OpenNextEntry(Entry** next_entry,
const CompletionCallback& callback) override {
if (!backend_)
return net::ERR_FAILED;
if (!backend_keys_) {
backend_keys_ = std::make_unique<Strings>(backend_->entries_.size());
for (const auto& iter : backend_->entries_)
backend_keys_->push_back(iter.first);
current_ = backend_keys_->begin();
} else {
current_++;
}
while (true) {
if (current_ == backend_keys_->end()) {
*next_entry = nullptr;
backend_keys_.reset();
return net::ERR_FAILED;
}
const auto& entry_iter = backend_->entries_.find(*current_);
if (entry_iter == backend_->entries_.end()) {
// The key is no longer in the cache, move on to the next key.
current_++;
continue;
}
entry_iter->second->Open();
*next_entry = entry_iter->second;
return net::OK;
}
}
private:
using Strings = std::vector<std::string>;
base::WeakPtr<MemBackendImpl> backend_;
std::unique_ptr<Strings> backend_keys_;
Strings::iterator 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);
}
size_t MemBackendImpl::DumpMemoryStats(
base::trace_event::ProcessMemoryDump* pmd,
const std::string& parent_absolute_name) const {
base::trace_event::MemoryAllocatorDump* dump =
pmd->CreateAllocatorDump(parent_absolute_name + "/memory_backend");
// Entries in lru_list_ will be counted by EMU but not in entries_ since
// they're pointers.
size_t size = base::trace_event::EstimateMemoryUsage(lru_list_) +
base::trace_event::EstimateMemoryUsage(entries_);
dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes, size);
dump->AddScalar("mem_backend_size",
base::trace_event::MemoryAllocatorDump::kUnitsBytes,
current_size_);
dump->AddScalar("mem_backend_max_size",
base::trace_event::MemoryAllocatorDump::kUnitsBytes,
max_size_);
return size;
}
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

View File

@@ -0,0 +1,142 @@
// 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.
// See net/disk_cache/disk_cache.h for the public interface of the cache.
#ifndef NET_DISK_CACHE_MEMORY_MEM_BACKEND_IMPL_H_
#define NET_DISK_CACHE_MEMORY_MEM_BACKEND_IMPL_H_
#include <stdint.h>
#include <string>
#include <unordered_map>
#include "base/callback_forward.h"
#include "base/compiler_specific.h"
#include "base/containers/linked_list.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_split.h"
#include "base/time/time.h"
#include "net/base/net_export.h"
#include "net/disk_cache/disk_cache.h"
#include "net/disk_cache/memory/mem_entry_impl.h"
namespace net {
class NetLog;
} // namespace net
namespace disk_cache {
// This class implements the Backend interface. An object of this class handles
// the operations of the cache without writing to disk.
class NET_EXPORT_PRIVATE MemBackendImpl final : public Backend {
public:
explicit MemBackendImpl(net::NetLog* net_log);
~MemBackendImpl() override;
// Returns an instance of a Backend implemented only in memory. The returned
// object should be deleted when not needed anymore. max_bytes is the maximum
// size the cache can grow to. If zero is passed in as max_bytes, the cache
// will determine the value to use based on the available memory. The returned
// pointer can be NULL if a fatal error is found.
static std::unique_ptr<MemBackendImpl> CreateBackend(int max_bytes,
net::NetLog* net_log);
// Performs general initialization for this current instance of the cache.
bool Init();
// Sets the maximum size for the total amount of data stored by this instance.
bool SetMaxSize(int max_bytes);
// Returns the maximum size for a file to reside on the cache.
int MaxFileSize() const;
// These next methods (before the implementation of the Backend interface) are
// called by MemEntryImpl to update the state of the backend during the entry
// lifecycle.
// Signals that new entry has been created, and should be placed in
// |lru_list_| so that it is eligable for eviction.
void OnEntryInserted(MemEntryImpl* entry);
// Signals that an entry has been updated, and thus should be moved to the end
// of |lru_list_|.
void OnEntryUpdated(MemEntryImpl* entry);
// Signals that an entry has been doomed, and so it should be removed from the
// list of active entries as appropriate, as well as removed from the
// |lru_list_|.
void OnEntryDoomed(MemEntryImpl* entry);
// Adjust the current size of this backend by |delta|. This is used to
// determine if eviction is neccessary and when eviction is finished.
void ModifyStorageSize(int32_t delta);
// Returns true if the cache's size is greater than the maximum allowed
// size.
bool HasExceededStorageSize() const;
// Sets a callback to be posted after we are destroyed. Should be called at
// most once.
void SetPostCleanupCallback(base::OnceClosure cb);
// Backend interface.
net::CacheType GetCacheType() const override;
int32_t GetEntryCount() const override;
int OpenEntry(const std::string& key,
Entry** entry,
const CompletionCallback& callback) override;
int CreateEntry(const std::string& key,
Entry** entry,
const CompletionCallback& callback) override;
int DoomEntry(const std::string& key,
const CompletionCallback& callback) override;
int DoomAllEntries(const CompletionCallback& callback) override;
int DoomEntriesBetween(base::Time initial_time,
base::Time end_time,
const CompletionCallback& callback) override;
int DoomEntriesSince(base::Time initial_time,
const CompletionCallback& callback) override;
int CalculateSizeOfAllEntries(const CompletionCallback& callback) override;
int CalculateSizeOfEntriesBetween(
base::Time initial_time,
base::Time end_time,
const CompletionCallback& callback) override;
std::unique_ptr<Iterator> CreateIterator() override;
void GetStats(base::StringPairs* stats) override {}
void OnExternalCacheHit(const std::string& key) override;
size_t DumpMemoryStats(
base::trace_event::ProcessMemoryDump* pmd,
const std::string& parent_absolute_name) const override;
private:
class MemIterator;
friend class MemIterator;
using EntryMap = std::unordered_map<std::string, MemEntryImpl*>;
// Deletes entries from the cache until the current size is below the limit.
void EvictIfNeeded();
EntryMap entries_;
// Stored in increasing order of last use time, from least recently used to
// most recently used.
base::LinkedList<MemEntryImpl> lru_list_;
int32_t max_size_; // Maximum data size for this instance.
int32_t current_size_;
net::NetLog* net_log_;
base::OnceClosure post_cleanup_callback_;
base::WeakPtrFactory<MemBackendImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MemBackendImpl);
};
} // namespace disk_cache
#endif // NET_DISK_CACHE_MEMORY_MEM_BACKEND_IMPL_H_

View File

@@ -0,0 +1,624 @@
// 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_entry_impl.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/memory/mem_backend_impl.h"
#include "net/disk_cache/net_log_parameters.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_source_type.h"
using base::Time;
namespace disk_cache {
namespace {
const int kSparseData = 1;
// Maximum size of a sparse entry is 2 to the power of this number.
const int kMaxSparseEntryBits = 12;
// Sparse entry has maximum size of 4KB.
const int kMaxSparseEntrySize = 1 << kMaxSparseEntryBits;
// This enum is used for histograms, so only append to the end.
enum WriteResult {
WRITE_RESULT_SUCCESS = 0,
WRITE_RESULT_INVALID_ARGUMENT = 1,
WRITE_RESULT_OVER_MAX_ENTRY_SIZE = 2,
WRITE_RESULT_EXCEEDED_CACHE_STORAGE_SIZE = 3,
WRITE_RESULT_MAX = 4,
};
void RecordWriteResult(WriteResult result) {
UMA_HISTOGRAM_ENUMERATION("MemCache.WriteResult", result, WRITE_RESULT_MAX);
}
// Convert global offset to child index.
int ToChildIndex(int64_t offset) {
return static_cast<int>(offset >> kMaxSparseEntryBits);
}
// Convert global offset to offset in child entry.
int ToChildOffset(int64_t offset) {
return static_cast<int>(offset & (kMaxSparseEntrySize - 1));
}
// Returns a name for a child entry given the base_name of the parent and the
// child_id. This name is only used for logging purposes.
// If the entry is called entry_name, child entries will be named something
// like Range_entry_name:YYY where YYY is the number of the particular child.
std::string GenerateChildName(const std::string& base_name, int child_id) {
return base::StringPrintf("Range_%s:%i", base_name.c_str(), child_id);
}
// Returns NetLog parameters for the creation of a MemEntryImpl. A separate
// function is needed because child entries don't store their key().
std::unique_ptr<base::Value> NetLogEntryCreationCallback(
const MemEntryImpl* entry,
net::NetLogCaptureMode /* capture_mode */) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
std::string key;
switch (entry->type()) {
case MemEntryImpl::PARENT_ENTRY:
key = entry->key();
break;
case MemEntryImpl::CHILD_ENTRY:
key = GenerateChildName(entry->parent()->key(), entry->child_id());
break;
}
dict->SetString("key", key);
dict->SetBoolean("created", true);
return std::move(dict);
}
} // namespace
MemEntryImpl::MemEntryImpl(MemBackendImpl* backend,
const std::string& key,
net::NetLog* net_log)
: MemEntryImpl(backend,
key,
0, // child_id
nullptr, // parent
net_log) {
Open();
// Just creating the entry (without any data) could cause the storage to
// grow beyond capacity, but we allow such infractions.
backend_->ModifyStorageSize(GetStorageSize());
}
MemEntryImpl::MemEntryImpl(MemBackendImpl* backend,
int child_id,
MemEntryImpl* parent,
net::NetLog* net_log)
: MemEntryImpl(backend,
std::string(), // key
child_id,
parent,
net_log) {
(*parent_->children_)[child_id] = this;
}
void MemEntryImpl::Open() {
// Only a parent entry can be opened.
DCHECK_EQ(PARENT_ENTRY, type());
++ref_count_;
DCHECK_GE(ref_count_, 1);
DCHECK(!doomed_);
}
bool MemEntryImpl::InUse() const {
if (type() == CHILD_ENTRY)
return parent_->InUse();
return ref_count_ > 0;
}
int MemEntryImpl::GetStorageSize() const {
int storage_size = static_cast<int32_t>(key_.size());
for (const auto& i : data_)
storage_size += i.size();
return storage_size;
}
void MemEntryImpl::UpdateStateOnUse(EntryModified modified_enum) {
if (!doomed_)
backend_->OnEntryUpdated(this);
last_used_ = Time::Now();
if (modified_enum == ENTRY_WAS_MODIFIED)
last_modified_ = last_used_;
}
void MemEntryImpl::Doom() {
if (!doomed_) {
doomed_ = true;
backend_->OnEntryDoomed(this);
net_log_.AddEvent(net::NetLogEventType::ENTRY_DOOM);
}
if (!ref_count_)
delete this;
}
void MemEntryImpl::Close() {
DCHECK_EQ(PARENT_ENTRY, type());
--ref_count_;
DCHECK_GE(ref_count_, 0);
if (!ref_count_ && doomed_)
delete this;
}
std::string MemEntryImpl::GetKey() const {
// A child entry doesn't have key so this method should not be called.
DCHECK_EQ(PARENT_ENTRY, type());
return key_;
}
Time MemEntryImpl::GetLastUsed() const {
return last_used_;
}
Time MemEntryImpl::GetLastModified() const {
return last_modified_;
}
int32_t MemEntryImpl::GetDataSize(int index) const {
if (index < 0 || index >= kNumStreams)
return 0;
return data_[index].size();
}
int MemEntryImpl::ReadData(int index, int offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback) {
if (net_log_.IsCapturing()) {
net_log_.BeginEvent(
net::NetLogEventType::ENTRY_READ_DATA,
CreateNetLogReadWriteDataCallback(index, offset, buf_len, false));
}
int result = InternalReadData(index, offset, buf, buf_len);
if (net_log_.IsCapturing()) {
net_log_.EndEvent(net::NetLogEventType::ENTRY_READ_DATA,
CreateNetLogReadWriteCompleteCallback(result));
}
return result;
}
int MemEntryImpl::WriteData(int index, int offset, IOBuffer* buf, int buf_len,
const CompletionCallback& callback, bool truncate) {
if (net_log_.IsCapturing()) {
net_log_.BeginEvent(
net::NetLogEventType::ENTRY_WRITE_DATA,
CreateNetLogReadWriteDataCallback(index, offset, buf_len, truncate));
}
int result = InternalWriteData(index, offset, buf, buf_len, truncate);
if (net_log_.IsCapturing()) {
net_log_.EndEvent(net::NetLogEventType::ENTRY_WRITE_DATA,
CreateNetLogReadWriteCompleteCallback(result));
}
return result;
}
int MemEntryImpl::ReadSparseData(int64_t offset,
IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
if (net_log_.IsCapturing()) {
net_log_.BeginEvent(net::NetLogEventType::SPARSE_READ,
CreateNetLogSparseOperationCallback(offset, buf_len));
}
int result = InternalReadSparseData(offset, buf, buf_len);
if (net_log_.IsCapturing())
net_log_.EndEvent(net::NetLogEventType::SPARSE_READ);
return result;
}
int MemEntryImpl::WriteSparseData(int64_t offset,
IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) {
if (net_log_.IsCapturing()) {
net_log_.BeginEvent(net::NetLogEventType::SPARSE_WRITE,
CreateNetLogSparseOperationCallback(offset, buf_len));
}
int result = InternalWriteSparseData(offset, buf, buf_len);
if (net_log_.IsCapturing())
net_log_.EndEvent(net::NetLogEventType::SPARSE_WRITE);
return result;
}
int MemEntryImpl::GetAvailableRange(int64_t offset,
int len,
int64_t* start,
const CompletionCallback& callback) {
if (net_log_.IsCapturing()) {
net_log_.BeginEvent(net::NetLogEventType::SPARSE_GET_RANGE,
CreateNetLogSparseOperationCallback(offset, len));
}
int result = InternalGetAvailableRange(offset, len, start);
if (net_log_.IsCapturing()) {
net_log_.EndEvent(
net::NetLogEventType::SPARSE_GET_RANGE,
CreateNetLogGetAvailableRangeResultCallback(*start, result));
}
return result;
}
bool MemEntryImpl::CouldBeSparse() const {
DCHECK_EQ(PARENT_ENTRY, type());
return (children_.get() != nullptr);
}
int MemEntryImpl::ReadyForSparseIO(const CompletionCallback& callback) {
return net::OK;
}
size_t MemEntryImpl::EstimateMemoryUsage() const {
// Subtlety: the entries in children_ are not double counted, as the entry
// pointers won't be followed by EstimateMemoryUsage.
return base::trace_event::EstimateMemoryUsage(data_) +
base::trace_event::EstimateMemoryUsage(key_) +
base::trace_event::EstimateMemoryUsage(children_);
}
// ------------------------------------------------------------------------
MemEntryImpl::MemEntryImpl(MemBackendImpl* backend,
const ::std::string& key,
int child_id,
MemEntryImpl* parent,
net::NetLog* net_log)
: key_(key),
ref_count_(0),
child_id_(child_id),
child_first_pos_(0),
parent_(parent),
last_modified_(Time::Now()),
last_used_(last_modified_),
backend_(backend),
doomed_(false) {
backend_->OnEntryInserted(this);
net_log_ = net::NetLogWithSource::Make(
net_log, net::NetLogSourceType::MEMORY_CACHE_ENTRY);
net_log_.BeginEvent(net::NetLogEventType::DISK_CACHE_MEM_ENTRY_IMPL,
base::Bind(&NetLogEntryCreationCallback, this));
}
MemEntryImpl::~MemEntryImpl() {
backend_->ModifyStorageSize(-GetStorageSize());
if (type() == PARENT_ENTRY) {
if (children_) {
EntryMap children;
children_->swap(children);
for (auto& it : children) {
// Since |this| is stored in the map, it should be guarded against
// double dooming, which will result in double destruction.
if (it.second != this)
it.second->Doom();
}
}
} else {
parent_->children_->erase(child_id_);
}
net_log_.EndEvent(net::NetLogEventType::DISK_CACHE_MEM_ENTRY_IMPL);
}
int MemEntryImpl::InternalReadData(int index, int offset, IOBuffer* buf,
int buf_len) {
DCHECK(type() == PARENT_ENTRY || index == kSparseData);
if (index < 0 || index >= kNumStreams || buf_len < 0)
return net::ERR_INVALID_ARGUMENT;
int entry_size = data_[index].size();
if (offset >= entry_size || offset < 0 || !buf_len)
return 0;
if (offset + buf_len > entry_size)
buf_len = entry_size - offset;
UpdateStateOnUse(ENTRY_WAS_NOT_MODIFIED);
std::copy(data_[index].begin() + offset,
data_[index].begin() + offset + buf_len, buf->data());
return buf_len;
}
int MemEntryImpl::InternalWriteData(int index, int offset, IOBuffer* buf,
int buf_len, bool truncate) {
DCHECK(type() == PARENT_ENTRY || index == kSparseData);
if (index < 0 || index >= kNumStreams) {
RecordWriteResult(WRITE_RESULT_INVALID_ARGUMENT);
return net::ERR_INVALID_ARGUMENT;
}
if (offset < 0 || buf_len < 0) {
RecordWriteResult(WRITE_RESULT_INVALID_ARGUMENT);
return net::ERR_INVALID_ARGUMENT;
}
int max_file_size = backend_->MaxFileSize();
// offset of buf_len could be negative numbers.
if (offset > max_file_size || buf_len > max_file_size ||
offset + buf_len > max_file_size) {
RecordWriteResult(WRITE_RESULT_OVER_MAX_ENTRY_SIZE);
return net::ERR_FAILED;
}
int old_data_size = data_[index].size();
if (truncate || old_data_size < offset + buf_len) {
int delta = offset + buf_len - old_data_size;
backend_->ModifyStorageSize(delta);
if (backend_->HasExceededStorageSize()) {
backend_->ModifyStorageSize(-delta);
RecordWriteResult(WRITE_RESULT_EXCEEDED_CACHE_STORAGE_SIZE);
return net::ERR_INSUFFICIENT_RESOURCES;
}
data_[index].resize(offset + buf_len);
// Zero fill any hole.
if (old_data_size < offset) {
std::fill(data_[index].begin() + old_data_size,
data_[index].begin() + offset, 0);
}
}
UpdateStateOnUse(ENTRY_WAS_MODIFIED);
RecordWriteResult(WRITE_RESULT_SUCCESS);
if (!buf_len)
return 0;
std::copy(buf->data(), buf->data() + buf_len, data_[index].begin() + offset);
return buf_len;
}
int MemEntryImpl::InternalReadSparseData(int64_t offset,
IOBuffer* buf,
int buf_len) {
DCHECK_EQ(PARENT_ENTRY, type());
if (!InitSparseInfo())
return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
if (offset < 0 || buf_len < 0)
return net::ERR_INVALID_ARGUMENT;
// We will keep using this buffer and adjust the offset in this buffer.
scoped_refptr<net::DrainableIOBuffer> io_buf(
new net::DrainableIOBuffer(buf, buf_len));
// Iterate until we have read enough.
while (io_buf->BytesRemaining()) {
MemEntryImpl* child = GetChild(offset + io_buf->BytesConsumed(), false);
// No child present for that offset.
if (!child)
break;
// We then need to prepare the child offset and len.
int child_offset = ToChildOffset(offset + io_buf->BytesConsumed());
// If we are trying to read from a position that the child entry has no data
// we should stop.
if (child_offset < child->child_first_pos_)
break;
if (net_log_.IsCapturing()) {
net_log_.BeginEvent(
net::NetLogEventType::SPARSE_READ_CHILD_DATA,
CreateNetLogSparseReadWriteCallback(child->net_log_.source(),
io_buf->BytesRemaining()));
}
int ret = child->ReadData(kSparseData, child_offset, io_buf.get(),
io_buf->BytesRemaining(), CompletionCallback());
if (net_log_.IsCapturing()) {
net_log_.EndEventWithNetErrorCode(
net::NetLogEventType::SPARSE_READ_CHILD_DATA, ret);
}
// If we encounter an error in one entry, return immediately.
if (ret < 0)
return ret;
else if (ret == 0)
break;
// Increment the counter by number of bytes read in the child entry.
io_buf->DidConsume(ret);
}
UpdateStateOnUse(ENTRY_WAS_NOT_MODIFIED);
return io_buf->BytesConsumed();
}
int MemEntryImpl::InternalWriteSparseData(int64_t offset,
IOBuffer* buf,
int buf_len) {
DCHECK_EQ(PARENT_ENTRY, type());
if (!InitSparseInfo())
return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
if (offset < 0 || buf_len < 0)
return net::ERR_INVALID_ARGUMENT;
scoped_refptr<net::DrainableIOBuffer> io_buf(
new net::DrainableIOBuffer(buf, buf_len));
// This loop walks through child entries continuously starting from |offset|
// and writes blocks of data (of maximum size kMaxSparseEntrySize) into each
// child entry until all |buf_len| bytes are written. The write operation can
// start in the middle of an entry.
while (io_buf->BytesRemaining()) {
MemEntryImpl* child = GetChild(offset + io_buf->BytesConsumed(), true);
int child_offset = ToChildOffset(offset + io_buf->BytesConsumed());
// Find the right amount to write, this evaluates the remaining bytes to
// write and remaining capacity of this child entry.
int write_len = std::min(static_cast<int>(io_buf->BytesRemaining()),
kMaxSparseEntrySize - child_offset);
// Keep a record of the last byte position (exclusive) in the child.
int data_size = child->GetDataSize(kSparseData);
if (net_log_.IsCapturing()) {
net_log_.BeginEvent(net::NetLogEventType::SPARSE_WRITE_CHILD_DATA,
CreateNetLogSparseReadWriteCallback(
child->net_log_.source(), write_len));
}
// Always writes to the child entry. This operation may overwrite data
// previously written.
// TODO(hclam): if there is data in the entry and this write is not
// continuous we may want to discard this write.
int ret = child->WriteData(kSparseData, child_offset, io_buf.get(),
write_len, CompletionCallback(), true);
if (net_log_.IsCapturing()) {
net_log_.EndEventWithNetErrorCode(
net::NetLogEventType::SPARSE_WRITE_CHILD_DATA, ret);
}
if (ret < 0)
return ret;
else if (ret == 0)
break;
// Keep a record of the first byte position in the child if the write was
// not aligned nor continuous. This is to enable witting to the middle
// of an entry and still keep track of data off the aligned edge.
if (data_size != child_offset)
child->child_first_pos_ = child_offset;
// Adjust the offset in the IO buffer.
io_buf->DidConsume(ret);
}
UpdateStateOnUse(ENTRY_WAS_MODIFIED);
return io_buf->BytesConsumed();
}
int MemEntryImpl::InternalGetAvailableRange(int64_t offset,
int len,
int64_t* start) {
DCHECK_EQ(PARENT_ENTRY, type());
DCHECK(start);
if (!InitSparseInfo())
return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
if (offset < 0 || len < 0 || !start)
return net::ERR_INVALID_ARGUMENT;
MemEntryImpl* current_child = nullptr;
// Find the first child and record the number of empty bytes.
int empty = FindNextChild(offset, len, &current_child);
if (current_child && empty < len) {
*start = offset + empty;
len -= empty;
// Counts the number of continuous bytes.
int continuous = 0;
// This loop scan for continuous bytes.
while (len && current_child) {
// Number of bytes available in this child.
int data_size = current_child->GetDataSize(kSparseData) -
ToChildOffset(*start + continuous);
if (data_size > len)
data_size = len;
// We have found more continuous bytes so increment the count. Also
// decrement the length we should scan.
continuous += data_size;
len -= data_size;
// If the next child is discontinuous, break the loop.
if (FindNextChild(*start + continuous, len, &current_child))
break;
}
return continuous;
}
*start = offset;
return 0;
}
bool MemEntryImpl::InitSparseInfo() {
DCHECK_EQ(PARENT_ENTRY, type());
if (!children_) {
// If we already have some data in sparse stream but we are being
// initialized as a sparse entry, we should fail.
if (GetDataSize(kSparseData))
return false;
children_.reset(new EntryMap());
// The parent entry stores data for the first block, so save this object to
// index 0.
(*children_)[0] = this;
}
return true;
}
MemEntryImpl* MemEntryImpl::GetChild(int64_t offset, bool create) {
DCHECK_EQ(PARENT_ENTRY, type());
int index = ToChildIndex(offset);
EntryMap::iterator i = children_->find(index);
if (i != children_->end())
return i->second;
if (create)
return new MemEntryImpl(backend_, index, this, net_log_.net_log());
return nullptr;
}
int MemEntryImpl::FindNextChild(int64_t offset, int len, MemEntryImpl** child) {
DCHECK(child);
*child = nullptr;
int scanned_len = 0;
// This loop tries to find the first existing child.
while (scanned_len < len) {
// This points to the current offset in the child.
int current_child_offset = ToChildOffset(offset + scanned_len);
MemEntryImpl* current_child = GetChild(offset + scanned_len, false);
if (current_child) {
int child_first_pos = current_child->child_first_pos_;
// This points to the first byte that we should be reading from, we need
// to take care of the filled region and the current offset in the child.
int first_pos = std::max(current_child_offset, child_first_pos);
// If the first byte position we should read from doesn't exceed the
// filled region, we have found the first child.
if (first_pos < current_child->GetDataSize(kSparseData)) {
*child = current_child;
// We need to advance the scanned length.
scanned_len += first_pos - current_child_offset;
break;
}
}
scanned_len += kMaxSparseEntrySize - current_child_offset;
}
return scanned_len;
}
} // namespace disk_cache

View File

@@ -0,0 +1,196 @@
// 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.
#ifndef NET_DISK_CACHE_MEMORY_MEM_ENTRY_IMPL_H_
#define NET_DISK_CACHE_MEMORY_MEM_ENTRY_IMPL_H_
#include <stdint.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "base/containers/linked_list.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "base/trace_event/memory_usage_estimator.h"
#include "net/base/net_export.h"
#include "net/disk_cache/disk_cache.h"
#include "net/log/net_log_with_source.h"
namespace net {
class NetLog;
}
namespace disk_cache {
class MemBackendImpl;
// This class implements the Entry interface for the memory-only cache. An
// object of this class represents a single entry on the cache. We use two types
// of entries, parent and child to support sparse caching.
//
// A parent entry is non-sparse until a sparse method is invoked (i.e.
// ReadSparseData, WriteSparseData, GetAvailableRange) when sparse information
// is initialized. It then manages a list of child entries and delegates the
// sparse API calls to the child entries. It creates and deletes child entries
// and updates the list when needed.
//
// A child entry is used to carry partial cache content, non-sparse methods like
// ReadData and WriteData cannot be applied to them. The lifetime of a child
// entry is managed by the parent entry that created it except that the entry
// can be evicted independently. A child entry does not have a key and it is not
// registered in the backend's entry map.
//
// A sparse child entry has a fixed maximum size and can be partially
// filled. There can only be one continous filled region in a sparse entry, as
// illustrated by the following example:
// | xxx ooooo |
// x = unfilled region
// o = filled region
// It is guaranteed that there is at most one unfilled region and one filled
// region, and the unfilled region (if there is one) is always before the filled
// region. The book keeping for filled region in a sparse entry is done by using
// the variable |child_first_pos_|.
class NET_EXPORT_PRIVATE MemEntryImpl final
: public Entry,
public base::LinkNode<MemEntryImpl> {
public:
enum EntryType {
PARENT_ENTRY,
CHILD_ENTRY,
};
// Provided to better document calls to |UpdateStateOnUse()|.
enum EntryModified {
ENTRY_WAS_NOT_MODIFIED,
ENTRY_WAS_MODIFIED,
};
// Constructor for parent entries.
MemEntryImpl(MemBackendImpl* backend,
const std::string& key,
net::NetLog* net_log);
// Constructor for child entries.
MemEntryImpl(MemBackendImpl* backend,
int child_id,
MemEntryImpl* parent,
net::NetLog* net_log);
void Open();
bool InUse() const;
EntryType type() const { return parent_ ? CHILD_ENTRY : PARENT_ENTRY; }
const std::string& key() const { return key_; }
const MemEntryImpl* parent() const { return parent_; }
int child_id() const { return child_id_; }
base::Time last_used() const { return last_used_; }
// The in-memory size of this entry to use for the purposes of eviction.
int GetStorageSize() const;
// Update an entry's position in the backend LRU list and set |last_used_|. If
// the entry was modified, also update |last_modified_|.
void UpdateStateOnUse(EntryModified modified_enum);
// From disk_cache::Entry:
void Doom() override;
void Close() override;
std::string GetKey() const override;
base::Time GetLastUsed() const override;
base::Time GetLastModified() const override;
int32_t GetDataSize(int index) const override;
int ReadData(int index,
int offset,
IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) override;
int WriteData(int index,
int offset,
IOBuffer* buf,
int buf_len,
const CompletionCallback& callback,
bool truncate) override;
int ReadSparseData(int64_t offset,
IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) override;
int WriteSparseData(int64_t offset,
IOBuffer* buf,
int buf_len,
const CompletionCallback& callback) override;
int GetAvailableRange(int64_t offset,
int len,
int64_t* start,
const CompletionCallback& callback) override;
bool CouldBeSparse() const override;
void CancelSparseIO() override {}
int ReadyForSparseIO(const CompletionCallback& callback) override;
size_t EstimateMemoryUsage() const;
private:
MemEntryImpl(MemBackendImpl* backend,
const std::string& key,
int child_id,
MemEntryImpl* parent,
net::NetLog* net_log);
using EntryMap = std::unordered_map<int, MemEntryImpl*>;
static const int kNumStreams = 3;
~MemEntryImpl() override;
// Do all the work for corresponding public functions. Implemented as
// separate functions to make logging of results simpler.
int InternalReadData(int index, int offset, IOBuffer* buf, int buf_len);
int InternalWriteData(int index, int offset, IOBuffer* buf, int buf_len,
bool truncate);
int InternalReadSparseData(int64_t offset, IOBuffer* buf, int buf_len);
int InternalWriteSparseData(int64_t offset, IOBuffer* buf, int buf_len);
int InternalGetAvailableRange(int64_t offset, int len, int64_t* start);
// Initializes the children map and sparse info. This method is only called
// on a parent entry.
bool InitSparseInfo();
// Returns an entry responsible for |offset|. The returned entry can be a
// child entry or this entry itself if |offset| points to the first range.
// If such entry does not exist and |create| is true, a new child entry is
// created.
MemEntryImpl* GetChild(int64_t offset, bool create);
// Finds the first child located within the range [|offset|, |offset + len|).
// Returns the number of bytes ahead of |offset| to reach the first available
// bytes in the entry. The first child found is output to |child|.
int FindNextChild(int64_t offset, int len, MemEntryImpl** child);
std::string key_;
std::vector<char> data_[kNumStreams]; // User data.
int ref_count_;
int child_id_; // The ID of a child entry.
int child_first_pos_; // The position of the first byte in a child
// entry.
// Pointer to the parent entry, or nullptr if this entry is a parent entry.
MemEntryImpl* parent_;
std::unique_ptr<EntryMap> children_;
base::Time last_modified_;
base::Time last_used_;
MemBackendImpl* backend_; // Back pointer to the cache.
bool doomed_; // True if this entry was removed from the cache.
net::NetLogWithSource net_log_;
DISALLOW_COPY_AND_ASSIGN(MemEntryImpl);
};
} // namespace disk_cache
#endif // NET_DISK_CACHE_MEMORY_MEM_ENTRY_IMPL_H_