| 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].group() == 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].group() == 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].group() == 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
|
|
|
|
|