Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(559)

Unified Diff: net/http/http_cache.cc

Issue 2519473002: Fixes the cache lock issue. (Closed)
Patch Set: Initial patch Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698