Index: net/disk_cache/v3/backend_worker.cc |
=================================================================== |
--- net/disk_cache/v3/backend_worker.cc (revision 232523) |
+++ net/disk_cache/v3/backend_worker.cc (working copy) |
@@ -2,42 +2,32 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "net/disk_cache/backend_impl.h" |
+#include "net/disk_cache/v3/backend_worker.h" |
#include "base/bind.h" |
-#include "base/bind_helpers.h" |
#include "base/file_util.h" |
-#include "base/files/file_path.h" |
-#include "base/hash.h" |
#include "base/message_loop/message_loop.h" |
-#include "base/metrics/field_trial.h" |
-#include "base/metrics/histogram.h" |
-#include "base/metrics/stats_counters.h" |
-#include "base/rand_util.h" |
-#include "base/strings/string_util.h" |
#include "base/strings/stringprintf.h" |
-#include "base/sys_info.h" |
-#include "base/threading/thread_restrictions.h" |
-#include "base/time/time.h" |
-#include "base/timer/timer.h" |
#include "net/base/net_errors.h" |
#include "net/disk_cache/cache_util.h" |
-#include "net/disk_cache/entry_impl.h" |
#include "net/disk_cache/errors.h" |
#include "net/disk_cache/experiments.h" |
-#include "net/disk_cache/file.h" |
+#include "net/disk_cache/mapped_file.h" |
+#include "net/disk_cache/v3/backend_work_item.h" |
+#include "net/disk_cache/v3/disk_format_v3.h" |
-// This has to be defined before including histogram_macros.h from this file. |
-#define NET_DISK_CACHE_BACKEND_IMPL_CC_ |
-#include "net/disk_cache/histogram_macros.h" |
- |
using base::Time; |
using base::TimeDelta; |
using base::TimeTicks; |
namespace { |
-const char* kIndexName = "index"; |
+const char kIndexName[] = "index"; |
+const char kIndexBackupName[] = "index_bak"; |
+const char kTable1Name[] = "index_tb1"; |
+const char kTable2Name[] = "index_tb2"; |
+const char kTable2TempName[] = "index_tb2_tmp"; |
+const int kMaxOldFolders = 100; |
// Seems like ~240 MB correspond to less than 50k entries for 99% of the people. |
// Note that the actual target is to keep the index table load factor under 55% |
@@ -68,31 +58,28 @@ |
} |
size_t GetIndexSize(int table_len) { |
- size_t table_size = sizeof(disk_cache::CacheAddr) * table_len; |
- return sizeof(disk_cache::IndexHeader) + table_size; |
+ // |
+ //size_t table_size = sizeof(disk_cache::CacheAddr) * table_len; |
+ //return sizeof(disk_cache::IndexHeaderV3) + table_size; |
+ return 0; |
} |
+size_t GetIndexBitmapSize(int table_len) { |
+ DCHECK_LT(table_len, 1 << 22); |
+ size_t base_bits = disk_cache::kBaseBitmapBytes * 8; |
+ if (table_len < static_cast<int>(base_bits)) |
+ return sizeof(disk_cache::IndexBitmap); |
+ |
+ size_t extra_pages = (table_len / 8) - disk_cache::kBaseBitmapBytes; |
+ extra_pages = (extra_pages + 4095) / 4096; |
+ return sizeof(disk_cache::IndexBitmap) + extra_pages * 4096; |
+} |
+ |
// ------------------------------------------------------------------------ |
// Sets group for the current experiment. Returns false if the files should be |
// discarded. |
-bool InitExperiment(disk_cache::IndexHeader* header, bool cache_created) { |
- if (header->experiment == disk_cache::EXPERIMENT_OLD_FILE1 || |
- header->experiment == disk_cache::EXPERIMENT_OLD_FILE2) { |
- // Discard current cache. |
- return false; |
- } |
- |
- if (base::FieldTrialList::FindFullName("SimpleCacheTrial") == |
- "ExperimentControl") { |
- if (cache_created) { |
- header->experiment = disk_cache::EXPERIMENT_SIMPLE_CONTROL; |
- return true; |
- } else if (header->experiment != disk_cache::EXPERIMENT_SIMPLE_CONTROL) { |
- return false; |
- } |
- } |
- |
+bool InitExperiment(disk_cache::IndexHeaderV3* header) { |
header->experiment = disk_cache::NO_EXPERIMENT; |
return true; |
} |
@@ -103,204 +90,363 @@ |
namespace disk_cache { |
-BackendImpl::BackendImpl(const base::FilePath& path, |
- base::MessageLoopProxy* cache_thread, |
- net::NetLog* net_log) |
- : background_queue_(this, cache_thread), |
- path_(path), |
- block_files_(path), |
- mask_(0), |
- max_size_(0), |
- up_ticks_(0), |
- cache_type_(net::DISK_CACHE), |
- uma_report_(0), |
- user_flags_(0), |
- init_(false), |
- restarted_(false), |
- unit_test_(false), |
- read_only_(false), |
- disabled_(false), |
- new_eviction_(false), |
- first_timer_(true), |
- user_load_(false), |
- net_log_(net_log), |
- done_(true, false), |
- ptr_factory_(this) { |
+BackendImplV3::Worker::Worker(const base::FilePath& path, |
+ base::MessageLoopProxy* main_thread) |
+ : path_(path), |
+ main_thread_(main_thread), |
+ cleanup_work_item_(NULL), |
+ init_(false), |
+ doubling_index_(false), |
+ user_flags_(0) { |
} |
-int BackendImpl::SyncInit() { |
-#if defined(NET_BUILD_STRESS_CACHE) |
- // Start evictions right away. |
- up_ticks_ = kTrimDelay * 2; |
-#endif |
+int BackendImplV3::Worker::Init(uint32 flags, scoped_ptr<InitResult>* result) { |
DCHECK(!init_); |
if (init_) |
- return net::ERR_FAILED; |
+ return ERR_INIT_FAILED; |
+ user_flags_ = flags; |
+ result->reset(new InitResult); |
+ |
bool create_files = false; |
- if (!InitBackingStore(&create_files)) { |
- ReportError(ERR_STORAGE_ERROR); |
- return net::ERR_FAILED; |
+ if (!InitBackingStore(&create_files)) |
+ return ERR_STORAGE_ERROR; |
+ |
+ init_ = true; |
+ if (!LoadIndex(result->get())) |
+ return ERR_INIT_FAILED; |
+ |
+ int rv = ERR_NO_ERROR; |
+ IndexHeaderV3* index = |
+ reinterpret_cast<IndexHeaderV3*>(index_header_->buffer()); |
+ if (create_files || !index->num_entries) |
+ rv = ERR_CACHE_CREATED; |
+ |
+ if (create_files && (flags & EVICTION_V2)) { |
+ index->flags |= CACHE_EVICTION_2; |
} |
- num_refs_ = num_pending_io_ = max_refs_ = 0; |
- entry_count_ = byte_count_ = 0; |
+ if (!(flags & BASIC_UNIT_TEST) && !InitExperiment(index)) |
+ return ERR_INIT_FAILED; |
- if (!restarted_) { |
- buffer_bytes_ = 0; |
- trace_object_ = TraceObject::GetTraceObject(); |
- // Create a recurrent timer of 30 secs. |
- int timer_delay = unit_test_ ? 1000 : 30000; |
- timer_.reset(new base::RepeatingTimer<BackendImpl>()); |
- timer_->Start(FROM_HERE, TimeDelta::FromMilliseconds(timer_delay), this, |
- &BackendImpl::OnStatsTimer); |
+ if (index->crash != 0) |
+ rv = ERR_PREVIOUS_CRASH; |
+ index->crash = 1; |
+ |
+ block_files_.reset(new BlockFiles(path_)); |
+ if (flags & BASIC_UNIT_TEST) |
+ block_files_->UseSmallSizeIncrementsForTest(); |
+ |
+ if (!block_files_->Init(create_files, kFirstAdditionalBlockFileV3)) |
+ return ERR_INIT_FAILED; |
+ |
+ block_files_->GetBitmaps(index->max_block_file, |
+ &result->get()->block_bitmaps); |
+ index->max_block_file = static_cast<int>(result->get()->block_bitmaps.size()); |
+ |
+ if (!InitStats(index, result->get())) |
+ return ERR_INIT_FAILED; |
+ |
+#if defined(STRESS_CACHE_EXTENDED_VALIDATION) |
+ trace_object_->EnableTracing(false); |
+ int sc = SelfCheck(); |
+ if (sc < 0 && sc != ERR_NUM_ENTRIES_MISMATCH) |
+ NOTREACHED(); |
+ trace_object_->EnableTracing(true); |
+#endif |
+ |
+ return rv; |
+} |
+ |
+int BackendImplV3::Worker::Restart(uint32 flags, |
+ scoped_ptr<InitResult>* result) { |
+ Trace("Worker::Restart"); |
+ if (init_) { |
+ init_ = false; |
} |
- init_ = true; |
- Trace("Init"); |
+ CloseFiles(); |
+ DeleteCache(path_, false); |
- if (data_->header.experiment != NO_EXPERIMENT && |
- cache_type_ != net::DISK_CACHE) { |
- // No experiment for other caches. |
- return net::ERR_FAILED; |
+ return Init(flags, result); |
+} |
+ |
+int BackendImplV3::Worker::GrowIndex(uint32 flags, |
+ scoped_ptr<InitResult>* result) { |
+ Trace("Worker::GrowIndex, flags 0x%x", flags); |
+ if (!init_) |
+ return ERR_OPERATION_FAILED; |
+ |
+ if (flags & WorkItem::WORK_COMPLETE) { |
+ index_header_ = big_index_header_; |
+ big_index_header_ = NULL; |
+ if (big_main_table_) { |
+ main_table_ = big_main_table_; |
+ big_main_table_ = NULL; |
+ } |
+ if (!big_extra_temp_table_) |
+ extra_table_ = big_extra_table_; |
+ big_extra_table_ = NULL; |
+ |
+ // If the index takes time to move the cells, it creates a new work item to |
+ // notify completion, which executes this code. |
+ if (big_extra_temp_table_) |
+ return GrowDone(); |
+ |
+ return ERR_NO_ERROR; |
} |
- if (!(user_flags_ & kNoRandom)) { |
- // The unit test controls directly what to test. |
- new_eviction_ = (cache_type_ == net::DISK_CACHE); |
+ IndexHeaderV3* header = |
+ reinterpret_cast<IndexHeaderV3*>(index_header_->buffer()); |
+ |
+ int current_main_len = header->table_len / kBaseTableLen * kBaseTableLen; |
+ int step_size = std::min(8192, current_main_len / 8); |
+ if (user_flags_ & BASIC_UNIT_TEST) |
+ step_size = 8; |
+ if ((user_flags_ & UNIT_TEST_MODE) && !doubling_index_) |
+ step_size = (header->table_len * 3 / 2) & 0x7ffffff0; |
+ int new_len = header->table_len + step_size; |
+ |
+ bool double_index = false; |
+ if (!doubling_index_) { |
+ DCHECK(!big_extra_table_); |
+ DCHECK(!big_main_table_); |
+ double_index = (new_len / kBaseTableLen != |
+ header->table_len / kBaseTableLen); |
} |
- if (!CheckIndex()) { |
- ReportError(ERR_INIT_FAILED); |
- return net::ERR_FAILED; |
+ int extra_len = new_len - kBaseTableLen; |
+ if (double_index) { |
+ // We double the table when the extra table is about to reach the size of |
+ // the main table. That means that right after this, the new extra table |
+ // should be between 19% and 23% of the main table so we start with 25%. |
+ extra_len = std::min(8192, current_main_len / 4); |
+ extra_len = (user_flags_ & BASIC_UNIT_TEST) ? 128 : extra_len; |
+ int main_len = (header->table_len / kBaseTableLen + 1) * kBaseTableLen; |
+ new_len = main_len + extra_len; |
+ |
+ if (!CreateExtraTable(extra_len * kBytesPerCell)) |
+ return ERR_OPERATION_FAILED; |
+ |
+ if (!main_table_->SetLength(main_len * kBytesPerCell)) |
+ return ERR_OPERATION_FAILED; |
+ } else if (doubling_index_) { |
+ if (!big_extra_temp_table_->SetLength(extra_len * kBytesPerCell)) |
+ return ERR_OPERATION_FAILED; |
+ } else { |
+ if (!extra_table_->SetLength(extra_len * kBytesPerCell)) |
+ return ERR_OPERATION_FAILED; |
} |
- if (!restarted_ && (create_files || !data_->header.num_entries)) |
- ReportError(ERR_CACHE_CREATED); |
+ if (!index_header_->SetLength(GetIndexBitmapSize(new_len))) |
+ return ERR_OPERATION_FAILED; |
- if (!(user_flags_ & kNoRandom) && cache_type_ == net::DISK_CACHE && |
- !InitExperiment(&data_->header, create_files)) { |
- return net::ERR_FAILED; |
+ scoped_refptr<MappedFile> big_index_header = new MappedFile(); |
+ if (!big_index_header->Init(path_.AppendASCII(kIndexName), 0)) { |
+ LOG(ERROR) << "Unable to remap index"; |
+ return ERR_OPERATION_FAILED; |
} |
- // We don't care if the value overflows. The only thing we care about is that |
- // the id cannot be zero, because that value is used as "not dirty". |
- // Increasing the value once per second gives us many years before we start |
- // having collisions. |
- data_->header.this_id++; |
- if (!data_->header.this_id) |
- data_->header.this_id++; |
+ scoped_refptr<MappedFile> big_extra_table = new MappedFile(); |
+ const char* extra_name = (double_index || doubling_index_) ? kTable2TempName : |
+ kTable2Name; |
+ if (!big_extra_table->Init(path_.AppendASCII(extra_name), 0)) { |
+ LOG(ERROR) << "Unable to remap index_tb2"; |
+ return ERR_OPERATION_FAILED; |
+ } |
- bool previous_crash = (data_->header.crash != 0); |
- data_->header.crash = 1; |
+ if (double_index) { |
+ scoped_refptr<MappedFile> big_main_table = new MappedFile(); |
+ if (!big_main_table->Init(path_.AppendASCII(kTable1Name), 0)) { |
+ LOG(ERROR) << "Unable to remap index_tb1"; |
+ return ERR_OPERATION_FAILED; |
+ } |
+ big_main_table_.swap(big_main_table); |
- if (!block_files_.Init(create_files)) |
- return net::ERR_FAILED; |
- |
- // We want to minimize the changes to cache for an AppCache. |
- if (cache_type() == net::APP_CACHE) { |
- DCHECK(!new_eviction_); |
- read_only_ = true; |
- } else if (cache_type() == net::SHADER_CACHE) { |
- DCHECK(!new_eviction_); |
+ // Grab an extra reference to the new extra table that can be used for an |
+ // extended period, while the index is being rebuilt. The normal reference |
+ // (big_extra_table_) will be released when the work item is completed, but |
+ // that doesn't mean the index is done with it. |
+ // Note that we are able to process slow grow requests even when the index |
+ // is being doubled. |
+ big_extra_temp_table_ = big_extra_table; |
} |
+ big_index_header_.swap(big_index_header); |
+ big_extra_table_.swap(big_extra_table); |
- eviction_.Init(this); |
+ header = reinterpret_cast<IndexHeaderV3*>(big_index_header_->buffer()); |
+ header->table_len = new_len; |
- // stats_ and rankings_ may end up calling back to us so we better be enabled. |
- disabled_ = false; |
- if (!InitStats()) |
- return net::ERR_FAILED; |
+ result->reset(new InitResult); |
+ result->get()->index_data.main_table = NULL; |
- disabled_ = !rankings_.Init(this, new_eviction_); |
+ result->get()->index_data.index_bitmap = |
+ reinterpret_cast<IndexBitmap*>(big_index_header_->buffer()); |
+ result->get()->index_data.extra_table = |
+ reinterpret_cast<IndexBucket*>(big_extra_table_->buffer()); |
-#if defined(STRESS_CACHE_EXTENDED_VALIDATION) |
- trace_object_->EnableTracing(false); |
- int sc = SelfCheck(); |
- if (sc < 0 && sc != ERR_NUM_ENTRIES_MISMATCH) |
- NOTREACHED(); |
- trace_object_->EnableTracing(true); |
-#endif |
+ if (double_index) { |
+ result->get()->index_data.main_table = |
+ reinterpret_cast<IndexBucket*>(big_main_table_->buffer()); |
+ doubling_index_ = true; |
+ } |
- if (previous_crash) { |
- ReportError(ERR_PREVIOUS_CRASH); |
- } else if (!restarted_) { |
- ReportError(ERR_NO_ERROR); |
+ return ERR_NO_ERROR; |
+} |
+ |
+int BackendImplV3::Worker::GrowFiles(uint32 flags, |
+ scoped_ptr<InitResult>* result) { |
+ Trace("Worker::GrowFiles, flags 0x%x", flags); |
+ if (!init_) |
+ return ERR_OPERATION_FAILED; |
+ |
+ if (flags & WorkItem::WORK_COMPLETE) { |
+ block_files_.reset(); |
+ block_files_.swap(big_block_files_); |
+ return ERR_NO_ERROR; |
} |
- FlushIndex(); |
+ big_block_files_.reset(new BlockFiles(path_)); |
+ if (user_flags_ & BASIC_UNIT_TEST) |
+ big_block_files_->UseSmallSizeIncrementsForTest(); |
- return disabled_ ? net::ERR_FAILED : net::OK; |
+ if (!big_block_files_->Init(false, kFirstAdditionalBlockFileV3)) |
+ return ERR_INIT_FAILED; |
+ |
+ IndexHeaderV3* index = |
+ reinterpret_cast<IndexHeaderV3*>(index_header_->buffer()); |
+ |
+ result->reset(new InitResult); |
+ big_block_files_->GetBitmaps(index->max_block_file, |
+ &result->get()->block_bitmaps); |
+ index->max_block_file = static_cast<int>(result->get()->block_bitmaps.size()); |
+ return ERR_NO_ERROR; |
} |
-void BackendImpl::PrepareForRestart() { |
- // Reset the mask_ if it was not given by the user. |
- if (!(user_flags_ & kMask)) |
- mask_ = 0; |
+int BackendImplV3::Worker::Delete(Addr address) { |
+ if (address.is_block_file()) |
+ return ERR_OPERATION_FAILED; |
- if (!(user_flags_ & kNewEviction)) |
- new_eviction_ = false; |
+ if (DeleteCacheFile(GetFileName(address))) |
+ return ERR_NO_ERROR; |
- disabled_ = true; |
- data_->header.crash = 0; |
- index_->Flush(); |
- index_ = NULL; |
- data_ = NULL; |
- block_files_.CloseFiles(); |
- rankings_.Reset(); |
- init_ = false; |
- restarted_ = true; |
+ return ERR_OPERATION_FAILED; |
} |
-BackendImpl::~BackendImpl() { |
- if (user_flags_ & kNoRandom) { |
- // This is a unit test, so we want to be strict about not leaking entries |
- // and completing all the work. |
- background_queue_.WaitForPendingIO(); |
- } else { |
- // This is most likely not a test, so we want to do as little work as |
- // possible at this time, at the price of leaving dirty entries behind. |
- background_queue_.DropPendingIO(); |
- } |
+int BackendImplV3::Worker::Close(Addr address) { |
+ if (address.is_block_file()) |
+ return ERR_OPERATION_FAILED; |
- if (background_queue_.BackgroundIsCurrentThread()) { |
- // Unit tests may use the same thread for everything. |
- CleanupCache(); |
- } else { |
- background_queue_.background_thread()->PostTask( |
- FROM_HERE, base::Bind(&FinalCleanupCallback, base::Unretained(this))); |
- // http://crbug.com/74623 |
- base::ThreadRestrictions::ScopedAllowWait allow_wait; |
- done_.Wait(); |
+ FilesMap::iterator it = files_.find(address.value()); |
+ if (it != files_.end()) |
+ files_.erase(it); |
+ |
+ return ERR_NO_ERROR; |
+} |
+ |
+void BackendImplV3::Worker::OnDoWork(WorkItem* work_item) { |
+ if (work_item->type() == WorkItem::WORK_CLEANUP) |
+ return Cleanup(work_item); |
+ |
+ work_item->Start(this); |
+} |
+ |
+void BackendImplV3::Worker::DoneWithItem(WorkItem* work_item) { |
+ bool rv = main_thread_->PostTask(FROM_HERE, |
+ base::Bind(&WorkItem::OnDone, work_item)); |
+ DCHECK(rv); |
+} |
+ |
+File* BackendImplV3::Worker::GetBackingFile(Addr address, bool for_write) { |
+ disk_cache::File* file; |
+ if (address.is_separate_file()) |
+ file = GetExternalFile(address, for_write); |
+ else |
+ file = block_files_->GetFile(address); |
+ return file; |
+} |
+ |
+File* BackendImplV3::Worker::GetBackupIndexFile() { |
+ DCHECK(!index_backup_.get()); |
+ index_backup_ = new MappedFile(); |
+ index_backup_->set_force_creation(); |
+ if (!index_backup_->InitNoMap(path_.AppendASCII(kIndexBackupName))) { |
+ LOG(ERROR) << "Unable to open index_bak"; |
+ return NULL; |
} |
+ return index_backup_.get(); |
} |
-void BackendImpl::CleanupCache() { |
- Trace("Backend Cleanup"); |
- eviction_.Stop(); |
- timer_.reset(); |
+void BackendImplV3::Worker::CloseBackupIndexFile() { |
+ index_backup_ = NULL; |
+} |
+bool BackendImplV3::Worker::IsValid() { |
+ return init_; |
+} |
+ |
+// ------------------------------------------------------------------------ |
+ |
+BackendImplV3::Worker::~Worker() { |
+ if (cleanup_work_item_) |
+ main_thread_->PostTask(FROM_HERE, |
+ base::Bind(&WorkItem::OnDone, cleanup_work_item_)); |
+} |
+ |
+void BackendImplV3::Worker::Cleanup(WorkItem* work_item) { |
+ Trace("Worker::Cleanup"); |
+ if (!work_item->user_callback().is_null()) |
+ cleanup_work_item_ = work_item; |
+ |
if (init_) { |
- StoreStats(); |
- if (data_) |
- data_->header.crash = 0; |
+ IndexHeaderV3* index = |
+ reinterpret_cast<IndexHeaderV3*>(index_header_->buffer()); |
+ index->crash = 0; |
+ } |
- if (user_flags_ & kNoRandom) { |
- // This is a net_unittest, verify that we are not 'leaking' entries. |
- File::WaitForPendingIO(&num_pending_io_); |
- DCHECK(!num_refs_); |
- } else { |
- File::DropPendingIO(); |
- } |
+ CloseFiles(); |
+ init_ = false; |
+ |
+ if (work_item->user_callback().is_null()) { |
+ // This is the only message we don't return to the main thread, we are done |
+ // with the work item for good. |
+ work_item->Release(); |
} |
- block_files_.CloseFiles(); |
- FlushIndex(); |
- index_ = NULL; |
- ptr_factory_.InvalidateWeakPtrs(); |
- done_.Signal(); |
} |
-base::FilePath BackendImpl::GetFileName(Addr address) const { |
+void BackendImplV3::Worker::CloseFiles() { |
+ index_header_ = NULL; |
+ main_table_ = NULL; |
+ extra_table_ = NULL; |
+ index_backup_ = NULL; |
+ block_files_->CloseFiles(); |
+ files_.clear(); |
+ |
+ big_index_header_ = NULL; |
+ big_main_table_ = NULL; |
+ big_extra_table_ = NULL; |
+ big_extra_temp_table_ = NULL; |
+ if (big_block_files_.get()) |
+ big_block_files_->CloseFiles(); |
+} |
+ |
+File* BackendImplV3::Worker::GetExternalFile(Addr address, bool for_write) { |
+ FilesMap::iterator it = files_.find(address.value()); |
+ if (it != files_.end()) |
+ return it->second; |
+ |
+ scoped_refptr<disk_cache::File> file(new disk_cache::File(false)); |
+ if (for_write) |
+ file->set_force_creation(); |
+ if (file->Init(GetFileName(address))) |
+ files_[address.value()] = file.get(); |
+ else |
+ file = NULL; |
+ |
+ return file; |
+} |
+ |
+base::FilePath BackendImplV3::Worker::GetFileName(Addr address) const { |
if (!address.is_separate_file() || !address.is_initialized()) { |
NOTREACHED(); |
return base::FilePath(); |
@@ -312,25 +458,86 @@ |
// We just created a new file so we're going to write the header and set the |
// file length to include the hash table (zero filled). |
-bool BackendImpl::CreateBackingStore(disk_cache::File* file) { |
- AdjustMaxCacheSize(0); |
+bool BackendImplV3::Worker::CreateBackingStore(disk_cache::File* file) { |
+ IndexHeaderV3 header; |
+ memset(&header, 0, sizeof(header)); |
+ header.magic = kIndexMagicV3; |
+ header.version = kVersion3; |
+ header.max_block_file = kFirstAdditionalBlockFileV3; |
+ |
+ // Start with 12.5% of the size of the main table. |
+ int extra_len = (user_flags_ & BASIC_UNIT_TEST) ? 8 : kBaseTableLen / 8; |
+ header.table_len = kBaseTableLen + extra_len; |
+ header.max_bucket = kBaseTableLen / 4 - 1; |
+ header.flags = SMALL_CACHE; |
- IndexHeader header; |
- header.table_len = DesiredIndexTableLen(max_size_); |
- |
- // We need file version 2.1 for the new eviction algorithm. |
- if (new_eviction_) |
- header.version = 0x20001; |
- |
header.create_time = Time::Now().ToInternalValue(); |
+ header.base_time = (Time::Now() - TimeDelta::FromDays(20)).ToInternalValue(); |
if (!file->Write(&header, sizeof(header), 0)) |
return false; |
- return file->SetLength(GetIndexSize(header.table_len)); |
+ if (!file->SetLength(GetIndexBitmapSize(header.table_len))) |
+ return false; |
+ |
+ int flags = base::PLATFORM_FILE_READ | |
+ base::PLATFORM_FILE_WRITE | |
+ base::PLATFORM_FILE_CREATE | |
+ base::PLATFORM_FILE_EXCLUSIVE_WRITE; |
+ |
+ base::FilePath name = path_.AppendASCII(kIndexBackupName); |
+ scoped_refptr<disk_cache::File> file2(new disk_cache::File( |
+ base::CreatePlatformFile(name, flags, NULL, NULL))); |
+ |
+ if (!file2->IsValid()) |
+ return false; |
+ |
+ if (!file2->Write(&header, sizeof(header), 0)) |
+ return false; |
+ |
+ if (!file2->SetLength(GetIndexBitmapSize(header.table_len))) |
+ return false; |
+ |
+ name = path_.AppendASCII(kTable1Name); |
+ file2 = new disk_cache::File(base::CreatePlatformFile(name, flags, NULL, |
+ NULL)); |
+ if (!file2->IsValid()) |
+ return false; |
+ |
+ if (!file2->SetLength(kBaseTableLen * kBytesPerCell)) |
+ return false; |
+ |
+ name = path_.AppendASCII(kTable2Name); |
+ file2 = new disk_cache::File(base::CreatePlatformFile(name, flags, NULL, |
+ NULL)); |
+ if (!file2->IsValid()) |
+ return false; |
+ |
+ if (!file2->SetLength(extra_len * kBytesPerCell)) |
+ return false; |
+ |
+ return true; |
} |
-bool BackendImpl::InitBackingStore(bool* file_created) { |
+bool BackendImplV3::Worker::CreateExtraTable(int extra_len) { |
+ int flags = base::PLATFORM_FILE_READ | |
+ base::PLATFORM_FILE_WRITE | |
+ base::PLATFORM_FILE_CREATE | |
+ base::PLATFORM_FILE_EXCLUSIVE_WRITE; |
+ |
+ base::FilePath name = path_.AppendASCII(kTable2TempName); |
+ scoped_refptr<disk_cache::File> file(new disk_cache::File( |
+ base::CreatePlatformFile(name, flags, NULL, NULL))); |
+ if (!file->IsValid()) |
+ return false; |
+ |
+ if (!file->SetLength(extra_len * kBytesPerCell)) |
+ return false; |
+ |
+ return true; |
+} |
+ |
+bool BackendImplV3::Worker::InitBackingStore(bool* file_created) { |
if (!file_util::CreateDirectory(path_)) |
return false; |
@@ -348,124 +555,152 @@ |
bool ret = true; |
if (*file_created) |
- ret = CreateBackingStore(file.get()); |
+ ret = CreateBackingStore(file); |
file = NULL; |
if (!ret) |
return false; |
- index_ = new MappedFile(); |
- data_ = reinterpret_cast<Index*>(index_->Init(index_name, 0)); |
- if (!data_) { |
- LOG(ERROR) << "Unable to map Index file"; |
+ index_header_ = new MappedFile(); |
+ if (!index_header_->Init(index_name, 0)) { |
+ LOG(ERROR) << "Unable to map index"; |
return false; |
} |
- if (index_->GetLength() < sizeof(Index)) { |
+ if (index_header_->GetLength() < sizeof(IndexBitmap)) { |
// We verify this again on CheckIndex() but it's easier to make sure now |
// that the header is there. |
- LOG(ERROR) << "Corrupt Index file"; |
+ LOG(ERROR) << "Corrupt index file"; |
return false; |
} |
+ main_table_ = new MappedFile(); |
+ if (!main_table_->Init(path_.AppendASCII(kTable1Name), 0)) { |
+ LOG(ERROR) << "Unable to map index_tb1"; |
+ return false; |
+ } |
+ |
+ extra_table_ = new MappedFile(); |
+ if (!extra_table_->Init(path_.AppendASCII(kTable2Name), 0)) { |
+ LOG(ERROR) << "Unable to map index_tb2"; |
+ return false; |
+ } |
+ |
+ index_backup_ = new MappedFile(); |
+ if (!index_backup_->Init(path_.AppendASCII(kIndexBackupName), 0)) { |
+ LOG(ERROR) << "Unable to map index_bak"; |
+ return false; |
+ } |
+ |
return true; |
} |
-void BackendImpl::ReportError(int error) { |
- STRESS_DCHECK(!error || error == ERR_PREVIOUS_CRASH || |
- error == ERR_CACHE_CREATED); |
+bool BackendImplV3::Worker::LoadIndex(InitResult* init_result) { |
+ init_result->index_data.index_bitmap = |
+ reinterpret_cast<IndexBitmap*>(index_header_->buffer()); |
+ init_result->index_data.main_table = |
+ reinterpret_cast<IndexBucket*>(main_table_->buffer()); |
+ init_result->index_data.extra_table = |
+ reinterpret_cast<IndexBucket*>(extra_table_->buffer()); |
- // We transmit positive numbers, instead of direct error codes. |
- DCHECK_LE(error, 0); |
- CACHE_UMA(CACHE_ERROR, "Error", 0, error * -1); |
-} |
+ if (!CheckIndexFile(index_header_)) |
+ return false; |
+ if (!CheckIndexFile(index_backup_)) |
+ return false; |
-bool BackendImpl::CheckIndex() { |
- DCHECK(data_); |
+ IndexHeaderV3& header = init_result->index_data.index_bitmap->header; |
- size_t current_size = index_->GetLength(); |
- if (current_size < sizeof(Index)) { |
+ size_t extra_table_len = header.table_len % kBaseTableLen; |
+ size_t main_table_len = (kBaseTableLen - extra_table_len) * kBytesPerCell; |
+ extra_table_len *= kBytesPerCell; |
+ |
+ if (main_table_->GetLength() < main_table_len || |
+ extra_table_->GetLength() < extra_table_len) { |
+ LOG(ERROR) << "Truncated table"; |
+ return false; |
+ } |
+ |
+ IndexBitmap* index = reinterpret_cast<IndexBitmap*>(index_backup_->buffer()); |
+ |
+ init_result->index_data.backup_header.reset(new IndexHeaderV3); |
+ memcpy(init_result->index_data.backup_header.get(), &index->header, |
+ sizeof(index->header)); |
+ |
+ size_t bitmap_len = GetIndexBitmapSize(index->header.table_len) - |
+ sizeof(index->header); |
+ init_result->index_data.backup_bitmap.reset(new uint32[bitmap_len / 4]); |
+ memcpy(init_result->index_data.backup_bitmap.get(), &index->bitmap, |
+ bitmap_len); |
+ |
+ // Close the backup. |
+ index_backup_ = NULL; |
+ return true; |
+} |
+ |
+bool BackendImplV3::Worker::CheckIndexFile(MappedFile* file) { |
+ size_t current_size = file->GetLength(); |
+ if (current_size < sizeof(IndexBitmap)) { |
LOG(ERROR) << "Corrupt Index file"; |
return false; |
} |
- if (new_eviction_) { |
- // We support versions 2.0 and 2.1, upgrading 2.0 to 2.1. |
- if (kIndexMagic != data_->header.magic || |
- kCurrentVersion >> 16 != data_->header.version >> 16) { |
- LOG(ERROR) << "Invalid file version or magic"; |
- return false; |
- } |
- if (kCurrentVersion == data_->header.version) { |
- // We need file version 2.1 for the new eviction algorithm. |
- UpgradeTo2_1(); |
- } |
- } else { |
- if (kIndexMagic != data_->header.magic || |
- kCurrentVersion != data_->header.version) { |
- LOG(ERROR) << "Invalid file version or magic"; |
- return false; |
- } |
+ IndexHeaderV3* header = reinterpret_cast<IndexHeaderV3*>(file->buffer()); |
+ |
+ if (kIndexMagicV3 != header->magic || kVersion3 != header->version) { |
+ LOG(ERROR) << "Invalid file version or magic"; |
+ return false; |
} |
- if (!data_->header.table_len) { |
+ if (header->table_len <= 0 || header->table_len > 1 << 22) { |
LOG(ERROR) << "Invalid table size"; |
return false; |
} |
- if (current_size < GetIndexSize(data_->header.table_len) || |
- data_->header.table_len & (kBaseTableLen - 1)) { |
+ int min_mask = (user_flags_ & BASIC_UNIT_TEST) ? 0x3 : 0xff; |
+ if (current_size < GetIndexBitmapSize(header->table_len) || |
+ header->table_len & (min_mask)) { |
LOG(ERROR) << "Corrupt Index file"; |
return false; |
} |
- AdjustMaxCacheSize(data_->header.table_len); |
+ //AdjustMaxCacheSize(header->table_len); |
#if !defined(NET_BUILD_STRESS_CACHE) |
- if (data_->header.num_bytes < 0 || |
- (max_size_ < kint32max - kDefaultCacheSize && |
- data_->header.num_bytes > max_size_ + kDefaultCacheSize)) { |
- LOG(ERROR) << "Invalid cache (current) size"; |
+ if (header->num_bytes < 0 || header->max_bytes < 0 || |
+ header->num_bytes > header->max_bytes + kDefaultCacheSize) { |
+ LOG(ERROR) << "Invalid cache size"; |
return false; |
} |
#endif |
- if (data_->header.num_entries < 0) { |
+ if (header->num_entries < 0) { |
LOG(ERROR) << "Invalid number of entries"; |
return false; |
} |
- if (!mask_) |
- mask_ = data_->header.table_len - 1; |
- |
// Load the table into memory with a single read. |
- scoped_ptr<char[]> buf(new char[current_size]); |
- return index_->Read(buf.get(), current_size, 0); |
+ //scoped_array<char> buf(new char[current_size]); |
+ //return index_->Read(buf.get(), current_size, 0); |
+ |
+ return true; |
} |
-bool BackendImpl::InitStats() { |
- Addr address(data_->header.stats); |
- int size = stats_.StorageSize(); |
+bool BackendImplV3::Worker::InitStats(IndexHeaderV3* index, |
+ InitResult* result) { |
+ Addr address(index->stats); |
+ if (!address.is_initialized()) |
+ return true; |
- if (!address.is_initialized()) { |
- FileType file_type = Addr::RequiredFileType(size); |
- DCHECK_NE(file_type, EXTERNAL); |
- int num_blocks = Addr::RequiredBlocks(size, file_type); |
- |
- if (!CreateBlock(file_type, num_blocks, &address)) |
- return false; |
- return stats_.Init(NULL, 0, address); |
- } |
- |
if (!address.is_block_file()) { |
NOTREACHED(); |
return false; |
} |
+ int size = address.num_blocks() * address.BlockSize(); |
+ |
// Load the required data. |
- size = address.num_blocks() * address.BlockSize(); |
- MappedFile* file = File(address); |
+ MappedFile* file = GetMappedFile(address); |
if (!file) |
return false; |
@@ -475,11 +710,22 @@ |
if (!file->Read(data.get(), size, offset)) |
return false; |
- if (!stats_.Init(data.get(), size, address)) |
- return false; |
- if (cache_type_ == net::DISK_CACHE && ShouldReportAgain()) |
- stats_.InitSizeHistogram(); |
+ result->stats_data = data.Pass(); |
return true; |
} |
+int BackendImplV3::Worker::GrowDone() { |
+ Trace("Worker::GrowDone"); |
+ if (!init_) |
+ return ERR_OPERATION_FAILED; |
+ |
+ DCHECK(doubling_index_); |
+ doubling_index_ = false; |
+ |
+ extra_table_ = big_extra_temp_table_; |
+ big_extra_temp_table_ = NULL; |
+ |
+ return ERR_NO_ERROR; |
+} |
+ |
} // namespace disk_cache |