mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-28 16:26:10 +03:00
354 lines
12 KiB
C++
354 lines
12 KiB
C++
|
// Copyright 2017 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/simple/simple_file_tracker.h"
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <limits>
|
||
|
#include <memory>
|
||
|
#include <utility>
|
||
|
|
||
|
#include "base/files/file.h"
|
||
|
#include "base/metrics/histogram_macros.h"
|
||
|
#include "base/synchronization/lock.h"
|
||
|
#include "net/disk_cache/simple/simple_histogram_enums.h"
|
||
|
#include "net/disk_cache/simple/simple_synchronous_entry.h"
|
||
|
|
||
|
namespace disk_cache {
|
||
|
|
||
|
SimpleFileTracker::SimpleFileTracker(int file_limit)
|
||
|
: file_limit_(file_limit), open_files_(0) {}
|
||
|
|
||
|
SimpleFileTracker::~SimpleFileTracker() {
|
||
|
DCHECK(lru_.empty());
|
||
|
DCHECK(tracked_files_.empty());
|
||
|
}
|
||
|
|
||
|
void SimpleFileTracker::Register(const SimpleSynchronousEntry* owner,
|
||
|
SubFile subfile,
|
||
|
std::unique_ptr<base::File> file) {
|
||
|
DCHECK(file->IsValid());
|
||
|
std::vector<std::unique_ptr<base::File>> files_to_close;
|
||
|
|
||
|
{
|
||
|
base::AutoLock hold_lock(lock_);
|
||
|
|
||
|
// Make sure the list of everything with given hash exists.
|
||
|
auto insert_status = tracked_files_.insert(
|
||
|
std::make_pair(owner->entry_file_key().entry_hash,
|
||
|
std::vector<std::unique_ptr<TrackedFiles>>()));
|
||
|
|
||
|
std::vector<std::unique_ptr<TrackedFiles>>& candidates =
|
||
|
insert_status.first->second;
|
||
|
|
||
|
// See if entry for |owner| already exists, if not append.
|
||
|
TrackedFiles* owners_files = nullptr;
|
||
|
for (const std::unique_ptr<TrackedFiles>& candidate : candidates) {
|
||
|
if (candidate->owner == owner) {
|
||
|
owners_files = candidate.get();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!owners_files) {
|
||
|
candidates.emplace_back(new TrackedFiles());
|
||
|
owners_files = candidates.back().get();
|
||
|
owners_files->owner = owner;
|
||
|
owners_files->key = owner->entry_file_key();
|
||
|
}
|
||
|
|
||
|
EnsureInFrontOfLRU(owners_files);
|
||
|
|
||
|
int file_index = static_cast<int>(subfile);
|
||
|
DCHECK_EQ(TrackedFiles::TF_NO_REGISTRATION,
|
||
|
owners_files->state[file_index]);
|
||
|
owners_files->files[file_index] = std::move(file);
|
||
|
owners_files->state[file_index] = TrackedFiles::TF_REGISTERED;
|
||
|
++open_files_;
|
||
|
CloseFilesIfTooManyOpen(&files_to_close);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SimpleFileTracker::FileHandle SimpleFileTracker::Acquire(
|
||
|
const SimpleSynchronousEntry* owner,
|
||
|
SubFile subfile) {
|
||
|
std::vector<std::unique_ptr<base::File>> files_to_close;
|
||
|
|
||
|
{
|
||
|
base::AutoLock hold_lock(lock_);
|
||
|
TrackedFiles* owners_files = Find(owner);
|
||
|
int file_index = static_cast<int>(subfile);
|
||
|
|
||
|
DCHECK_EQ(TrackedFiles::TF_REGISTERED, owners_files->state[file_index]);
|
||
|
owners_files->state[file_index] = TrackedFiles::TF_ACQUIRED;
|
||
|
EnsureInFrontOfLRU(owners_files);
|
||
|
|
||
|
// Check to see if we have to reopen the file. That might push us over the
|
||
|
// fd limit. CloseFilesIfTooManyOpen will not close anything in
|
||
|
// |*owners_files| since it's already in the the TF_ACQUIRED state.
|
||
|
if (owners_files->files[file_index] == nullptr) {
|
||
|
ReopenFile(owners_files, subfile);
|
||
|
CloseFilesIfTooManyOpen(&files_to_close);
|
||
|
}
|
||
|
|
||
|
return FileHandle(this, owner, subfile,
|
||
|
owners_files->files[file_index].get());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SimpleFileTracker::TrackedFiles::TrackedFiles() : in_lru(false) {
|
||
|
std::fill(state, state + kSimpleEntryTotalFileCount, TF_NO_REGISTRATION);
|
||
|
}
|
||
|
|
||
|
SimpleFileTracker::TrackedFiles::~TrackedFiles() = default;
|
||
|
|
||
|
bool SimpleFileTracker::TrackedFiles::Empty() const {
|
||
|
for (State s : state)
|
||
|
if (s != TF_NO_REGISTRATION)
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool SimpleFileTracker::TrackedFiles::HasOpenFiles() const {
|
||
|
for (const std::unique_ptr<base::File>& file : files)
|
||
|
if (file != nullptr)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void SimpleFileTracker::Release(const SimpleSynchronousEntry* owner,
|
||
|
SubFile subfile) {
|
||
|
std::vector<std::unique_ptr<base::File>> files_to_close;
|
||
|
|
||
|
{
|
||
|
base::AutoLock hold_lock(lock_);
|
||
|
TrackedFiles* owners_files = Find(owner);
|
||
|
int file_index = static_cast<int>(subfile);
|
||
|
|
||
|
DCHECK(owners_files->state[file_index] == TrackedFiles::TF_ACQUIRED ||
|
||
|
owners_files->state[file_index] ==
|
||
|
TrackedFiles::TF_ACQUIRED_PENDING_CLOSE);
|
||
|
|
||
|
// Prepare to executed deferred close, if any.
|
||
|
if (owners_files->state[file_index] ==
|
||
|
TrackedFiles::TF_ACQUIRED_PENDING_CLOSE) {
|
||
|
files_to_close.push_back(PrepareClose(owners_files, file_index));
|
||
|
} else {
|
||
|
owners_files->state[file_index] = TrackedFiles::TF_REGISTERED;
|
||
|
}
|
||
|
|
||
|
// It's possible that we were over limit and couldn't do much about it
|
||
|
// since everything was lent out, so now may be the time to close extra
|
||
|
// stuff.
|
||
|
CloseFilesIfTooManyOpen(&files_to_close);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SimpleFileTracker::Close(const SimpleSynchronousEntry* owner,
|
||
|
SubFile subfile) {
|
||
|
std::unique_ptr<base::File> file_to_close;
|
||
|
|
||
|
{
|
||
|
base::AutoLock hold_lock(lock_);
|
||
|
TrackedFiles* owners_files = Find(owner);
|
||
|
int file_index = static_cast<int>(subfile);
|
||
|
|
||
|
DCHECK(owners_files->state[file_index] == TrackedFiles::TF_ACQUIRED ||
|
||
|
owners_files->state[file_index] == TrackedFiles::TF_REGISTERED);
|
||
|
|
||
|
if (owners_files->state[file_index] == TrackedFiles::TF_ACQUIRED) {
|
||
|
// The FD is currently acquired, so we can't clean up the TrackedFiles,
|
||
|
// just yet; even if this is the last close, so delay the close until it
|
||
|
// gets released.
|
||
|
owners_files->state[file_index] = TrackedFiles::TF_ACQUIRED_PENDING_CLOSE;
|
||
|
} else {
|
||
|
file_to_close = PrepareClose(owners_files, file_index);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SimpleFileTracker::Doom(const SimpleSynchronousEntry* owner,
|
||
|
EntryFileKey* key) {
|
||
|
base::AutoLock hold_lock(lock_);
|
||
|
auto iter = tracked_files_.find(key->entry_hash);
|
||
|
DCHECK(iter != tracked_files_.end());
|
||
|
|
||
|
uint64_t max_doom_gen = 0;
|
||
|
for (const std::unique_ptr<TrackedFiles>& file_with_same_hash :
|
||
|
iter->second) {
|
||
|
max_doom_gen =
|
||
|
std::max(max_doom_gen, file_with_same_hash->key.doom_generation);
|
||
|
}
|
||
|
|
||
|
// It would take >502 years to doom the same hash enough times (at 10^9 dooms
|
||
|
// per second) to wrap the 64 bit counter. Still, if it does wrap around,
|
||
|
// there is a security risk since we could confuse different keys.
|
||
|
CHECK_NE(max_doom_gen, std::numeric_limits<uint64_t>::max());
|
||
|
uint64_t new_doom_gen = max_doom_gen + 1;
|
||
|
|
||
|
// Update external key.
|
||
|
key->doom_generation = new_doom_gen;
|
||
|
|
||
|
// Update our own.
|
||
|
for (const std::unique_ptr<TrackedFiles>& file_with_same_hash :
|
||
|
iter->second) {
|
||
|
if (file_with_same_hash->owner == owner)
|
||
|
file_with_same_hash->key.doom_generation = new_doom_gen;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool SimpleFileTracker::IsEmptyForTesting() {
|
||
|
base::AutoLock hold_lock(lock_);
|
||
|
return tracked_files_.empty() && lru_.empty();
|
||
|
}
|
||
|
|
||
|
SimpleFileTracker::TrackedFiles* SimpleFileTracker::Find(
|
||
|
const SimpleSynchronousEntry* owner) {
|
||
|
auto candidates = tracked_files_.find(owner->entry_file_key().entry_hash);
|
||
|
DCHECK(candidates != tracked_files_.end());
|
||
|
for (const auto& candidate : candidates->second) {
|
||
|
if (candidate->owner == owner) {
|
||
|
return candidate.get();
|
||
|
}
|
||
|
}
|
||
|
LOG(DFATAL) << "SimpleFileTracker operation on non-found entry";
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<base::File> SimpleFileTracker::PrepareClose(
|
||
|
TrackedFiles* owners_files,
|
||
|
int file_index) {
|
||
|
std::unique_ptr<base::File> file_out =
|
||
|
std::move(owners_files->files[file_index]);
|
||
|
owners_files->state[file_index] = TrackedFiles::TF_NO_REGISTRATION;
|
||
|
if (owners_files->Empty()) {
|
||
|
auto iter = tracked_files_.find(owners_files->key.entry_hash);
|
||
|
for (auto i = iter->second.begin(); i != iter->second.end(); ++i) {
|
||
|
if ((*i).get() == owners_files) {
|
||
|
if (owners_files->in_lru)
|
||
|
lru_.erase(owners_files->position_in_lru);
|
||
|
iter->second.erase(i);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (iter->second.empty())
|
||
|
tracked_files_.erase(iter);
|
||
|
}
|
||
|
if (file_out != nullptr)
|
||
|
--open_files_;
|
||
|
return file_out;
|
||
|
}
|
||
|
|
||
|
void SimpleFileTracker::CloseFilesIfTooManyOpen(
|
||
|
std::vector<std::unique_ptr<base::File>>* files_to_close) {
|
||
|
std::list<TrackedFiles*>::iterator i = lru_.end();
|
||
|
while (open_files_ > file_limit_ && i != lru_.begin()) {
|
||
|
--i; // Point to the actual entry.
|
||
|
TrackedFiles* tracked_files = *i;
|
||
|
DCHECK(tracked_files->in_lru);
|
||
|
for (int j = 0; j < kSimpleEntryTotalFileCount; ++j) {
|
||
|
if (tracked_files->state[j] == TrackedFiles::TF_REGISTERED &&
|
||
|
tracked_files->files[j] != nullptr) {
|
||
|
files_to_close->push_back(std::move(tracked_files->files[j]));
|
||
|
--open_files_;
|
||
|
UMA_HISTOGRAM_ENUMERATION("SimpleCache.FileDescriptorLimiterAction",
|
||
|
FD_LIMIT_CLOSE_FILE, FD_LIMIT_OP_MAX);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!tracked_files->HasOpenFiles()) {
|
||
|
// If there is nothing here that can possibly be closed, remove this from
|
||
|
// LRU for now so we don't have to rescan it next time we are here. If the
|
||
|
// files get re-opened (in Acquire), it will get added back in.
|
||
|
DCHECK_EQ(*tracked_files->position_in_lru, tracked_files);
|
||
|
DCHECK(i == tracked_files->position_in_lru);
|
||
|
// Note that we're erasing at i, which would make it invalid, so go back
|
||
|
// one element ahead to we can decrement from that on next iteration.
|
||
|
++i;
|
||
|
lru_.erase(tracked_files->position_in_lru);
|
||
|
tracked_files->in_lru = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SimpleFileTracker::ReopenFile(TrackedFiles* owners_files,
|
||
|
SubFile subfile) {
|
||
|
int file_index = static_cast<int>(subfile);
|
||
|
DCHECK(owners_files->files[file_index] == nullptr);
|
||
|
int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
|
||
|
base::File::FLAG_WRITE | base::File::FLAG_SHARE_DELETE;
|
||
|
base::FilePath file_path =
|
||
|
owners_files->owner->GetFilenameForSubfile(subfile);
|
||
|
owners_files->files[file_index] =
|
||
|
std::make_unique<base::File>(file_path, flags);
|
||
|
if (owners_files->files[file_index]->IsValid()) {
|
||
|
UMA_HISTOGRAM_ENUMERATION("SimpleCache.FileDescriptorLimiterAction",
|
||
|
FD_LIMIT_REOPEN_FILE, FD_LIMIT_OP_MAX);
|
||
|
|
||
|
++open_files_;
|
||
|
} else {
|
||
|
owners_files->files[file_index] = nullptr;
|
||
|
UMA_HISTOGRAM_ENUMERATION("SimpleCache.FileDescriptorLimiterAction",
|
||
|
FD_LIMIT_FAIL_REOPEN_FILE, FD_LIMIT_OP_MAX);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SimpleFileTracker::EnsureInFrontOfLRU(TrackedFiles* owners_files) {
|
||
|
if (!owners_files->in_lru) {
|
||
|
lru_.push_front(owners_files);
|
||
|
owners_files->position_in_lru = lru_.begin();
|
||
|
owners_files->in_lru = true;
|
||
|
} else if (owners_files->position_in_lru != lru_.begin()) {
|
||
|
lru_.splice(lru_.begin(), lru_, owners_files->position_in_lru);
|
||
|
}
|
||
|
DCHECK_EQ(*owners_files->position_in_lru, owners_files);
|
||
|
}
|
||
|
|
||
|
SimpleFileTracker::FileHandle::FileHandle()
|
||
|
: file_tracker_(nullptr), entry_(nullptr), file_(nullptr) {}
|
||
|
|
||
|
SimpleFileTracker::FileHandle::FileHandle(SimpleFileTracker* file_tracker,
|
||
|
const SimpleSynchronousEntry* entry,
|
||
|
SimpleFileTracker::SubFile subfile,
|
||
|
base::File* file)
|
||
|
: file_tracker_(file_tracker),
|
||
|
entry_(entry),
|
||
|
subfile_(subfile),
|
||
|
file_(file) {}
|
||
|
|
||
|
SimpleFileTracker::FileHandle::FileHandle(FileHandle&& other) {
|
||
|
*this = std::move(other);
|
||
|
}
|
||
|
|
||
|
SimpleFileTracker::FileHandle::~FileHandle() {
|
||
|
if (entry_)
|
||
|
file_tracker_->Release(entry_, subfile_);
|
||
|
}
|
||
|
|
||
|
SimpleFileTracker::FileHandle& SimpleFileTracker::FileHandle::operator=(
|
||
|
FileHandle&& other) {
|
||
|
file_tracker_ = other.file_tracker_;
|
||
|
entry_ = other.entry_;
|
||
|
subfile_ = other.subfile_;
|
||
|
file_ = other.file_;
|
||
|
other.file_tracker_ = nullptr;
|
||
|
other.entry_ = nullptr;
|
||
|
other.file_ = nullptr;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
base::File* SimpleFileTracker::FileHandle::operator->() const {
|
||
|
return file_;
|
||
|
}
|
||
|
|
||
|
base::File* SimpleFileTracker::FileHandle::get() const {
|
||
|
return file_;
|
||
|
}
|
||
|
|
||
|
bool SimpleFileTracker::FileHandle::IsOK() const {
|
||
|
return file_ && file_->IsValid();
|
||
|
}
|
||
|
|
||
|
} // namespace disk_cache
|