| Index: net/disk_cache/v3/block_bitmaps.cc
|
| ===================================================================
|
| --- net/disk_cache/v3/block_bitmaps.cc (revision 209202)
|
| +++ net/disk_cache/v3/block_bitmaps.cc (working copy)
|
| @@ -2,77 +2,40 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include "net/disk_cache/block_files.h"
|
| +#include "net/disk_cache/v3/block_bitmaps.h"
|
|
|
| -#include "base/atomicops.h"
|
| -#include "base/file_util.h"
|
| #include "base/metrics/histogram.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/threading/thread_checker.h"
|
| #include "base/time/time.h"
|
| -#include "net/disk_cache/cache_util.h"
|
| -#include "net/disk_cache/file_lock.h"
|
| +#include "net/disk_cache/disk_format_base.h"
|
| #include "net/disk_cache/trace.h"
|
|
|
| using base::TimeTicks;
|
|
|
| namespace disk_cache {
|
|
|
| -BlockFiles::BlockFiles(const base::FilePath& path)
|
| - : init_(false), zero_buffer_(NULL), path_(path) {
|
| +BlockBitmaps::BlockBitmaps() {
|
| }
|
|
|
| -BlockFiles::~BlockFiles() {
|
| - if (zero_buffer_)
|
| - delete[] zero_buffer_;
|
| - CloseFiles();
|
| +BlockBitmaps::~BlockBitmaps() {
|
| }
|
|
|
| -bool BlockFiles::Init(bool create_files) {
|
| - DCHECK(!init_);
|
| - if (init_)
|
| - return false;
|
| -
|
| - thread_checker_.reset(new base::ThreadChecker);
|
| -
|
| - block_files_.resize(kFirstAdditionalBlockFile);
|
| - for (int i = 0; i < kFirstAdditionalBlockFile; i++) {
|
| - if (create_files)
|
| - if (!CreateBlockFile(i, static_cast<FileType>(i + 1), true))
|
| - return false;
|
| -
|
| - if (!OpenBlockFile(i))
|
| - return false;
|
| -
|
| - // Walk this chain of files removing empty ones.
|
| - if (!RemoveEmptyFile(static_cast<FileType>(i + 1)))
|
| - return false;
|
| - }
|
| -
|
| - init_ = true;
|
| - return true;
|
| +void BlockBitmaps::Init(const BlockFilesBitmaps& bitmaps) {
|
| + bitmaps_ = bitmaps;
|
| }
|
|
|
| -bool BlockFiles::CreateBlock(FileType block_type, int block_count,
|
| - Addr* block_address) {
|
| - DCHECK(thread_checker_->CalledOnValidThread());
|
| - if (block_type < RANKINGS || block_type > BLOCK_4K ||
|
| - block_count < 1 || block_count > 4)
|
| +bool BlockBitmaps::CreateBlock(FileType block_type,
|
| + int block_count,
|
| + Addr* block_address) {
|
| + if (block_type < BLOCK_256 || block_count < 1 || block_count > kMaxNumBlocks)
|
| return false;
|
| - if (!init_)
|
| - return false;
|
|
|
| - MappedFile* file = FileForNewBlock(block_type, block_count);
|
| - if (!file)
|
| + int header_num = HeaderNumberForNewBlock(block_type, block_count);
|
| + if (header_num < 0)
|
| return false;
|
|
|
| - ScopedFlush flush(file);
|
| - BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
|
| -
|
| int target_size = 0;
|
| - for (int i = block_count; i <= 4; i++) {
|
| - if (header->empty[i - 1]) {
|
| + for (int i = block_count; i <= kMaxNumBlocks; i++) {
|
| + if (bitmaps_[header_num]->empty[i - 1]) {
|
| target_size = i;
|
| break;
|
| }
|
| @@ -80,65 +43,39 @@
|
|
|
| DCHECK(target_size);
|
| int index;
|
| - if (!CreateMapBlock(target_size, block_count, header, &index))
|
| + if (!bitmaps_[header_num].CreateMapBlock(target_size, block_count, &index))
|
| return false;
|
|
|
| - Addr address(block_type, block_count, header->this_file, index);
|
| + if (!index && (block_type == BLOCK_ENTRIES || block_type == BLOCK_EVICTED) &&
|
| + !bitmaps_[header_num].CreateMapBlock(target_size, block_count, &index)) {
|
| + // index 0 for entries is a reserved value.
|
| + return false;
|
| + }
|
| +
|
| + Addr address(block_type, block_count, bitmaps_[header_num]->this_file, index);
|
| block_address->set_value(address.value());
|
| Trace("CreateBlock 0x%x", address.value());
|
| return true;
|
| }
|
|
|
| -void BlockFiles::DeleteBlock(Addr address, bool deep) {
|
| - DCHECK(thread_checker_->CalledOnValidThread());
|
| +void BlockBitmaps::DeleteBlock(Addr address) {
|
| if (!address.is_initialized() || address.is_separate_file())
|
| return;
|
|
|
| - if (!zero_buffer_) {
|
| - zero_buffer_ = new char[Addr::BlockSizeForFileType(BLOCK_4K) * 4];
|
| - memset(zero_buffer_, 0, Addr::BlockSizeForFileType(BLOCK_4K) * 4);
|
| - }
|
| - MappedFile* file = GetFile(address);
|
| - if (!file)
|
| + int header_num = GetHeaderNumber(address);
|
| + if (header_num < 0)
|
| return;
|
|
|
| Trace("DeleteBlock 0x%x", address.value());
|
| -
|
| - size_t size = address.BlockSize() * address.num_blocks();
|
| - size_t offset = address.start_block() * address.BlockSize() +
|
| - kBlockHeaderSize;
|
| - if (deep)
|
| - file->Write(zero_buffer_, size, offset);
|
| -
|
| - BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
|
| - DeleteMapBlock(address.start_block(), address.num_blocks(), header);
|
| - file->Flush();
|
| -
|
| - if (!header->num_entries) {
|
| - // This file is now empty. Let's try to delete it.
|
| - FileType type = Addr::RequiredFileType(header->entry_size);
|
| - if (Addr::BlockSizeForFileType(RANKINGS) == header->entry_size)
|
| - type = RANKINGS;
|
| - RemoveEmptyFile(type); // Ignore failures.
|
| - }
|
| + bitmaps_[header_num].DeleteMapBlock(address.start_block(),
|
| + address.num_blocks());
|
| }
|
|
|
| -void BlockFiles::CloseFiles() {
|
| - if (init_) {
|
| - DCHECK(thread_checker_->CalledOnValidThread());
|
| - }
|
| - init_ = false;
|
| - for (unsigned int i = 0; i < block_files_.size(); i++) {
|
| - if (block_files_[i]) {
|
| - block_files_[i]->Release();
|
| - block_files_[i] = NULL;
|
| - }
|
| - }
|
| - block_files_.clear();
|
| +void BlockBitmaps::Clear() {
|
| + bitmaps_.clear();
|
| }
|
|
|
| -void BlockFiles::ReportStats() {
|
| - DCHECK(thread_checker_->CalledOnValidThread());
|
| +void BlockBitmaps::ReportStats() {
|
| int used_blocks[kFirstAdditionalBlockFile];
|
| int load[kFirstAdditionalBlockFile];
|
| for (int i = 0; i < kFirstAdditionalBlockFile; i++) {
|
| @@ -155,167 +92,88 @@
|
| UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_3", load[3], 101);
|
| }
|
|
|
| -bool BlockFiles::IsValid(Addr address) {
|
| +bool BlockBitmaps::IsValid(Addr address) {
|
| #ifdef NDEBUG
|
| return true;
|
| #else
|
| if (!address.is_initialized() || address.is_separate_file())
|
| return false;
|
|
|
| - MappedFile* file = GetFile(address);
|
| - if (!file)
|
| + int header_num = GetHeaderNumber(address);
|
| + if (header_num < 0)
|
| return false;
|
|
|
| - BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
|
| - bool rv = UsedMapBlock(address.start_block(), address.num_blocks(), header);
|
| + bool rv = bitmaps_[header_num].UsedMapBlock(address.start_block(),
|
| + address.num_blocks());
|
| DCHECK(rv);
|
| -
|
| - static bool read_contents = false;
|
| - if (read_contents) {
|
| - scoped_ptr<char[]> buffer;
|
| - buffer.reset(new char[Addr::BlockSizeForFileType(BLOCK_4K) * 4]);
|
| - size_t size = address.BlockSize() * address.num_blocks();
|
| - size_t offset = address.start_block() * address.BlockSize() +
|
| - kBlockHeaderSize;
|
| - bool ok = file->Read(buffer.get(), size, offset);
|
| - DCHECK(ok);
|
| - }
|
| -
|
| return rv;
|
| #endif
|
| }
|
|
|
| -MappedFile* BlockFiles::GetFile(Addr address) {
|
| - DCHECK(thread_checker_->CalledOnValidThread());
|
| - DCHECK(block_files_.size() >= 4);
|
| +int BlockBitmaps::GetHeaderNumber(Addr address) {
|
| + DCHECK_GE(bitmaps_.size(),
|
| + static_cast<size_t>(kFirstAdditionalBlockFileV3));
|
| DCHECK(address.is_block_file() || !address.is_initialized());
|
| if (!address.is_initialized())
|
| - return NULL;
|
| + return -1;
|
|
|
| int file_index = address.FileNumber();
|
| - if (static_cast<unsigned int>(file_index) >= block_files_.size() ||
|
| - !block_files_[file_index]) {
|
| - // We need to open the file
|
| - if (!OpenBlockFile(file_index))
|
| - return NULL;
|
| + if (static_cast<unsigned int>(file_index) >= bitmaps_.size() ||
|
| + !bitmaps_[file_index].Get()) {
|
| + return -1;
|
| }
|
| - DCHECK(block_files_.size() >= static_cast<unsigned int>(file_index));
|
| - return block_files_[file_index];
|
| + DCHECK(bitmaps_.size() >= static_cast<unsigned int>(file_index));
|
| + return file_index;
|
| }
|
|
|
| -bool BlockFiles::GrowBlockFile(MappedFile* file, BlockFileHeader* header) {
|
| - if (kMaxBlocks == header->max_entries)
|
| - return false;
|
| -
|
| - ScopedFlush flush(file);
|
| - DCHECK(!header->empty[3]);
|
| - int new_size = header->max_entries + 1024;
|
| - if (new_size > kMaxBlocks)
|
| - new_size = kMaxBlocks;
|
| -
|
| - int new_size_bytes = new_size * header->entry_size + sizeof(*header);
|
| -
|
| - if (!file->SetLength(new_size_bytes)) {
|
| - // Most likely we are trying to truncate the file, so the header is wrong.
|
| - if (header->updating < 10 && !FixBlockFileHeader(file)) {
|
| - // If we can't fix the file increase the lock guard so we'll pick it on
|
| - // the next start and replace it.
|
| - header->updating = 100;
|
| - return false;
|
| - }
|
| - return (header->max_entries >= new_size);
|
| - }
|
| -
|
| - FileLock lock(header);
|
| - header->empty[3] = (new_size - header->max_entries) / 4; // 4 blocks entries
|
| - header->max_entries = new_size;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -MappedFile* BlockFiles::FileForNewBlock(FileType block_type, int block_count) {
|
| +int BlockBitmaps::HeaderNumberForNewBlock(FileType block_type,
|
| + int block_count) {
|
| COMPILE_ASSERT(RANKINGS == 1, invalid_file_type);
|
| - MappedFile* file = block_files_[block_type - 1];
|
| - BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
|
| + int header_num = block_type - 1;
|
| + bool found = true;
|
|
|
| TimeTicks start = TimeTicks::Now();
|
| - while (NeedToGrowBlockFile(header, block_count)) {
|
| - if (kMaxBlocks == header->max_entries) {
|
| - file = NextFile(file);
|
| - if (!file)
|
| - return NULL;
|
| - header = reinterpret_cast<BlockFileHeader*>(file->buffer());
|
| - continue;
|
| + while (bitmaps_[header_num].NeedToGrowBlockFile(block_count)) {
|
| + header_num = bitmaps_[header_num]->next_file;
|
| + if (!header_num) {
|
| + found = false;
|
| + break;
|
| }
|
| -
|
| - if (!GrowBlockFile(file, header))
|
| - return NULL;
|
| - break;
|
| }
|
| - HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start);
|
| - return file;
|
| -}
|
|
|
| -// Note that we expect to be called outside of a FileLock... however, we cannot
|
| -// DCHECK on header->updating because we may be fixing a crash.
|
| -bool BlockFiles::FixBlockFileHeader(MappedFile* file) {
|
| - ScopedFlush flush(file);
|
| - BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
|
| - int file_size = static_cast<int>(file->GetLength());
|
| - if (file_size < static_cast<int>(sizeof(*header)))
|
| - return false; // file_size > 2GB is also an error.
|
| -
|
| - const int kMinBlockSize = 36;
|
| - const int kMaxBlockSize = 4096;
|
| - if (header->entry_size < kMinBlockSize ||
|
| - header->entry_size > kMaxBlockSize || header->num_entries < 0)
|
| - return false;
|
| -
|
| - // Make sure that we survive crashes.
|
| - header->updating = 1;
|
| - int expected = header->entry_size * header->max_entries + sizeof(*header);
|
| - if (file_size != expected) {
|
| - int max_expected = header->entry_size * kMaxBlocks + sizeof(*header);
|
| - if (file_size < expected || header->empty[3] || file_size > max_expected) {
|
| + if (!found) {
|
| + // Restart the search, looking for any file with space.
|
| + header_num = block_type - 1;
|
| + do {
|
| + if (bitmaps_[header_num].CanAllocate(block_count))
|
| + found = true;
|
| + else
|
| + header_num = bitmaps_[header_num]->next_file;
|
| + } while (header_num && !found);
|
| + if (!found) {
|
| NOTREACHED();
|
| - LOG(ERROR) << "Unexpected file size";
|
| - return false;
|
| + header_num = -1;
|
| }
|
| - // We were in the middle of growing the file.
|
| - int num_entries = (file_size - sizeof(*header)) / header->entry_size;
|
| - header->max_entries = num_entries;
|
| }
|
|
|
| - FixAllocationCounters(header);
|
| - int empty_blocks = EmptyBlocks(header);
|
| - if (empty_blocks + header->num_entries > header->max_entries)
|
| - header->num_entries = header->max_entries - empty_blocks;
|
| -
|
| - if (!ValidateCounters(header))
|
| - return false;
|
| -
|
| - header->updating = 0;
|
| - return true;
|
| + HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start);
|
| + return header_num;
|
| }
|
|
|
| // We are interested in the total number of blocks used by this file type, and
|
| // the max number of blocks that we can store (reported as the percentage of
|
| // used blocks). In order to find out the number of used blocks, we have to
|
| // substract the empty blocks from the total blocks for each file in the chain.
|
| -void BlockFiles::GetFileStats(int index, int* used_count, int* load) {
|
| +void BlockBitmaps::GetFileStats(int index, int* used_count, int* load) {
|
| int max_blocks = 0;
|
| *used_count = 0;
|
| *load = 0;
|
| for (;;) {
|
| - if (!block_files_[index] && !OpenBlockFile(index))
|
| - return;
|
| + BlockFileHeader* header = bitmaps_[index].Get();
|
|
|
| - BlockFileHeader* header =
|
| - reinterpret_cast<BlockFileHeader*>(block_files_[index]->buffer());
|
| -
|
| max_blocks += header->max_entries;
|
| int used = header->max_entries;
|
| - for (int i = 0; i < 4; i++) {
|
| + for (int i = 0; i < kMaxNumBlocks; i++) {
|
| used -= header->empty[i] * (i + 1);
|
| DCHECK_GE(used, 0);
|
| }
|
|
|