| Index: net/http/http_cache.cc
|
| diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
|
| index 099412a8d1444a95c37dfc6c6dbe3328e1bb540f..720028609d63864338b5a0d02f4c0dc40a3fcd47 100644
|
| --- a/net/http/http_cache.cc
|
| +++ b/net/http/http_cache.cc
|
| @@ -199,7 +199,7 @@ class HttpCache::MetadataWriter {
|
| buf_len_(0) {
|
| }
|
|
|
| - ~MetadataWriter() {}
|
| + ~MetadataWriter() { transaction_->Orphan(std::move(transaction_)); }
|
|
|
| // Implements the bulk of HttpCache::WriteMetadata.
|
| void Write(const GURL& url,
|
| @@ -338,7 +338,8 @@ HttpCache::~HttpCache() {
|
| entry->will_process_pending_queue = false;
|
| entry->pending_queue.clear();
|
| entry->readers.clear();
|
| - entry->writer = NULL;
|
| + entry->writer = nullptr;
|
| + entry->shared_writers.reset();
|
| DeactivateEntry(entry);
|
| }
|
|
|
| @@ -581,8 +582,8 @@ int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
|
| entry_ptr->disk_entry->Doom();
|
| entry_ptr->doomed = true;
|
|
|
| - DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() ||
|
| - entry_ptr->will_process_pending_queue);
|
| + DCHECK(entry_ptr->writer || entry_ptr->shared_writers ||
|
| + !entry_ptr->readers.empty() || entry_ptr->will_process_pending_queue);
|
| return OK;
|
| }
|
|
|
| @@ -629,7 +630,7 @@ void HttpCache::DoomMainEntryForUrl(const GURL& url) {
|
|
|
| void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
|
| DCHECK(entry->doomed);
|
| - DCHECK(!entry->writer);
|
| + DCHECK(!entry->writer && !entry->shared_writers);
|
| DCHECK(entry->readers.empty());
|
| DCHECK(entry->pending_queue.empty());
|
|
|
| @@ -654,7 +655,7 @@ HttpCache::ActiveEntry* HttpCache::ActivateEntry(
|
| void HttpCache::DeactivateEntry(ActiveEntry* entry) {
|
| DCHECK(!entry->will_process_pending_queue);
|
| DCHECK(!entry->doomed);
|
| - DCHECK(!entry->writer);
|
| + DCHECK(!entry->writer && !entry->shared_writers);
|
| DCHECK(entry->disk_entry);
|
| DCHECK(entry->readers.empty());
|
| DCHECK(entry->pending_queue.empty());
|
| @@ -788,20 +789,37 @@ int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
|
| DCHECK(entry);
|
| DCHECK(entry->disk_entry);
|
|
|
| - // We implement a basic reader/writer lock for the disk cache entry. If
|
| - // there is already a writer, then everyone has to wait for the writer to
|
| - // finish before they can access the cache entry. There can be multiple
|
| - // readers.
|
| + // If a writer is in the validation stage, all other transactions need to wait
|
| + // before they can access the cache entry. Once the validation stage is over
|
| + // and if the writer creates a SharedWriters object, other eligible
|
| + // transactions will become equally privileged to be able to read from the
|
| + // network and write to the cache.
|
| //
|
| // NOTE: If the transaction can only write, then the entry should not be in
|
| // use (since any existing entry should have already been doomed).
|
| -
|
| if (entry->writer || entry->will_process_pending_queue) {
|
| entry->pending_queue.push_back(trans);
|
| return ERR_IO_PENDING;
|
| }
|
|
|
| - if (trans->mode() & Transaction::WRITE) {
|
| + if (entry->shared_writers) {
|
| + DCHECK(!entry->writer);
|
| + if (!trans->IsEligibleForSharedWriting()) {
|
| + entry->pending_queue.push_back(trans);
|
| + return ERR_IO_PENDING;
|
| + } else {
|
| + if (entry->shared_writers->AddTransaction(trans)) {
|
| + return OK;
|
| + } else {
|
| + // Another transaction is in the process of validation, wait for it and
|
| + // any other transactions already in the queue to complete.
|
| + return ERR_IO_PENDING;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // No SharedWriters.
|
| + else if (trans->mode() & Transaction::WRITE) {
|
| // transaction needs exclusive access to the entry
|
| if (entry->readers.empty()) {
|
| entry->writer = trans;
|
| @@ -811,7 +829,7 @@ int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
|
| }
|
| } else {
|
| // transaction needs read access to the entry
|
| - entry->readers.push_back(trans);
|
| + entry->readers.insert(trans);
|
| }
|
|
|
| // We do this before calling EntryAvailable to force any further calls to
|
| @@ -825,6 +843,10 @@ int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
|
|
|
| void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
|
| bool cancel) {
|
| + // This is not expected to be called for shared writing transactions which
|
| + // have completed their Start state machine.
|
| + DCHECK(!trans->shared());
|
| +
|
| // If we already posted a task to move on to the next transaction and this was
|
| // the writer, there is nothing to cancel.
|
| if (entry->will_process_pending_queue && entry->readers.empty())
|
| @@ -832,7 +854,6 @@ void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
|
|
|
| if (entry->writer) {
|
| DCHECK(trans == entry->writer);
|
| -
|
| // Assume there was a failure.
|
| bool success = false;
|
| if (cancel) {
|
| @@ -852,35 +873,41 @@ void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
|
|
|
| void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
|
| DCHECK(entry->readers.empty());
|
| + DCHECK(!entry->shared_writers);
|
|
|
| entry->writer = NULL;
|
|
|
| if (success) {
|
| ProcessPendingQueue(entry);
|
| } else {
|
| - DCHECK(!entry->will_process_pending_queue);
|
| + DestroyEntryAndRestartPendingQueueTxns(entry);
|
| + }
|
| +}
|
|
|
| - // We failed to create this entry.
|
| - TransactionList pending_queue;
|
| - pending_queue.swap(entry->pending_queue);
|
| +void HttpCache::DestroyEntryAndRestartPendingQueueTxns(ActiveEntry* entry) {
|
| + DCHECK(!entry->will_process_pending_queue);
|
|
|
| - entry->disk_entry->Doom();
|
| - DestroyEntry(entry);
|
| + // We failed to create this entry.
|
| + TransactionList pending_queue;
|
| + pending_queue.swap(entry->pending_queue);
|
|
|
| - // We need to do something about these pending entries, which now need to
|
| - // be added to a new entry.
|
| - while (!pending_queue.empty()) {
|
| - // ERR_CACHE_RACE causes the transaction to restart the whole process.
|
| - pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
|
| - pending_queue.pop_front();
|
| - }
|
| + entry->disk_entry->Doom();
|
| +
|
| + DestroyEntry(entry);
|
| +
|
| + // We need to do something about these pending entries, which now need to
|
| + // be added to a new entry.
|
| + while (!pending_queue.empty()) {
|
| + // ERR_CACHE_RACE causes the transaction to restart the whole process.
|
| + pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
|
| + pending_queue.pop_front();
|
| }
|
| }
|
|
|
| void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
|
| - DCHECK(!entry->writer);
|
| + DCHECK(!entry->writer && !entry->shared_writers);
|
|
|
| - auto it = std::find(entry->readers.begin(), entry->readers.end(), trans);
|
| + auto it = entry->readers.find(trans);
|
| DCHECK(it != entry->readers.end());
|
|
|
| entry->readers.erase(it);
|
| @@ -889,14 +916,13 @@ void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
|
| }
|
|
|
| void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
|
| - DCHECK(entry->writer);
|
| DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
|
| DCHECK(entry->readers.empty());
|
|
|
| Transaction* trans = entry->writer;
|
|
|
| entry->writer = NULL;
|
| - entry->readers.push_back(trans);
|
| + entry->readers.insert(trans);
|
|
|
| ProcessPendingQueue(entry);
|
| }
|
| @@ -911,12 +937,19 @@ LoadState HttpCache::GetLoadStateForPendingTransaction(
|
| }
|
|
|
| Transaction* writer = i->second->writer;
|
| - return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
|
| + if (writer)
|
| + return writer->GetWriterLoadState();
|
| +
|
| + if (i->second->shared_writers)
|
| + return i->second->shared_writers->GetLoadState();
|
| +
|
| + return LOAD_STATE_WAITING_FOR_CACHE;
|
| }
|
|
|
| void HttpCache::RemovePendingTransaction(Transaction* trans) {
|
| - auto i = active_entries_.find(trans->key());
|
| bool found = false;
|
| +
|
| + auto i = active_entries_.find(trans->key());
|
| if (i != active_entries_.end())
|
| found = RemovePendingTransactionFromEntry(i->second.get(), trans);
|
|
|
| @@ -949,14 +982,16 @@ void HttpCache::RemovePendingTransaction(Transaction* trans) {
|
|
|
| bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
|
| Transaction* trans) {
|
| - TransactionList& pending_queue = entry->pending_queue;
|
| -
|
| - auto j = find(pending_queue.begin(), pending_queue.end(), trans);
|
| - if (j == pending_queue.end())
|
| - return false;
|
| -
|
| - pending_queue.erase(j);
|
| - return true;
|
| + if (trans->shared()) {
|
| + return entry->shared_writers->RemoveWaitingTransaction(trans);
|
| + } else {
|
| + TransactionList& pending_queue = entry->pending_queue;
|
| + auto j = find(pending_queue.begin(), pending_queue.end(), trans);
|
| + if (j == pending_queue.end())
|
| + return false;
|
| + pending_queue.erase(j);
|
| + return true;
|
| + }
|
| }
|
|
|
| bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
|
| @@ -992,7 +1027,7 @@ void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
|
|
|
| void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
|
| entry->will_process_pending_queue = false;
|
| - DCHECK(!entry->writer);
|
| + DCHECK(!entry->writer && !entry->shared_writers);
|
|
|
| // If no one is interested in this entry, then we can deactivate it.
|
| if (entry->pending_queue.empty()) {
|
| @@ -1003,6 +1038,7 @@ void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
|
|
|
| // Promote next transaction from the pending queue.
|
| Transaction* next = entry->pending_queue.front();
|
| +
|
| if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
|
| return; // Have to wait.
|
|
|
| @@ -1157,4 +1193,671 @@ void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
|
| item->NotifyTransaction(result, NULL);
|
| }
|
|
|
| +void HttpCache::CreateSharedWriters(
|
| + Transaction* cache_trans,
|
| + std::unique_ptr<HttpTransaction> network_trans,
|
| + RequestPriority priority) {
|
| + ActiveEntry* entry = cache_trans->entry();
|
| + DCHECK(!entry->shared_writers);
|
| + DCHECK_EQ(entry->writer, cache_trans);
|
| + DCHECK(entry->readers.empty());
|
| + entry->shared_writers.reset(new HttpCache::SharedWriters(
|
| + this, entry, cache_trans, priority, std::move(network_trans)));
|
| +
|
| + // entry->writer should no longer exist as it is moved to
|
| + // shared_writers.
|
| + entry->writer = nullptr;
|
| +
|
| + // Add the eligible transactions to waiting_for_validation_ and process the
|
| + // first among them. Post this, pending_queue will only contain transactions
|
| + // that are not eligible for shared writing.
|
| + entry->shared_writers->MoveFromPendingQueue();
|
| + entry->shared_writers->ProcessFirstWaitingValidation();
|
| +}
|
| +
|
| +HttpCache::SharedWriters::SharedWriters(
|
| + HttpCache* cache,
|
| + ActiveEntry* entry,
|
| + Transaction* cache_trans,
|
| + RequestPriority priority,
|
| + std::unique_ptr<HttpTransaction> transaction)
|
| + : cache_(cache->GetWeakPtr()),
|
| + entry_(entry),
|
| + current_writer_(nullptr),
|
| + validating_trans_(nullptr),
|
| + priority_(priority),
|
| + destroy_entry_(false),
|
| + weak_factory_(this) {
|
| + cache_trans->SetShared();
|
| + all_writers_.insert(cache_trans);
|
| + network_trans_ = std::move(transaction);
|
| +}
|
| +
|
| +HttpCache::SharedWriters::~SharedWriters() {
|
| + weak_factory_.InvalidateWeakPtrs();
|
| +}
|
| +
|
| +std::unique_ptr<HttpTransaction> HttpCache::StopCachingSharedWriters(
|
| + Transaction* transaction,
|
| + ActiveEntry* entry) {
|
| + auto network_trans = entry->shared_writers->StopCaching(transaction);
|
| +
|
| + if (network_trans) { // stopped
|
| + DCHECK(entry->shared_writers->empty());
|
| + DCHECK_EQ(entry->writer, transaction);
|
| + entry->shared_writers.reset();
|
| + }
|
| +
|
| + return network_trans;
|
| +}
|
| +
|
| +std::unique_ptr<HttpTransaction> HttpCache::SharedWriters::StopCaching(
|
| + Transaction* transaction) {
|
| + bool result = false;
|
| + if (transaction == validating_trans_) {
|
| + if (all_writers_.empty()) {
|
| + result = true;
|
| + validating_trans_ = nullptr;
|
| + }
|
| + } else if (all_writers_.size() == 1 && all_writers_.count(transaction) &&
|
| + !validating_trans_) {
|
| + if (current_writer_ == transaction) {
|
| + current_writer_ = nullptr;
|
| + }
|
| + all_writers_.erase(transaction);
|
| + result = true;
|
| + }
|
| + if (result) {
|
| + transaction->ResetShared(true);
|
| + entry_->writer = transaction;
|
| + MoveToPendingQueue();
|
| + return std::move(network_trans_);
|
| + }
|
| + return std::unique_ptr<HttpTransaction>();
|
| +}
|
| +
|
| +HttpTransaction* HttpCache::CacheWriteFailedSharedWriters(Transaction* trans,
|
| + ActiveEntry* entry) {
|
| + HttpTransaction* network_trans =
|
| + entry->shared_writers->OnCacheWriteFailure(trans);
|
| +
|
| + if (entry->shared_writers->empty()) {
|
| + entry->shared_writers.reset();
|
| + DestroyEntryAndRestartPendingQueueTxns(entry);
|
| + }
|
| + return network_trans;
|
| +}
|
| +
|
| +HttpTransaction* HttpCache::SharedWriters::OnCacheWriteFailure(
|
| + Transaction* trans) {
|
| + DCHECK_EQ(trans, current_writer_);
|
| +
|
| + // Now we need to take care of all the transactions in SharedWriters and
|
| + // delete SharedWriters as without the cache, we cannot continue the shared
|
| + // logic.
|
| + ResetAndRemoveCurrentWriter(true);
|
| +
|
| + // Notify waiting_writers_ of the failure. Tasks will be posted for all the
|
| + // transactions.
|
| + ProcessWaitingWriters(ERR_CACHE_WRITE_FAILURE);
|
| +
|
| + // Idle readers should know to fail when Read is invoked by their consumers.
|
| + SetIdleWritersFailState();
|
| + DCHECK(all_writers_.empty());
|
| +
|
| + // If there is a transaction validating currently, let it continue reading
|
| + // from the network without attempting to write to the cache.
|
| + if (validating_trans_) {
|
| + validating_trans_->ContinueWithoutSharedWriting();
|
| + validating_trans_ = nullptr;
|
| + }
|
| + MoveToPendingQueue();
|
| +
|
| + if (!empty()) {
|
| + DCHECK(doomed_writer_);
|
| + destroy_entry_ = true;
|
| + }
|
| +
|
| + return network_trans_.release();
|
| +}
|
| +
|
| +void HttpCache::FinalizeDoomedSharedWriter(ActiveEntry* entry) {
|
| + bool destroy_entry = false;
|
| + entry->shared_writers->FinalizeDoomedWriter(destroy_entry);
|
| + if (destroy_entry) {
|
| + DestroyEntryAndRestartPendingQueueTxns(entry);
|
| + } else {
|
| + ResetSharedWritersProcessPendingQueue(entry);
|
| + }
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::FinalizeDoomedWriter(bool& out_destroy_entry) {
|
| + if (doomed_writer_) {
|
| + DCHECK_EQ(doomed_writer_.get(), current_writer_);
|
| + all_writers_.erase(current_writer_);
|
| + doomed_writer_.reset();
|
| + current_writer_ = nullptr;
|
| + out_destroy_entry = destroy_entry_;
|
| + }
|
| +}
|
| +
|
| +void HttpCache::NetworkReadFailedSharedWriters(Transaction* trans,
|
| + ActiveEntry* entry,
|
| + int result) {
|
| + entry->shared_writers->OnNetworkReadFailure(trans, result);
|
| +
|
| + DCHECK(entry->shared_writers->empty());
|
| + entry->shared_writers.reset();
|
| +
|
| + // If entry->writer was set from the SharedWriters' validating_trans_, let it
|
| + // run else process pending queue.
|
| + if (!entry->writer) {
|
| + ProcessPendingQueue(entry);
|
| + }
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::OnNetworkReadFailure(const Transaction* trans,
|
| + int result) {
|
| + DCHECK_EQ(trans, current_writer_);
|
| +
|
| + // Now we need to take care of all the transactions in SharedWriters and
|
| + // delete SharedWriters.
|
| + ResetAndRemoveCurrentWriter();
|
| +
|
| + // Notify waiting_writers_ of the failure. Tasks will be posted for all the
|
| + // transactions.
|
| + ProcessWaitingWriters(result);
|
| +
|
| + // Idle readers should know to fail when Read is invoked by their consumers.
|
| + SetIdleWritersFailState();
|
| + DCHECK(all_writers_.empty());
|
| +
|
| + // If there is a transaction validating currently, let it continue reading
|
| + // from the network without attempting to write to the cache.
|
| + if (validating_trans_) {
|
| + DCHECK(!entry_->writer);
|
| + entry_->writer = validating_trans_;
|
| + validating_trans_->ResetShared(true);
|
| + validating_trans_ = nullptr;
|
| + }
|
| + MoveToPendingQueue();
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::ResetAndRemoveCurrentWriter(
|
| + bool continue_network_reading) {
|
| + current_writer_->ResetShared(continue_network_reading);
|
| + all_writers_.erase(current_writer_);
|
| + ResetCurrentWriter();
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::ResetCurrentWriter() {
|
| + // doomed_writer_ will be destroyed after cache transaction is done with its
|
| + // processing.
|
| + if (doomed_writer_) {
|
| + DCHECK_EQ(doomed_writer_.get(), current_writer_);
|
| + current_writer_->SetFinalizeDoomed();
|
| + } else {
|
| + current_writer_ = nullptr;
|
| + }
|
| +}
|
| +
|
| +bool HttpCache::SharedWriters::empty() {
|
| + if (doomed_writer_)
|
| + return false;
|
| + int count = all_writers_.size() + waiting_for_validation_.size() +
|
| + (validating_trans_ ? 1 : 0);
|
| + return count ? false : true;
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::SetIdleWritersFailState() {
|
| + // Since this is only for idle transactions, all waiting_writers_ and
|
| + // current_writer_ should be empty.
|
| + DCHECK(waiting_writers_.empty());
|
| + DCHECK(!current_writer_);
|
| +
|
| + for (auto trans : all_writers_) {
|
| + trans->SetSharedWritingFailState();
|
| + trans->ResetShared();
|
| + }
|
| +
|
| + all_writers_.clear();
|
| +}
|
| +
|
| +bool HttpCache::SharedWriters::AddTransaction(Transaction* transaction) {
|
| + transaction->SetShared();
|
| +
|
| + if (!validating_trans_) {
|
| + validating_trans_ = transaction;
|
| + return true;
|
| + } else {
|
| + waiting_for_validation_.push_back(transaction);
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +void HttpCache::ValidationMatchSharedWriters(Transaction* transaction,
|
| + RequestPriority priority,
|
| + ActiveEntry* entry) {
|
| + entry->shared_writers->OnValidationMatch(transaction, priority);
|
| + if (entry->shared_writers->empty()) {
|
| + entry->shared_writers.reset();
|
| + ProcessPendingQueue(entry);
|
| + }
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::OnValidationMatch(Transaction* transaction,
|
| + RequestPriority priority) {
|
| + DCHECK_EQ(validating_trans_, transaction);
|
| + validating_trans_ = nullptr;
|
| +
|
| + ValidationDoneContinue(transaction, priority);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::ValidationDoneContinue(
|
| + Transaction* transaction,
|
| + RequestPriority priority) {
|
| + validating_trans_ = nullptr;
|
| + if (priority > priority_) {
|
| + network_trans_->SetPriority(priority);
|
| + priority_ = priority;
|
| + }
|
| + all_writers_.insert(transaction);
|
| + transaction->SetShared();
|
| + ProcessFirstWaitingValidation();
|
| +}
|
| +
|
| +std::unique_ptr<HttpTransaction> HttpCache::ValidationNoMatchSharedWriters(
|
| + const std::string& key,
|
| + Transaction* transaction,
|
| + std::unique_ptr<HttpTransaction> network_trans,
|
| + RequestPriority priority,
|
| + ActiveEntry* entry) {
|
| + // This will either return the ownership of the network transaction so that
|
| + // this transaction can continue reading from the network or not, if this is
|
| + // the only transaction in SharedWriters.
|
| + std::unique_ptr<HttpTransaction> nw_trans =
|
| + entry->shared_writers->OnValidationNoMatch(
|
| + transaction, std::move(network_trans), priority);
|
| + if (nw_trans) {
|
| + DCHECK(!transaction->shared());
|
| + DoomActiveEntry(key);
|
| + // We need to do something about these pending entries, which now need to
|
| + // be added to a new entry.
|
| + while (!entry->pending_queue.empty()) {
|
| + // ERR_CACHE_RACE causes the transaction to restart the whole process.
|
| + entry->pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
|
| + entry->pending_queue.pop_front();
|
| + }
|
| + }
|
| + return nw_trans;
|
| +}
|
| +
|
| +std::unique_ptr<HttpTransaction> HttpCache::SharedWriters::OnValidationNoMatch(
|
| + Transaction* transaction,
|
| + std::unique_ptr<HttpTransaction> network_trans,
|
| + RequestPriority priority) {
|
| + DCHECK_EQ(validating_trans_, transaction);
|
| + // If there is no transaction in all_writers_, its ok to rewrite the entry
|
| + // response.
|
| + if (all_writers_.empty()) {
|
| + network_trans_ = std::move(network_trans);
|
| + ValidationDoneContinue(transaction, priority);
|
| + return std::unique_ptr<HttpTransaction>();
|
| + }
|
| +
|
| + transaction->ResetShared();
|
| + validating_trans_ = nullptr;
|
| + MoveToPendingQueue();
|
| + return network_trans;
|
| +}
|
| +
|
| +int HttpCache::SharedWriters::Read(IOBuffer* buf,
|
| + int buf_len,
|
| + const CompletionCallback& callback,
|
| + Transaction* transaction,
|
| + bool& read_in_progress) {
|
| + // If another transaction is already reading from the network, then this
|
| + // transaction waits for the read to complete and reads from the cache
|
| + // instead.
|
| + if (current_writer_) {
|
| + WaitingWriter waiting_writer(transaction, buf, buf_len);
|
| + waiting_writers_.push_back(waiting_writer);
|
| + read_in_progress = true;
|
| + return ERR_IO_PENDING;
|
| + }
|
| + current_writer_ = transaction;
|
| + return network_trans_->Read(buf, buf_len, callback);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::OnNetworkReadSuccess(const Transaction* trans,
|
| + scoped_refptr<IOBuffer> buf,
|
| + int len) {
|
| + DCHECK_EQ(trans, current_writer_);
|
| + // Save the network read data in all the waiting transactions' read buffers.
|
| + for (auto it = waiting_writers_.begin(); it != waiting_writers_.end(); it++) {
|
| + it->write_len = std::min(it->read_buf_len, len);
|
| + memcpy(it->read_buf->data(), buf->data(), it->write_len);
|
| + }
|
| +}
|
| +
|
| +void HttpCache::CacheWriteSuccessSharedWriters(Transaction* trans,
|
| + ActiveEntry* entry,
|
| + int result) {
|
| + entry->shared_writers->OnCacheWriteSuccess(trans, result);
|
| +
|
| + if (result > 0) // not the end of response.
|
| + return;
|
| +
|
| + ResetSharedWritersProcessPendingQueue(entry);
|
| +}
|
| +
|
| +void HttpCache::DoneReadingSharedWriters(Transaction* trans,
|
| + ActiveEntry* entry) {
|
| + entry->shared_writers->DoneReading(trans);
|
| +
|
| + ResetSharedWritersProcessPendingQueue(entry);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::DoneReading(Transaction* trans) {
|
| + // If current_writer_ is set, then wait for current_writer_ to detect the end
|
| + // of stream.
|
| + if (current_writer_) {
|
| + return;
|
| + }
|
| + DCHECK(waiting_writers_.empty());
|
| + RemoveIdleWriter(trans);
|
| + // If there is a transaction validating currently, return.
|
| + if (validating_trans_) {
|
| + return;
|
| + }
|
| +
|
| + // Else empty the SharedWriters object.
|
| + MoveIdleWritersToReaders();
|
| + DCHECK(all_writers_.empty());
|
| +
|
| + MoveToPendingQueue();
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::RemoveIdleWriter(Transaction* trans) {
|
| + // The transaction should be part of all_writers.
|
| + auto it = all_writers_.find(trans);
|
| + DCHECK(it != all_writers_.end());
|
| + all_writers_.erase(trans);
|
| + trans->ResetShared();
|
| +}
|
| +
|
| +void HttpCache::ResetSharedWritersProcessPendingQueue(ActiveEntry* entry) {
|
| + if (entry->shared_writers->empty()) {
|
| + entry->shared_writers.reset();
|
| + DCHECK(!entry->writer);
|
| + ProcessPendingQueue(entry);
|
| + }
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::OnCacheWriteSuccess(const Transaction* trans,
|
| + int result) {
|
| + DCHECK_EQ(trans, current_writer_);
|
| +
|
| + // Notify waiting_writers_. Tasks will be posted for all the
|
| + // transactions.
|
| + ProcessWaitingWriters(result);
|
| +
|
| + if (result > 0) { // not the end of response
|
| + ResetCurrentWriter();
|
| + return;
|
| + }
|
| +
|
| + ResetAndRemoveCurrentWriter();
|
| +
|
| + // If there is a transaction validating currently, change state and return.
|
| + if (validating_trans_) {
|
| + return;
|
| + }
|
| +
|
| + // Else empty the SharedWriters object.
|
| + MoveIdleWritersToReaders();
|
| + DCHECK(all_writers_.empty());
|
| +
|
| + MoveToPendingQueue();
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::MoveIdleWritersToReaders() {
|
| + // Should be invoked after waiting_writers_ are all processed.
|
| + DCHECK(waiting_writers_.empty());
|
| + for (auto idle_writer : all_writers_) {
|
| + entry_->readers.insert(idle_writer);
|
| + idle_writer->ResetShared(false, true);
|
| + }
|
| + all_writers_.clear();
|
| +}
|
| +
|
| +void HttpCache::DoomCurrentSharedWriter(std::unique_ptr<HttpTransaction> trans,
|
| + ActiveEntry* entry) {
|
| + entry->shared_writers->DoomCurrentWriter(std::move(trans));
|
| + ResetSharedWritersProcessPendingQueue(entry);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::RemoveWaitingWriter(Transaction* trans) {
|
| + auto it = waiting_writers_.begin();
|
| + for (; it != waiting_writers_.end(); it++) {
|
| + if (trans == it->transaction) {
|
| + waiting_writers_.erase(it);
|
| + all_writers_.erase(trans);
|
| + trans->ResetShared();
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // If a waiting_writer_ existed, there should have been a current_writer_.
|
| + DCHECK(current_writer_);
|
| +}
|
| +
|
| +void HttpCache::RemoveIdleSharedWriter(Transaction* trans,
|
| + ActiveEntry* entry,
|
| + bool cancel) {
|
| + entry->shared_writers->RemoveIdleWriter(trans);
|
| +
|
| + bool success = false;
|
| + if (cancel) {
|
| + success = trans->AddTruncatedFlag();
|
| + }
|
| + if (entry->shared_writers->empty()) {
|
| + if (!success) {
|
| + entry->shared_writers.reset();
|
| + DestroyEntryAndRestartPendingQueueTxns(entry);
|
| + } else {
|
| + ResetSharedWritersProcessPendingQueue(entry);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void HttpCache::RemoveValidatingTransSharedWriters(Transaction* trans,
|
| + ActiveEntry* entry) {
|
| + entry->shared_writers->RemoveValidatingTransaction(trans);
|
| + ResetSharedWritersProcessPendingQueue(entry);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::RemoveValidatingTransaction(Transaction* trans) {
|
| + DCHECK_EQ(validating_trans_, trans);
|
| + validating_trans_ = nullptr;
|
| + trans->ResetShared();
|
| + ProcessFirstWaitingValidation();
|
| + return;
|
| +}
|
| +
|
| +bool HttpCache::SharedWriters::RemoveWaitingTransaction(Transaction* trans) {
|
| + auto it = std::find(waiting_for_validation_.begin(),
|
| + waiting_for_validation_.end(), trans);
|
| + if (it != waiting_for_validation_.end()) {
|
| + trans->ResetShared();
|
| + waiting_for_validation_.erase(it);
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::DoomCurrentWriter(
|
| + std::unique_ptr<HttpTransaction> trans) {
|
| + DCHECK_EQ(current_writer_, trans.get());
|
| + if (waiting_writers_.empty() && all_writers_.empty()) {
|
| + ResetAndRemoveCurrentWriter();
|
| + } else {
|
| + doomed_writer_ = std::move(trans);
|
| + }
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::ProcessFirstWaitingValidation() {
|
| + if (!waiting_for_validation_.empty() || validating_trans_)
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&HttpCache::SharedWriters::OnProcessFirstWaitingValidation,
|
| + weak_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::OnProcessFirstWaitingValidation() {
|
| + if (waiting_for_validation_.empty() && !validating_trans_)
|
| + return;
|
| +
|
| + Transaction* trans = nullptr;
|
| + if (validating_trans_) {
|
| + trans = validating_trans_;
|
| + } else {
|
| + trans = waiting_for_validation_.front();
|
| + waiting_for_validation_.erase(waiting_for_validation_.begin());
|
| + validating_trans_ = trans;
|
| + }
|
| + trans->io_callback().Run(OK);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::ProcessWaitingWriters(int result) {
|
| + for (auto it = waiting_writers_.begin(); it != waiting_writers_.end(); it++) {
|
| + Transaction* transaction = it->transaction;
|
| +
|
| + if (result > 0) { // success
|
| + // Fill result with the length of buffer filled for this transaction which
|
| + // may be different from the transaction that actually wrote to the cache
|
| + // based on the buffer size.
|
| + // If cache write is completed, then result will be 0.
|
| + // That will lead to the transaction calling DoneWritingToEntry().
|
| + result = it->write_len;
|
| + } else {
|
| + // If its response completion or failure, this transaction needs to be
|
| + // removed.
|
| + transaction->ResetShared();
|
| + all_writers_.erase(transaction);
|
| + }
|
| +
|
| + // Post task to notify transaction.
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE, base::Bind(&HttpCache::NotifyTransaction,
|
| + cache_->GetWeakPtr(), transaction, result));
|
| + }
|
| +
|
| + waiting_writers_.clear();
|
| +}
|
| +
|
| +void HttpCache::NotifyTransaction(Transaction* transaction, int result) {
|
| + DCHECK(transaction);
|
| + transaction->io_callback().Run(result);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::MoveFromPendingQueue() {
|
| + auto it = entry_->pending_queue.begin();
|
| + while (it != entry_->pending_queue.end()) {
|
| + Transaction* transaction = *it;
|
| + if (transaction->IsEligibleForSharedWriting()) {
|
| + transaction->SetShared();
|
| + waiting_for_validation_.push_back(transaction);
|
| + it = entry_->pending_queue.erase(it);
|
| + } else {
|
| + ++it;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::MoveToPendingQueue() {
|
| + // For maintaining the order of the transactions as they arrived, append
|
| + // these to the front of the pending_queue. Note that the order is preserved
|
| + // only among the transactions that are eligible for sharing. For others, they
|
| + // may have arrived earlier but may be processed later which is fair since
|
| + // they have to anyways wait till the entry is written to the cache.
|
| + while (!waiting_for_validation_.empty()) {
|
| + Transaction* trans = waiting_for_validation_.back();
|
| + trans->ResetShared(true);
|
| + entry_->pending_queue.push_front(trans);
|
| + waiting_for_validation_.pop_back();
|
| + }
|
| +}
|
| +
|
| +int HttpCache::SharedWriters::GetTotalReceivedBytes() const {
|
| + DCHECK(network_trans_);
|
| + return network_trans_->GetTotalReceivedBytes();
|
| +}
|
| +
|
| +int HttpCache::SharedWriters::GetTotalSentBytes() const {
|
| + DCHECK(network_trans_);
|
| + return network_trans_->GetTotalSentBytes();
|
| +}
|
| +
|
| +LoadState HttpCache::SharedWriters::GetLoadState() const {
|
| + DCHECK(network_trans_);
|
| + return network_trans_->GetLoadState();
|
| +}
|
| +
|
| +bool HttpCache::SharedWriters::GetFullRequestHeaders(
|
| + HttpRequestHeaders* headers) const {
|
| + DCHECK(network_trans_);
|
| + return network_trans_->GetFullRequestHeaders(headers);
|
| +}
|
| +
|
| +bool HttpCache::SharedWriters::GetLoadTimingInfo(
|
| + LoadTimingInfo* load_timing_info) const {
|
| + DCHECK(network_trans_);
|
| + return network_trans_->GetLoadTimingInfo(load_timing_info);
|
| +}
|
| +
|
| +bool HttpCache::SharedWriters::GetRemoteEndpoint(IPEndPoint* endpoint) const {
|
| + DCHECK(network_trans_);
|
| + return network_trans_->GetRemoteEndpoint(endpoint);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::PopulateNetErrorDetails(
|
| + NetErrorDetails* details) const {
|
| + DCHECK(network_trans_);
|
| + return network_trans_->PopulateNetErrorDetails(details);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::SetPriority(RequestPriority priority) {
|
| + DCHECK(network_trans_);
|
| + network_trans_->SetPriority(priority_);
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::SetWebSocketHandshakeStreamCreateHelper(
|
| + WebSocketHandshakeStreamBase::CreateHelper* create_helper) {
|
| + DCHECK(network_trans_);
|
| + return network_trans_->SetWebSocketHandshakeStreamCreateHelper(create_helper);
|
| +}
|
| +
|
| +int HttpCache::SharedWriters::ResumeNetworkStart() {
|
| + DCHECK(network_trans_);
|
| + return network_trans_->ResumeNetworkStart();
|
| +}
|
| +
|
| +void HttpCache::SharedWriters::GetConnectionAttempts(
|
| + ConnectionAttempts* out) const {
|
| + DCHECK(network_trans_);
|
| + network_trans_->GetConnectionAttempts(out);
|
| +}
|
| +
|
| +HttpCache::SharedWriters::WaitingWriter::WaitingWriter(
|
| + Transaction* trans,
|
| + scoped_refptr<IOBuffer> buf,
|
| + int len)
|
| + : transaction(trans), read_buf(buf), read_buf_len(len), write_len(0) {}
|
| +
|
| +HttpCache::SharedWriters::WaitingWriter::~WaitingWriter() {}
|
| +
|
| +HttpCache::SharedWriters::WaitingWriter::WaitingWriter(const WaitingWriter&) =
|
| + default;
|
| +
|
| } // namespace net
|
|
|