Index: net/disk_cache/v3/backend_work_item.cc |
=================================================================== |
--- net/disk_cache/v3/backend_work_item.cc (revision 0) |
+++ net/disk_cache/v3/backend_work_item.cc (revision 0) |
@@ -0,0 +1,501 @@ |
+// 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/v3/backend_work_item.h" |
+ |
+#include "base/hash.h" |
+#include "base/sha1.h" |
+#include "net/base/net_errors.h" |
+#include "net/disk_cache/errors.h" |
+#include "net/disk_cache/file.h" |
+#include "net/disk_cache/storage_block-inl.h" |
+#include "net/disk_cache/v3/backend_worker.h" |
+#include "net/disk_cache/v3/entry_impl_v3.h" |
+ |
+namespace { |
+ |
+// Simple adaptor for the sha1 interface. |
+void ComputeCryptoHash(const std::string& source, |
+ disk_cache::ShortEntryRecord* record) { |
+ DCHECK(!source.empty()); |
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(source.data()), |
+ source.size(), |
+ reinterpret_cast<unsigned char*>(record->long_hash)); |
+} |
+ |
+bool CryptoHashMatches(const std::string& source, |
+ const disk_cache::ShortEntryRecord& record) { |
+ DCHECK(!source.empty()); |
+ unsigned char result[20]; |
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(source.data()), |
+ source.size(), result); |
+ |
+ return !memcmp(result, record.long_hash, sizeof(result)); |
+} |
+ |
+} // namespace |
+ |
+// ------------------------------------------------------------------------ |
+ |
+namespace disk_cache { |
+ |
+class BackendImplV3::IOCallback : public disk_cache::FileIOCallback { |
+ public: |
+ explicit IOCallback(BackendImplV3::WorkItem* work_item); |
+ virtual ~IOCallback() {} |
+ |
+ // FileIOCallback implementation. |
+ virtual void OnFileIOComplete(int bytes_copied) OVERRIDE; |
+ void Discard(); |
+ |
+ private: |
+ // Even though we need the work item alive until the operation completes, we |
+ // don't grab an extra reference because the work item itself keeps an extra |
+ // reference until it reaches the main thread again. |
+ BackendImplV3::WorkItem* work_item_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(IOCallback); |
+}; |
+ |
+BackendImplV3::IOCallback::IOCallback(BackendImplV3::WorkItem* work_item) |
+ : work_item_(work_item) { |
+} |
+ |
+void BackendImplV3::IOCallback::OnFileIOComplete(int bytes_copied) { |
+ if (work_item_) |
+ work_item_->DoLoop(bytes_copied); |
+ |
+ delete this; |
+} |
+ |
+void BackendImplV3::IOCallback::Discard() { |
+ work_item_ = NULL; |
+ OnFileIOComplete(0); |
+} |
+ |
+// ------------------------------------------------------------------------ |
+ |
+BackendImplV3::WorkItem::WorkItem(WorkType type) |
+ : type_(type), |
+ result_(0), |
+ flags_(0) {//init everything |
+} |
+ |
+void BackendImplV3::WorkItem::Start(BackendImplV3::Worker* worker) { |
+ Trace("Work 0x%p %d", this, type()); |
+ worker_ = worker; |
+ switch (type_) { |
+ case WORK_INIT: |
+ return CompleteItem(worker_->Init(flags_, &init_result_)); |
+ case WORK_RESTART: |
+ return CompleteItem(worker_->Restart(flags_, &init_result_)); |
+ case WORK_GROW_INDEX: |
+ return CompleteItem(worker_->GrowIndex(flags_, &init_result_)); |
+ case WORK_GROW_FILES: |
+ return CompleteItem(worker_->GrowFiles(flags_, &init_result_)); |
+ case WORK_WRITE_INDEX: |
+ next_state_ = STATE_WRITE_DATA; |
+ break; |
+ case WORK_OPEN_ENTRY: |
+ next_state_ = STATE_OPEN_ENTRY; |
+ break; |
+ case WORK_READ_DATA: |
+ next_state_ = STATE_READ_DATA; |
+ break; |
+ case WORK_WRITE_DATA: |
+ next_state_ = STATE_WRITE_DATA; |
+ break; |
+ case WORK_MOVE_DATA: |
+ next_state_ = STATE_MOVE_DATA; |
+ break; |
+ case WORK_TRUNCATE: |
+ next_state_ = STATE_TRUNCATE_DATA; |
+ break; |
+ case WORK_DELETE: |
+ return CompleteItem(worker_->Delete(address_)); |
+ case WORK_CLOSE: |
+ return CompleteItem(worker_->Close(address_)); |
+ case WORK_NONE: |
+ return CompleteItem(ERR_NO_ERROR); |
+ default: NOTREACHED(); |
+ } |
+ DoLoop(ERR_NO_ERROR); |
+} |
+ |
+void BackendImplV3::WorkItem::DoLoop(int result) { |
+ DCHECK(next_state_ != STATE_NONE); |
+ if (!worker_->IsValid()) |
+ return CompleteItem(result); |
+ |
+ int rv = result; |
+ do { |
+ State state = next_state_; |
+ next_state_ = STATE_NONE; |
+ switch (state) { |
+ case STATE_OPEN_ENTRY: |
+ DCHECK_EQ(ERR_NO_ERROR, rv); |
+ rv = DoOpenEntry(); |
+ break; |
+ case STATE_OPEN_ENTRY_COMPLETE: |
+ rv = DoOpenEntryComplete(rv); |
+ break; |
+ case STATE_READ_KEY: |
+ DCHECK_EQ(ERR_NO_ERROR, rv); |
+ rv = DoReadKey(); |
+ break; |
+ case STATE_READ_KEY_COMPLETE: |
+ DCHECK_EQ(ERR_NO_ERROR, rv); |
+ rv = DoReadKeyComplete(); |
+ break; |
+ case STATE_READ_DATA: |
+ DCHECK_EQ(ERR_NO_ERROR, rv); |
+ rv = DoReadData(); |
+ break; |
+ case STATE_READ_DATA_COMPLETE: |
+ rv = DoReadDataComplete(rv); |
+ break; |
+ case STATE_WRITE_DATA: |
+ DCHECK_EQ(ERR_NO_ERROR, rv); |
+ rv = DoWriteData(); |
+ break; |
+ case STATE_WRITE_DATA_COMPLETE: |
+ rv = DoWriteDataComplete(rv); |
+ break; |
+ case STATE_MOVE_DATA: |
+ DCHECK_EQ(ERR_NO_ERROR, rv); |
+ rv = DoMoveData(); |
+ break; |
+ case STATE_TRUNCATE_DATA: |
+ DCHECK_EQ(ERR_NO_ERROR, rv); |
+ rv = DoTruncateData(); |
+ break; |
+ case STATE_COPY_ENTRY: |
+ DCHECK_EQ(ERR_NO_ERROR, rv); |
+ rv = DoCopyEntry(); |
+ break; |
+ case STATE_COPY_ENTRY_COMPLETE: |
+ rv = DoCopyEntryComplete(rv); |
+ break; |
+ } |
+ } while (rv != ERR_PENDING && next_state_ != STATE_NONE); |
+ |
+ if (rv != ERR_PENDING) |
+ CompleteItem(rv); |
+} |
+ |
+void BackendImplV3::WorkItem::OnDone() { |
+ closure_.Run(this); |
+} |
+ |
+// ------------------------------------------------------------------------ |
+ |
+BackendImplV3::WorkItem::~WorkItem() { |
+} |
+ |
+void BackendImplV3::WorkItem::CompleteItem(int result) { |
+ Trace("Work done 0x%p %d %d", this, type(), result); |
+ result_ = result; |
+ entry_block_.reset(); // Release resources while on the worker thread. |
+ worker_->DoneWithItem(this); |
+} |
+ |
+int BackendImplV3::WorkItem::DoOpenEntry() { |
+ next_state_ = STATE_OPEN_ENTRY_COMPLETE; |
+ for (; entries_.current < entries_.cells.size(); entries_.current++) { |
+ Addr address = entries_.cells[entries_.current].GetAddress(); |
+ if (entries_.cells[entries_.current].GetGroup() == ENTRY_EVICTED) { |
+ if (flags_ & WORK_FOR_RESURRECT) |
+ return LoadShortEntryBlock(address); |
+ continue; |
+ } |
+ |
+ if (flags_ & WORK_FOR_RESURRECT) |
+ continue; |
+ return LoadEntryBlock(address); |
+ } |
+ next_state_ = STATE_NONE; |
+ return ERR_OPERATION_FAILED; |
+} |
+ |
+int BackendImplV3::WorkItem::DoOpenEntryComplete(int result) { |
+ if (entries_.cells[entries_.current].GetGroup() == ENTRY_EVICTED) { |
+ if (result != static_cast<int>(sizeof(ShortEntryRecord))) |
+ return ERR_READ_FAILURE; |
+ |
+ if (!EntryImplV3::DeletedSanityCheck(*short_entry_block_->Data())) |
+ return ERR_INVALID_ENTRY; |
+ } else { |
+ if (result != static_cast<int>(sizeof(EntryRecord))) |
+ return ERR_READ_FAILURE; |
+ |
+ if (!EntryImplV3::BasicSanityCheck(*entry_block_->Data())) |
+ return ERR_INVALID_ENTRY; |
+ } |
+ |
+ if (entries_.cells[entries_.current].GetGroup() == ENTRY_EVICTED) { |
+ DCHECK(!(flags_ & WORK_FOR_EVICT)); |
+ if (CryptoHashMatches(key_, *short_entry_block_->Data())) { |
+ // We have a match. |
+ short_entry_record_.reset(short_entry_block_->ReleaseData()); |
+ return ERR_NO_ERROR; |
+ } |
+ } else { |
+ next_state_ = STATE_READ_KEY; |
+ return ERR_NO_ERROR; |
+ } |
+ |
+ next_state_ = STATE_OPEN_ENTRY; |
+ entries_.current++; |
+ return ERR_NO_ERROR; |
+} |
+ |
+int BackendImplV3::WorkItem::DoReadKey() { |
+ address_.set_value(entry_block_->Data()->data_addr[0]); |
+ offset_ = 0; |
+ buffer_ = new net::IOBuffer(entry_block_->Data()->key_len); |
+ buffer_len_ = entry_block_->Data()->key_len; |
+ |
+ next_state_ = STATE_READ_DATA; |
+ return ERR_NO_ERROR; |
+} |
+ |
+int BackendImplV3::WorkItem::DoReadKeyComplete() { |
+ std::string key(buffer_->data(), buffer_len_); |
+ uint32 hash = base::Hash(key); |
+ Trace("DoReadKeyComplete hash 0x%x, 0x%p", hash, this); |
+ DCHECK_EQ(hash, entries_.cells[entries_.current].hash()); |
+ if (flags() & WORK_FOR_ITERATION) |
+ key_ = key; |
+ |
+ if (flags_ & WORK_FOR_EVICT) { |
+ key_ = key; |
+ if (!(flags_ & WORK_NO_COPY)) { |
+ next_state_ = STATE_COPY_ENTRY; |
+ return ERR_NO_ERROR; |
+ } |
+ } |
+ |
+ if (key == key_) { |
+ // We have a match. |
+ entry_record_.reset(entry_block_->ReleaseData()); |
+ return ERR_NO_ERROR; |
+ } |
+ |
+ next_state_ = STATE_OPEN_ENTRY; |
+ entries_.current++; |
+ return ERR_NO_ERROR; |
+} |
+ |
+int BackendImplV3::WorkItem::DoReadData() { |
+ disk_cache::File* file = worker_->GetBackingFile(address_, false); |
+ if (!file) |
+ return net::ERR_FILE_NOT_FOUND; |
+ |
+ |
+ size_t file_offset = offset_; |
+ if (address_.is_block_file()) { |
+ DCHECK_LE(offset_ + buffer_len_, kMaxBlockSize); |
+ file_offset += address_.start_block() * address_.BlockSize(); |
+ } |
+ DCHECK(buffer_len_); |
+ |
+ bool completed; |
+ IOCallback* callback = new IOCallback(this); |
+ |
+ if (!file->Read(buffer_->data(), buffer_len_, file_offset, callback, |
+ &completed)) { |
+ callback->Discard(); |
+ return net::ERR_CACHE_READ_FAILURE; |
+ } |
+ next_state_ = STATE_READ_DATA_COMPLETE; |
+ |
+ if (completed) { |
+ callback->Discard(); |
+ return NO_ERROR; |
+ } |
+ |
+ return ERR_PENDING; |
+} |
+ |
+int BackendImplV3::WorkItem::DoReadDataComplete(int result) { |
+ if (result != buffer_len_) |
+ return net::ERR_CACHE_READ_FAILURE; |
+ |
+ if (type() == WORK_OPEN_ENTRY) { |
+ next_state_ = STATE_READ_KEY_COMPLETE; |
+ return ERR_NO_ERROR; |
+ } |
+ |
+ return result; |
+} |
+ |
+int BackendImplV3::WorkItem::DoWriteData() { |
+ disk_cache::File* file = NULL; |
+ if (type_ == WORK_WRITE_INDEX) |
+ file = worker_->GetBackupIndexFile(); |
+ else |
+ file = worker_->GetBackingFile(address_, true); |
+ |
+ if (!file) |
+ return net::ERR_CACHE_WRITE_FAILURE; |
+ |
+ DCHECK(buffer_len_); |
+ |
+ size_t file_offset = offset_; |
+ if (type_ != WORK_WRITE_INDEX && address_.is_block_file()) { |
+ DCHECK_LE(offset_ + buffer_len_, kMaxBlockSize); |
+ file_offset += address_.start_block() * address_.BlockSize(); |
+ } |
+ |
+ bool completed; |
+ IOCallback* callback = new IOCallback(this); |
+ |
+ if (!file->Write(buffer_->data(), buffer_len_, file_offset, callback, |
+ &completed)) { |
+ callback->Discard(); |
+ return net::ERR_CACHE_WRITE_FAILURE; |
+ } |
+ next_state_ = STATE_WRITE_DATA_COMPLETE; |
+ |
+ if (completed) { |
+ callback->Discard(); |
+ return NO_ERROR; |
+ } |
+ |
+ return ERR_PENDING; |
+} |
+ |
+int BackendImplV3::WorkItem::DoWriteDataComplete(int result) { |
+ if (type_ == WORK_WRITE_INDEX) |
+ worker_->CloseBackupIndexFile(); |
+ |
+ if (result != buffer_len_) |
+ return net::ERR_CACHE_WRITE_FAILURE; |
+ |
+ return result; |
+} |
+ |
+int BackendImplV3::WorkItem::DoMoveData() { |
+ disk_cache::File* file = worker_->GetBackingFile(address_, false); |
+ if (!file) |
+ return ERR_OPERATION_FAILED; |
+ |
+ DCHECK(buffer_len_); |
+ |
+ if (!address_.is_block_file()) { |
+ NOTREACHED(); |
+ return ERR_OPERATION_FAILED; |
+ } |
+ |
+ offset_ = 0; |
+ size_t file_offset = 0; |
+ DCHECK_LE(buffer_len_, kMaxBlockSize); |
+ file_offset += address_.start_block() * address_.BlockSize(); |
+ |
+ buffer_ = new net::IOBufferWithSize(buffer_len_); |
+ |
+ // We could optimize this to be an async read, but we'd have to do that from |
+ // the main thread, not from here. |
+ if (!file->Read(buffer_->data(), buffer_len_, file_offset)) |
+ return ERR_WRITE_FAILURE; |
+ |
+ address_ = address2_; |
+ next_state_ = STATE_WRITE_DATA; |
+ return NO_ERROR; |
+} |
+ |
+int BackendImplV3::WorkItem::DoTruncateData() { |
+ if (address_.is_block_file()) |
+ return ERR_OPERATION_FAILED; |
+ |
+ disk_cache::File* file = worker_->GetBackingFile(address_, false); |
+ if (!file) |
+ return ERR_OPERATION_FAILED; |
+ |
+ if (!file->SetLength(offset_)) |
+ return ERR_OPERATION_FAILED; |
+ |
+ return ERR_NO_ERROR; |
+} |
+ |
+int BackendImplV3::WorkItem::DoCopyEntry() { |
+ next_state_ = STATE_COPY_ENTRY_COMPLETE; |
+ Addr address = entries_.cells[1].GetAddress(); |
+ short_entry_block_.reset( |
+ new CacheShortEntryBlock(worker_->GetMappedFile(address), address)); |
+ |
+ ShortEntryRecord* short_record = short_entry_block_->Data(); |
+ EntryRecord* long_record = entry_block_->Data(); |
+ |
+ short_record->hash = long_record->hash; |
+ short_record->reuse_count = long_record->reuse_count; |
+ short_record->refetch_count = long_record->refetch_count; |
+ short_record->refetch_count = long_record->refetch_count; |
+ short_record->key_len = long_record->key_len; |
+ short_record->last_access_time = long_record->last_access_time; |
+ ComputeCryptoHash(key_, short_record); |
+ |
+ bool completed; |
+ IOCallback* callback = new IOCallback(this); |
+ if (!short_entry_block_->Store(callback, &completed)) { |
+ callback->Discard(); |
+ next_state_ = STATE_NONE; |
+ return ERR_OPERATION_FAILED; |
+ } |
+ |
+ if (completed) { |
+ callback->Discard(); |
+ return ERR_NO_ERROR; |
+ } |
+ |
+ return ERR_PENDING; |
+} |
+ |
+int BackendImplV3::WorkItem::DoCopyEntryComplete(int result) { |
+ if (result != static_cast<int>(sizeof(ShortEntryRecord))) |
+ return ERR_READ_FAILURE; |
+ |
+ entry_record_.reset(entry_block_->ReleaseData()); |
+ return ERR_NO_ERROR; |
+} |
+ |
+int BackendImplV3::WorkItem::LoadEntryBlock(Addr address) { |
+ entry_block_.reset(new CacheEntryBlockV3(worker_->GetMappedFile(address), |
+ address)); |
+ bool completed; |
+ IOCallback* callback = new IOCallback(this); |
+ if (!entry_block_->Load(callback, &completed)) { |
+ callback->Discard(); |
+ next_state_ = STATE_NONE; |
+ return ERR_OPERATION_FAILED; |
+ } |
+ |
+ if (completed) { |
+ callback->Discard(); |
+ return ERR_NO_ERROR; |
+ } |
+ |
+ return ERR_PENDING; |
+} |
+ |
+int BackendImplV3::WorkItem::LoadShortEntryBlock(Addr address) { |
+ short_entry_block_.reset( |
+ new CacheShortEntryBlock(worker_->GetMappedFile(address), address)); |
+ bool completed; |
+ IOCallback* callback = new IOCallback(this); |
+ if (!short_entry_block_->Load(callback, &completed)) { |
+ callback->Discard(); |
+ next_state_ = STATE_NONE; |
+ return ERR_OPERATION_FAILED; |
+ } |
+ |
+ if (completed) { |
+ callback->Discard(); |
+ return ERR_NO_ERROR; |
+ } |
+ |
+ return ERR_PENDING; |
+} |
+ |
+} // namespace disk_cache |
Property changes on: net\disk_cache\v3\backend_work_item.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |