Chromium Code Reviews| Index: net/disk_cache/v3/block_bitmaps.cc |
| =================================================================== |
| --- net/disk_cache/v3/block_bitmaps.cc (revision 208788) |
| +++ 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.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 > 4) |
|
gavinp
2013/06/27 07:06:18
I don't think it's good to just put 1 and 4 throug
rvargas (doing something else)
2013/06/27 19:57:54
Updated the use of 4. 1 is not really magic.
|
| 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]) { |
| + 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()) |
|
gavinp
2013/06/27 07:06:18
Why isn't this an error?
rvargas (doing something else)
2013/06/27 19:57:54
I'm mainly following the current code semantics: T
|
| 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,164 +92,84 @@ |
| 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(bitmaps_.size() >= 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++) { |