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

Unified Diff: net/http/http_cache.cc

Issue 2519473002: Fixes the cache lock issue. (Closed)
Patch Set: Redesigned the fix using DataAccess class for eliminating Orphan API.(Rebased till refs/heads/master@{#442607}) Created 3 years, 11 months 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 bab16394f75f1d354c5c1b4fcade4f690c64efed..7881789fd3c6ff398836b979071865dbafa57344 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -35,6 +35,7 @@
#include "net/base/upload_data_stream.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/disk_cache_based_quic_server_info.h"
+#include "net/http/http_cache_shared_writers.h"
#include "net/http/http_cache_transaction.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_network_session.h"
@@ -339,7 +340,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);
}
@@ -451,12 +453,12 @@ int HttpCache::CreateTransaction(RequestPriority priority,
CreateBackend(NULL, CompletionCallback());
}
- HttpCache::Transaction* transaction =
+ HttpCache::Transaction* transaction =
new HttpCache::Transaction(priority, this);
- if (bypass_lock_for_test_)
+ if (bypass_lock_for_test_)
transaction->BypassLockForTest();
- if (fail_conditionalization_for_test_)
- transaction->FailConditionalizationForTest();
+ if (fail_conditionalization_for_test_)
+ transaction->FailConditionalizationForTest();
trans->reset(transaction);
return OK;
@@ -582,8 +584,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;
}
@@ -630,7 +632,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());
@@ -655,7 +657,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());
@@ -789,20 +791,34 @@ 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;
+ }
+ }
+ } else if (trans->mode() & Transaction::WRITE) { // No SharedWriters.
// transaction needs exclusive access to the entry
if (entry->readers.empty()) {
entry->writer = trans;
@@ -812,7 +828,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
@@ -826,6 +842,9 @@ 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.
+ 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())
@@ -833,7 +852,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) {
@@ -853,35 +871,42 @@ 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);
+ DestroyEntryAndRestartPendingQueueTransactions(entry);
+ }
+}
- // We failed to create this entry.
- TransactionList pending_queue;
- pending_queue.swap(entry->pending_queue);
+void HttpCache::DestroyEntryAndRestartPendingQueueTransactions(
+ 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);
@@ -897,7 +922,7 @@ void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
Transaction* trans = entry->writer;
entry->writer = NULL;
- entry->readers.push_back(trans);
+ entry->readers.insert(trans);
ProcessPendingQueue(entry);
}
@@ -916,8 +941,9 @@ LoadState HttpCache::GetLoadStateForPendingTransaction(
}
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);
@@ -950,12 +976,13 @@ void HttpCache::RemovePendingTransaction(Transaction* trans) {
bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
Transaction* trans) {
- TransactionList& pending_queue = entry->pending_queue;
+ if (trans->shared())
+ return entry->shared_writers->RemoveWaitingTransaction(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;
}
@@ -993,7 +1020,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()) {
@@ -1158,4 +1185,198 @@ void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
item->NotifyTransaction(result, NULL);
}
+bool HttpCache::IsResponseCompleted(const ActiveEntry* entry,
+ const HttpResponseInfo* response_info) {
+ int current_size = entry->disk_entry->GetDataSize(kResponseContentIndex);
+ int64_t content_length = response_info->headers->GetContentLength();
+ if ((content_length >= 0 && content_length <= current_size) ||
+ content_length < 0)
+ return true;
+ return false;
+}
+
+int HttpCache::WriteResponseInfo(ActiveEntry* entry,
+ const HttpResponseInfo* response,
+ CompletionCallback& callback,
+ bool truncated,
+ int* io_buf_len) {
+ // When writing headers, we normally only write the non-transient headers.
+ bool skip_transient_headers = true;
+ scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer());
+ response->Persist(data->pickle(), skip_transient_headers, truncated);
+ data->Done();
+
+ *io_buf_len = data->pickle()->size();
+ return entry->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(),
+ *io_buf_len, callback, true);
+}
+
+// Histogram data from the end of 2010 show the following distribution of
+// response headers:
+//
+// Content-Length............... 87%
+// Date......................... 98%
+// Last-Modified................ 49%
+// Etag......................... 19%
+// Accept-Ranges: bytes......... 25%
+// Accept-Ranges: none.......... 0.4%
+// Strong Validator............. 50%
+// Strong Validator + ranges.... 24%
+// Strong Validator + CL........ 49%
+//
+bool HttpCache::CanResumeEntry(bool has_data,
+ const std::string& method,
+ const HttpResponseInfo* response,
+ ActiveEntry* entry) {
+ // Double check that there is something worth keeping.
+ if (has_data && !entry->disk_entry->GetDataSize(kResponseContentIndex))
+ return false;
+
+ if (method != "GET")
+ return false;
+
+ // Note that if this is a 206, content-length was already fixed after calling
+ // PartialData::ResponseHeadersOK().
+ if (response->headers->GetContentLength() <= 0 ||
+ response->headers->HasHeaderValue("Accept-Ranges", "none") ||
+ !response->headers->HasStrongValidators()) {
+ return false;
+ }
+
+ return true;
+}
+
+void HttpCache::CreateSharedWriters(
+ Transaction* cache_transaction,
+ std::unique_ptr<HttpTransaction> network_transaction,
+ RequestPriority priority) {
+ ActiveEntry* entry = cache_transaction->entry();
+ DCHECK(!entry->shared_writers);
+ DCHECK_EQ(entry->writer, cache_transaction);
+ DCHECK(entry->readers.empty());
+ entry->shared_writers.reset(
+ new HttpCache::SharedWriters(this, entry, cache_transaction, priority,
+ std::move(network_transaction)));
+
+ // 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. After this, pending_queue will only contain transactions
+ // that are not eligible for shared writing.
+ entry->shared_writers->MoveFromPendingQueue();
+ entry->shared_writers->ProcessFirstWaitingValidation();
+}
+
+void HttpCache::ResponseCompleteSharedWriters(ActiveEntry* entry) {
+ DCHECK(entry->shared_writers->empty());
+ entry->shared_writers.release();
+ ProcessPendingQueue(entry);
+}
+
+void HttpCache::ResponseFailedSharedWriters(bool success, ActiveEntry* entry) {
+ DCHECK(entry->shared_writers->empty());
+ entry->shared_writers.release();
+
+ if (success)
+ ProcessPendingQueue(entry);
+ else
+ DestroyEntryAndRestartPendingQueueTransactions(entry);
+}
+
+void HttpCache::DoneReadingSharedWriters(Transaction* transaction,
+ ActiveEntry* entry) {
+ entry->shared_writers->DoneReading(transaction);
+
+ ResetSharedWritersProcessPendingQueue(entry);
+}
+
+void HttpCache::StopCachingSharedWriters(Transaction* transaction,
+ ActiveEntry* entry) {
+ bool stopped = entry->shared_writers->StopCaching(transaction);
+
+ if (stopped) {
+ DCHECK(entry->shared_writers->empty());
+ DCHECK_EQ(entry->writer, transaction);
+ entry->shared_writers.reset();
+ }
+}
+
+std::unique_ptr<HttpTransaction> HttpCache::ValidationNoMatchSharedWriters(
+ const std::string& key,
+ Transaction* transaction,
+ std::unique_ptr<HttpTransaction> network_transaction,
+ 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_transaction =
+ entry->shared_writers->OnValidationNoMatch(
+ transaction, std::move(network_transaction), priority);
+ if (nw_transaction) {
+ 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_transaction;
+}
+
+void HttpCache::ResetSharedWritersProcessPendingQueue(ActiveEntry* entry) {
+ if (entry->shared_writers->empty()) {
+ ResetOrReleaseSharedWriters(entry);
+ DCHECK(!entry->writer);
+ ProcessPendingQueue(entry);
+ }
+}
+
+void HttpCache::RemoveCurrentSharedWriter(Transaction* transaction,
+ ActiveEntry* entry) {
+ entry->shared_writers->RemoveCurrentWriter(transaction);
+ ResetSharedWritersProcessPendingQueue(entry);
+}
+
+void HttpCache::ResetOrReleaseSharedWriters(ActiveEntry* entry) {
+ if (entry->shared_writers->CanReset())
+ entry->shared_writers.reset();
+ else
+ entry->shared_writers.release();
+}
+
+void HttpCache::RemoveIdleSharedWriter(Transaction* transaction,
+ ActiveEntry* entry) {
+ entry->shared_writers->RemoveIdleWriter(transaction);
+
+ if (entry->shared_writers->empty()) {
+ bool success = false;
+ success = transaction->AddTruncatedFlag();
+ if (success) {
+ ResetSharedWritersProcessPendingQueue(entry);
+ } else {
+ ResetOrReleaseSharedWriters(entry);
+ DestroyEntryAndRestartPendingQueueTransactions(entry);
+ }
+ }
+}
+
+void HttpCache::RemoveValidatingTransSharedWriters(Transaction* transaction,
+ ActiveEntry* entry) {
+ entry->shared_writers->RemoveValidatingTransaction(transaction);
+ ResetSharedWritersProcessPendingQueue(entry);
+}
+
+void HttpCache::NotifyTransaction(base::WeakPtr<Transaction> transaction,
+ int result) {
+ if (transaction) {
+ transaction->io_callback().Run(result);
+ }
+}
+
} // namespace net

Powered by Google App Engine
This is Rietveld 408576698