Revert of HttpCache::Transaction layer allowing parallel validation (patchset #33 id:800001 of https://codereview.chromium.org/2721933002/ )
Reason for revert:
Breaks tricky-tot-chrome-pfq-informational audio_CrasSanity autotest: https://uberchromegw.corp.google.com/i/chromeos.chrome/builders/tricky-tot-chrome-pfq-informational/builds/4271
Original issue's description:
> This CL is a precursor to allowing shared writing to fix cache lock.
>
> This CL allows transactions to continue to their validation phase even when another
> transaction is the active reader/writer. After the validation phase, if its a match
> the transaction might wait till the response is written to the cache by the active
> writer. If its not a match the transaction will doom the entry and go to the
> network. In a subsequent CL, the not matching case will create a new entry as well.
>
> BUG=472740
>
> Review-Url: https://codereview.chromium.org/2721933002
> Cr-Commit-Position: refs/heads/master@{#467426}
> Committed: https://chromium.googlesource.com/chromium/src/+/1e2e347f957ef889aaee527bb757849f76e8a808
[email protected],[email protected],[email protected],[email protected]
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=472740
Review-Url: https://codereview.chromium.org/2847653002
Cr-Commit-Position: refs/heads/master@{#467556}
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index 42f8c17..968cffa 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -96,29 +96,28 @@
//-----------------------------------------------------------------------------
HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
- : disk_entry(entry) {}
+ : disk_entry(entry),
+ writer(NULL),
+ will_process_pending_queue(false),
+ doomed(false) {
+}
HttpCache::ActiveEntry::~ActiveEntry() {
if (disk_entry) {
disk_entry->Close();
- disk_entry = nullptr;
+ disk_entry = NULL;
}
}
size_t HttpCache::ActiveEntry::EstimateMemoryUsage() const {
// Skip |disk_entry| which is tracked in simple_backend_impl; Skip |readers|
- // and |add_to_entry_queue| because the Transactions are owned by their
- // respective URLRequestHttpJobs.
+ // and |pending_queue| because the Transactions are owned by their respective
+ // URLRequestHttpJobs.
return 0;
}
bool HttpCache::ActiveEntry::HasNoTransactions() {
- return !writer && readers.empty() && add_to_entry_queue.empty() &&
- done_headers_queue.empty() && !headers_transaction;
-}
-
-bool HttpCache::ActiveEntry::HasNoActiveTransactions() {
- return !writer && readers.empty() && !headers_transaction;
+ return !writer && readers.empty() && pending_queue.empty();
}
//-----------------------------------------------------------------------------
@@ -343,17 +342,15 @@
weak_factory_.InvalidateWeakPtrs();
// If we have any active entries remaining, then we need to deactivate them.
- // We may have some pending tasks to process queued transactions ,but since
- // those won't run (due to our destruction), we can simply ignore the
- // corresponding flags.
+ // We may have some pending calls to OnProcessPendingQueue, but since those
+ // won't run (due to our destruction), we can simply ignore the corresponding
+ // will_process_pending_queue flag.
while (!active_entries_.empty()) {
ActiveEntry* entry = active_entries_.begin()->second.get();
- entry->will_process_queued_transactions = false;
- entry->add_to_entry_queue.clear();
+ entry->will_process_pending_queue = false;
+ entry->pending_queue.clear();
entry->readers.clear();
- entry->done_headers_queue.clear();
- entry->headers_transaction = nullptr;
- entry->writer = nullptr;
+ entry->writer = NULL;
DeactivateEntry(entry);
}
@@ -614,8 +611,7 @@
entry_ptr->doomed = true;
DCHECK(entry_ptr->writer || !entry_ptr->readers.empty() ||
- entry_ptr->headers_transaction ||
- entry_ptr->will_process_queued_transactions);
+ entry_ptr->will_process_pending_queue);
return OK;
}
@@ -671,7 +667,7 @@
HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
auto it = active_entries_.find(key);
- return it != active_entries_.end() ? it->second.get() : nullptr;
+ return it != active_entries_.end() ? it->second.get() : NULL;
}
HttpCache::ActiveEntry* HttpCache::ActivateEntry(
@@ -683,7 +679,7 @@
}
void HttpCache::DeactivateEntry(ActiveEntry* entry) {
- DCHECK(!entry->will_process_queued_transactions);
+ DCHECK(!entry->will_process_pending_queue);
DCHECK(!entry->doomed);
DCHECK(entry->disk_entry);
DCHECK(entry->HasNoTransactions());
@@ -813,261 +809,121 @@
}
}
-int HttpCache::AddTransactionToEntry(ActiveEntry* entry,
- Transaction* transaction) {
+int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
DCHECK(entry);
DCHECK(entry->disk_entry);
- // Always add a new transaction to the queue to maintain FIFO order.
- entry->add_to_entry_queue.push_back(transaction);
- ProcessQueuedTransactions(entry);
- return ERR_IO_PENDING;
-}
-int HttpCache::DoneWithResponseHeaders(ActiveEntry* entry,
- Transaction* transaction) {
- // If |transaction| is the current writer, do nothing. This can happen for
- // range requests since they can go back to headers phase after starting to
- // write.
- if (entry->writer == transaction)
- return OK;
+ // 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.
+ //
+ // NOTE: If the transaction can only write, then the entry should not be in
+ // use (since any existing entry should have already been doomed).
- DCHECK_EQ(entry->headers_transaction, transaction);
-
- entry->headers_transaction = nullptr;
-
- // If transaction is responsible for writing the response body, then do not go
- // through done_headers_queue for performance benefit. (Also, in case of
- // writer transaction, the consumer sometimes depend on synchronous behaviour
- // e.g. while computing raw headers size. (crbug.com/711766))
- if (transaction->mode() & Transaction::WRITE) {
- DCHECK(entry->done_headers_queue.empty());
- DCHECK(!entry->writer);
- entry->writer = transaction;
- ProcessQueuedTransactions(entry);
- return OK;
+ if (entry->writer || entry->will_process_pending_queue) {
+ entry->pending_queue.push_back(trans);
+ return ERR_IO_PENDING;
}
- // If this is not the first transaction in done_headers_queue, it should be a
- // read-mode transaction.
- DCHECK(entry->done_headers_queue.empty() ||
- !(transaction->mode() & Transaction::WRITE));
+ if (trans->mode() & Transaction::WRITE) {
+ // transaction needs exclusive access to the entry
+ if (entry->readers.empty()) {
+ entry->writer = trans;
+ } else {
+ entry->pending_queue.push_back(trans);
+ return ERR_IO_PENDING;
+ }
+ } else {
+ // transaction needs read access to the entry
+ entry->readers.insert(trans);
+ }
- entry->done_headers_queue.push_back(transaction);
- ProcessQueuedTransactions(entry);
- return ERR_IO_PENDING;
+ // We do this before calling EntryAvailable to force any further calls to
+ // AddTransactionToEntry to add their transaction to the pending queue, which
+ // ensures FIFO ordering.
+ if (!entry->writer && !entry->pending_queue.empty())
+ ProcessPendingQueue(entry);
+
+ return OK;
}
-void HttpCache::DoneWithEntry(ActiveEntry* entry,
- Transaction* transaction,
+void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
bool cancel) {
- // Transaction is waiting in the done_headers_queue.
- auto it = std::find(entry->done_headers_queue.begin(),
- entry->done_headers_queue.end(), transaction);
- if (it != entry->done_headers_queue.end()) {
- entry->done_headers_queue.erase(it);
- if (cancel)
- ProcessEntryFailure(entry);
+ // 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())
return;
- }
- // Transaction is removed in the headers phase.
- if (transaction == entry->headers_transaction) {
- // If the response is not written (cancel is true), consider it a failure.
- DoneWritingToEntry(entry, !cancel, transaction);
- return;
- }
+ if (entry->writer) {
+ DCHECK(trans == entry->writer);
- // Transaction is removed in the writing phase.
- if (transaction == entry->writer) {
// Assume there was a failure.
bool success = false;
if (cancel) {
DCHECK(entry->disk_entry);
// This is a successful operation in the sense that we want to keep the
// entry.
- success = transaction->AddTruncatedFlag();
+ success = trans->AddTruncatedFlag();
// The previous operation may have deleted the entry.
- if (!transaction->entry())
+ if (!trans->entry())
return;
}
- DoneWritingToEntry(entry, success, transaction);
- return;
+ DoneWritingToEntry(entry, success);
+ } else {
+ DoneReadingFromEntry(entry, trans);
}
-
- // Transaction is reading from the entry.
- DoneReadingFromEntry(entry, transaction);
}
-void HttpCache::DoneWritingToEntry(ActiveEntry* entry,
- bool success,
- Transaction* transaction) {
- DCHECK(transaction == entry->writer ||
- transaction == entry->headers_transaction);
+void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
+ DCHECK(entry->readers.empty());
- if (transaction == entry->writer)
- entry->writer = nullptr;
- else
- entry->headers_transaction = nullptr;
+ entry->writer = NULL;
- // If writer fails, restart the headers_transaction by setting its state.
- // Since the headers_transactions is awaiting an asynchronous operation
- // completion, when it's IO callback is invoked, it will be restarted.
- if (!success && entry->headers_transaction) {
- entry->headers_transaction->SetValidatingCannotProceed();
- entry->headers_transaction = nullptr;
- DCHECK(entry->HasNoActiveTransactions());
+ if (success) {
+ ProcessPendingQueue(entry);
+ } else {
+ DCHECK(!entry->will_process_pending_queue);
+
+ // We failed to create this entry.
+ TransactionList pending_queue;
+ pending_queue.swap(entry->pending_queue);
+
+ 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();
+ }
}
- if (!success)
- ProcessEntryFailure(entry);
- else
- ProcessQueuedTransactions(entry);
}
-void HttpCache::DoneReadingFromEntry(ActiveEntry* entry,
- Transaction* transaction) {
+void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
DCHECK(!entry->writer);
- auto it = entry->readers.find(transaction);
+
+ auto it = entry->readers.find(trans);
DCHECK(it != entry->readers.end());
+
entry->readers.erase(it);
- ProcessQueuedTransactions(entry);
+ ProcessPendingQueue(entry);
}
-void HttpCache::RemoveAllQueuedTransactions(ActiveEntry* entry,
- TransactionList* list) {
- // Process done_headers_queue before add_to_entry_queue to maintain FIFO
- // order.
- for (auto* transaction : entry->done_headers_queue)
- list->push_back(transaction);
- entry->done_headers_queue.clear();
+void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
+ DCHECK(entry->writer);
+ DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
+ DCHECK(entry->readers.empty());
- for (auto* transaction : entry->add_to_entry_queue)
- list->push_back(transaction);
- entry->add_to_entry_queue.clear();
-}
+ Transaction* trans = entry->writer;
-void HttpCache::ProcessEntryFailure(ActiveEntry* entry) {
- // Failure case is either writer failing to completely write the response to
- // the cache or validating transaction received a non-304 response.
- TransactionList list;
- if (entry->HasNoActiveTransactions() &&
- !entry->will_process_queued_transactions) {
- entry->disk_entry->Doom();
- RemoveAllQueuedTransactions(entry, &list);
- DestroyEntry(entry);
- } else {
- DoomActiveEntry(entry->disk_entry->GetKey());
- RemoveAllQueuedTransactions(entry, &list);
- }
- // ERR_CACHE_RACE causes the transaction to restart the whole process.
- for (auto* transaction : list)
- transaction->io_callback().Run(net::ERR_CACHE_RACE);
-}
+ entry->writer = NULL;
+ entry->readers.insert(trans);
-void HttpCache::ProcessQueuedTransactions(ActiveEntry* entry) {
- // Multiple readers may finish with an entry at once, so we want to batch up
- // calls to OnProcessQueuedTransactions. This flag also tells us that we
- // should not delete the entry before OnProcessQueuedTransactions runs.
- if (entry->will_process_queued_transactions)
- return;
-
- entry->will_process_queued_transactions = true;
-
- // Post a task instead of invoking the io callback of another transaction here
- // to avoid re-entrancy.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(&HttpCache::OnProcessQueuedTransactions, GetWeakPtr(), entry));
-}
-
-void HttpCache::ProcessAddToEntryQueue(ActiveEntry* entry) {
- DCHECK(!entry->add_to_entry_queue.empty());
-
- // Note the entry may be new or may already have a response body written to
- // it. In both cases, a transaction needs to wait since only one transaction
- // can be in the headers phase at a time.
- if (entry->headers_transaction) {
- return;
- }
- Transaction* transaction = entry->add_to_entry_queue.front();
- entry->add_to_entry_queue.erase(entry->add_to_entry_queue.begin());
- entry->headers_transaction = transaction;
-
- transaction->io_callback().Run(OK);
-}
-
-void HttpCache::ProcessDoneHeadersQueue(ActiveEntry* entry) {
- DCHECK(!entry->writer);
- DCHECK(!entry->done_headers_queue.empty());
-
- Transaction* transaction = entry->done_headers_queue.front();
-
- // If this transaction is responsible for writing the response body.
- if (transaction->mode() & Transaction::WRITE) {
- entry->writer = transaction;
- } else {
- // If a transaction is in front of this queue with only read mode set and
- // there is no writer, it implies response body is already written, convert
- // to a reader.
- auto return_val = entry->readers.insert(transaction);
- DCHECK_EQ(return_val.second, true);
- }
-
- // Post another task to give a chance to more transactions to either join
- // readers or another transaction to start parallel validation.
- ProcessQueuedTransactions(entry);
-
- entry->done_headers_queue.erase(entry->done_headers_queue.begin());
- transaction->io_callback().Run(OK);
-}
-
-bool HttpCache::CanTransactionWriteResponseHeaders(ActiveEntry* entry,
- Transaction* transaction,
- bool is_match) const {
- if (transaction != entry->headers_transaction)
- return false;
-
- if (!(transaction->mode() & Transaction::WRITE))
- return false;
-
- // If its not a match then check if it is the transaction responsible for
- // writing the response body.
- if (!is_match) {
- return !entry->writer && entry->done_headers_queue.empty() &&
- entry->readers.empty();
- }
-
- return true;
-}
-
-bool HttpCache::IsTransactionWritingIncomplete(
- ActiveEntry* entry,
- Transaction* transaction,
- const std::string& method) const {
- if (transaction == entry->writer)
- return true;
-
- if (method == "HEAD" || method == "DELETE")
- return false;
-
- // Check if transaction is about to start writing to the cache.
-
- // Transaction's mode may have been set to NONE if StopCaching was invoked.
- if (!(transaction->mode() & Transaction::WRITE ||
- transaction->mode() == Transaction::NONE)) {
- return false;
- }
-
- // If a transaction is completing headers or done with headers phase with
- // write mode then it should be the future writer. Just checking the front of
- // done_headers_queue since the rest should anyways be READ mode transactions
- // as they would be a result of validation match.
- return transaction == entry->headers_transaction ||
- transaction == entry->done_headers_queue.front();
-}
-
-bool HttpCache::IsWritingInProgress(ActiveEntry* entry) const {
- return entry->writer != nullptr;
+ ProcessPendingQueue(entry);
}
LoadState HttpCache::GetLoadStateForPendingTransaction(
@@ -1117,15 +973,14 @@
}
bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
- Transaction* transaction) {
- TransactionList& add_to_entry_queue = entry->add_to_entry_queue;
+ Transaction* trans) {
+ TransactionList& pending_queue = entry->pending_queue;
- auto j =
- find(add_to_entry_queue.begin(), add_to_entry_queue.end(), transaction);
- if (j == add_to_entry_queue.end())
+ auto j = find(pending_queue.begin(), pending_queue.end(), trans);
+ if (j == pending_queue.end())
return false;
- add_to_entry_queue.erase(j);
+ pending_queue.erase(j);
return true;
}
@@ -1147,11 +1002,22 @@
return false;
}
-void HttpCache::OnProcessQueuedTransactions(ActiveEntry* entry) {
- entry->will_process_queued_transactions = false;
+void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
+ // Multiple readers may finish with an entry at once, so we want to batch up
+ // calls to OnProcessPendingQueue. This flag also tells us that we should
+ // not delete the entry before OnProcessPendingQueue runs.
+ if (entry->will_process_pending_queue)
+ return;
+ entry->will_process_pending_queue = true;
- // Note that this function should only invoke one transaction's IO callback
- // since its possible for IO callbacks' consumers to destroy the cache/entry.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
+}
+
+void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
+ entry->will_process_pending_queue = false;
+ DCHECK(!entry->writer);
// If no one is interested in this entry, then we can deactivate it.
if (entry->HasNoTransactions()) {
@@ -1159,22 +1025,20 @@
return;
}
- if (entry->done_headers_queue.empty() && entry->add_to_entry_queue.empty())
+ if (entry->pending_queue.empty())
return;
- // To maintain FIFO order of transactions, done_headers_queue should be
- // checked for processing before add_to_entry_queue.
+ // 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.
- // If another transaction is writing the response, let validated transactions
- // wait till the response is complete. If the response is not yet started, the
- // done_headers_queue transaction should start writing it.
- if (!entry->writer && !entry->done_headers_queue.empty()) {
- ProcessDoneHeadersQueue(entry);
- return;
+ entry->pending_queue.erase(entry->pending_queue.begin());
+
+ int rv = AddTransactionToEntry(entry, next);
+ if (rv != ERR_IO_PENDING) {
+ next->io_callback().Run(rv);
}
-
- if (!entry->add_to_entry_queue.empty())
- ProcessAddToEntryQueue(entry);
}
void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index ca8ba08..635cb92d 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -244,32 +244,10 @@
friend class ViewCacheHelper;
struct PendingOp; // Info for an entry under construction.
- // To help with testing.
- friend class MockHttpCache;
-
using TransactionList = std::list<Transaction*>;
using TransactionSet = std::unordered_set<Transaction*>;
typedef std::list<std::unique_ptr<WorkItem>> WorkItemList;
- // We implement a basic reader/writer lock for the disk cache entry. If there
- // is a writer, then all transactions must wait to read the body. But the
- // waiting transactions can start their headers phase in parallel. Headers
- // phase is allowed for one transaction at a time so that if it doesn't match
- // the existing headers, remaining transactions do not also try to match the
- // existing entry in parallel leading to wasted network requests. If the
- // headers do not match, this entry will be doomed.
- //
- // A transaction goes through these state transitions.
- //
- // Write mode transactions:
- // add_to_entry_queue-> headers_transaction -> writer
- // add_to_entry_queue-> headers_transaction -> done_headers_queue -> readers
- // (once the data is written to the cache by another writer)
- //
- // Read only transactions:
- // add_to_entry_queue-> headers_transaction -> done_headers_queue -> readers
- // (once the data is written to the cache by the writer)
-
struct ActiveEntry {
explicit ActiveEntry(disk_cache::Entry* entry);
~ActiveEntry();
@@ -278,36 +256,12 @@
// Returns true if no transactions are associated with this entry.
bool HasNoTransactions();
- // Returns true if no active readers/writer transactions are associated
- // with this entry.
- bool HasNoActiveTransactions();
-
- disk_cache::Entry* disk_entry = nullptr;
-
- // Transactions waiting to be added to entry.
- TransactionList add_to_entry_queue;
-
- // Transaction currently in the headers phase, either validating the
- // response or getting new headers. This can exist simultaneously with
- // writer or readers while validating existing headers.
- Transaction* headers_transaction = nullptr;
-
- // Transactions that have completed their headers phase and are waiting
- // to read the response body or write the response body.
- TransactionList done_headers_queue;
-
- // Transaction currently reading from the network and writing to the cache.
- Transaction* writer = nullptr;
-
- // Transactions that can only read from the cache. Only one of writer or
- // readers can exist at a time.
+ disk_cache::Entry* disk_entry;
+ Transaction* writer;
TransactionSet readers;
-
- // The following variables are true if OnProcessQueuedTransactions is posted
- bool will_process_queued_transactions = false;
-
- // True if entry is doomed.
- bool doomed = false;
+ TransactionList pending_queue;
+ bool will_process_pending_queue;
+ bool doomed;
};
using ActiveEntriesMap =
@@ -388,73 +342,28 @@
// Destroys an ActiveEntry (active or doomed).
void DestroyEntry(ActiveEntry* entry);
- // Adds a transaction to an ActiveEntry. This method returns ERR_IO_PENDING
- // and the transaction will be notified about completion via its IO callback.
- // In a failure case, the callback will be invoked with ERR_CACHE_RACE.
- int AddTransactionToEntry(ActiveEntry* entry, Transaction* transaction);
-
- // Transaction invokes this when its response headers phase is complete
- // If the transaction is responsible for writing the response body,
- // it becomes the writer and returns OK. In other cases ERR_IO_PENDING is
- // returned and the transaction will be notified about completion via its
- // IO callback. In a failure case, the callback will be invoked with
- // ERR_CACHE_RACE.
- int DoneWithResponseHeaders(ActiveEntry* entry, Transaction* transaction);
+ // Adds a transaction to an ActiveEntry. If this method returns ERR_IO_PENDING
+ // the transaction will be notified about completion via its IO callback. This
+ // method returns ERR_CACHE_RACE to signal the transaction that it cannot be
+ // added to the provided entry, and it should retry the process with another
+ // one (in this case, the entry is no longer valid).
+ int AddTransactionToEntry(ActiveEntry* entry, Transaction* trans);
// Called when the transaction has finished working with this entry. |cancel|
// is true if the operation was cancelled by the caller instead of running
// to completion.
- void DoneWithEntry(ActiveEntry* entry, Transaction* transaction, bool cancel);
+ void DoneWithEntry(ActiveEntry* entry, Transaction* trans, bool cancel);
// Called when the transaction has finished writing to this entry. |success|
// is false if the cache entry should be deleted.
- void DoneWritingToEntry(ActiveEntry* entry,
- bool success,
- Transaction* transaction);
+ void DoneWritingToEntry(ActiveEntry* entry, bool success);
// Called when the transaction has finished reading from this entry.
- void DoneReadingFromEntry(ActiveEntry* entry, Transaction* transaction);
+ void DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans);
- // Removes and returns all queued transactions in |entry| in FIFO order. This
- // includes transactions that have completed the headers phase and those that
- // have not been added to the entry yet in that order. |list| is the output
- // argument.
- void RemoveAllQueuedTransactions(ActiveEntry* entry, TransactionList* list);
-
- // Processes either writer's failure to write response body or
- // headers_transactions's failure to write headers. Also invoked when headers
- // transaction's validation result is not a match.
- void ProcessEntryFailure(ActiveEntry* entry);
-
- // Resumes processing the queued transactions of |entry|.
- void ProcessQueuedTransactions(ActiveEntry* entry);
-
- // Checks if a transaction can be added to the entry. If yes, it will
- // invoke the IO callback of the transaction. This is a helper function for
- // OnProcessQueuedTransactions. It will take a transaction from
- // add_to_entry_queue and make it a headers_transaction, if one doesn't exist
- // already.
- void ProcessAddToEntryQueue(ActiveEntry* entry);
-
- // Invoked when a transaction that has already completed the response headers
- // phase can resume reading/writing the response body. It will invoke the IO
- // callback of the transaction. This is a helper function for
- // OnProcessQueuedTransactions.
- void ProcessDoneHeadersQueue(ActiveEntry* entry);
-
- // Returns true if this transaction can write headers to the entry.
- bool CanTransactionWriteResponseHeaders(ActiveEntry* entry,
- Transaction* transaction,
- bool is_match) const;
-
- // Returns true if |transaction| is about to start writing response body or
- // already started but not yet finished.
- bool IsTransactionWritingIncomplete(ActiveEntry* entry,
- Transaction* transaction,
- const std::string& method) const;
-
- // Returns true if a transaction is currently writing the response body.
- bool IsWritingInProgress(ActiveEntry* entry) const;
+ // Converts the active writer transaction to a reader so that other
+ // transactions can start reading from this entry.
+ void ConvertWriterToReader(ActiveEntry* entry);
// Returns the LoadState of the provided pending transaction.
LoadState GetLoadStateForPendingTransaction(const Transaction* trans);
@@ -470,10 +379,12 @@
// Removes the transaction |trans|, from the pending list of |pending_op|.
bool RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
Transaction* trans);
+ // Resumes processing the pending list of |entry|.
+ void ProcessPendingQueue(ActiveEntry* entry);
// Events (called via PostTask) ---------------------------------------------
- void OnProcessQueuedTransactions(ActiveEntry* entry);
+ void OnProcessPendingQueue(ActiveEntry* entry);
// Callbacks ----------------------------------------------------------------
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index b10bf90..5189446 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -156,7 +156,6 @@
new_entry_(NULL),
new_response_(NULL),
mode_(NONE),
- original_mode_(NONE),
reading_(false),
invalid_range_(false),
truncated_(false),
@@ -197,12 +196,16 @@
if (cache_) {
if (entry_) {
- bool writing_incomplete = cache_->IsTransactionWritingIncomplete(
- entry_, this, request_->method);
- if (writing_incomplete && partial_)
- entry_->disk_entry->CancelSparseIO();
+ bool cancel_request = reading_ && response_.headers.get();
+ if (cancel_request) {
+ if (partial_) {
+ entry_->disk_entry->CancelSparseIO();
+ } else {
+ cancel_request &= (response_.headers->response_code() == 200);
+ }
+ }
- cache_->DoneWithEntry(entry_, this, writing_incomplete);
+ cache_->DoneWithEntry(entry_, this, cancel_request);
} else if (cache_pending_) {
cache_->RemovePendingTransaction(this);
}
@@ -562,12 +565,6 @@
old_connection_attempts_.end());
}
-void HttpCache::Transaction::SetValidatingCannotProceed() {
- DCHECK(!reading_);
- next_state_ = STATE_HEADERS_PHASE_CANNOT_PROCEED;
- entry_ = nullptr;
-}
-
size_t HttpCache::Transaction::EstimateMemoryUsage() const {
// TODO(xunjieli): Consider improving the coverage. crbug.com/669108.
return 0;
@@ -582,7 +579,7 @@
// GetBackend* -> InitEntry -> OpenEntry* -> CreateEntry* -> AddToEntry* ->
// SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse ->
// CacheWriteResponse* -> TruncateCachedData* -> TruncateCachedMetadata* ->
-// PartialHeadersReceived -> FinishHeaders*
+// PartialHeadersReceived
//
// Read():
// NetworkRead* -> CacheWriteData*
@@ -591,7 +588,7 @@
// Start():
// GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
// -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
-// BeginCacheValidation() -> SetupEntryForRead() -> FinishHeaders*
+// BeginCacheValidation() -> SetupEntryForRead()
//
// Read():
// CacheReadData*
@@ -603,7 +600,7 @@
// BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
// UpdateCachedResponse -> CacheWriteUpdatedResponse* ->
// UpdateCachedResponseComplete -> OverwriteCachedResponse ->
-// PartialHeadersReceived -> FinishHeaders*
+// PartialHeadersReceived
//
// Read():
// CacheReadData*
@@ -614,7 +611,7 @@
// -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
// BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
// OverwriteCachedResponse -> CacheWriteResponse* -> DoTruncateCachedData* ->
-// TruncateCachedMetadata* -> PartialHeadersReceived -> FinishHeaders*
+// TruncateCachedMetadata* -> PartialHeadersReceived
//
// Read():
// NetworkRead* -> CacheWriteData*
@@ -628,7 +625,7 @@
// BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
// UpdateCachedResponse -> CacheWriteUpdatedResponse* ->
// UpdateCachedResponseComplete -> OverwriteCachedResponse ->
-// PartialHeadersReceived -> FinishHeaders*
+// PartialHeadersReceived
//
// Read() 1:
// NetworkRead* -> CacheWriteData*
@@ -669,7 +666,7 @@
// GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
// -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
// BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
-// OverwriteCachedResponse -> FinishHeaders*
+// OverwriteCachedResponse
//
// 10. HEAD. Sparse entry, partially cached:
// Serve the request from the cache, as long as it doesn't require
@@ -694,7 +691,7 @@
// GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
// -> CacheToggleUnusedSincePrefetch* -> CacheDispatchValidation ->
// BeginPartialCacheValidation() -> BeginCacheValidation() ->
-// SetupEntryForRead() -> FinishHeaders*
+// SetupEntryForRead()
//
// Read():
// CacheReadData*
@@ -708,9 +705,8 @@
DCHECK(!in_do_loop_);
int rv = result;
- State state = next_state_;
do {
- state = next_state_;
+ State state = next_state_;
next_state_ = STATE_UNSET;
base::AutoReset<bool> scoped_in_do_loop(&in_do_loop_, true);
@@ -847,15 +843,6 @@
case STATE_CACHE_READ_METADATA_COMPLETE:
rv = DoCacheReadMetadataComplete(rv);
break;
- case STATE_HEADERS_PHASE_CANNOT_PROCEED:
- rv = DoHeadersPhaseCannotProceed();
- break;
- case STATE_FINISH_HEADERS:
- rv = DoFinishHeaders(rv);
- break;
- case STATE_FINISH_HEADERS_COMPLETE:
- rv = DoFinishHeadersComplete(rv);
- break;
case STATE_NETWORK_READ:
DCHECK_EQ(OK, rv);
rv = DoNetworkRead();
@@ -892,13 +879,8 @@
} while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
- // Assert Start() state machine's allowed last state in successful cases when
- // caching is happening.
- DCHECK(reading_ || rv != OK || !entry_ ||
- state == STATE_FINISH_HEADERS_COMPLETE);
-
if (rv != ERR_IO_PENDING && !callback_.is_null()) {
- read_buf_ = nullptr; // Release the buffer before invoking the callback.
+ read_buf_ = NULL; // Release the buffer before invoking the callback.
base::ResetAndReturn(&callback_).Run(rv);
}
@@ -925,7 +907,7 @@
if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
// The client has asked for nonsense.
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_CACHE_MISS;
}
mode_ = READ;
@@ -964,7 +946,7 @@
// If must use cache, then we must fail. This can happen for back/forward
// navigations to a page generated via a form post.
if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_CACHE_MISS;
}
@@ -981,9 +963,6 @@
// This is only set if we have something to do with the response.
range_requested_ = (partial_.get() != NULL);
- // mode_ may change later, save the initial mode in case we need to restart
- // this request.
- original_mode_ = mode_;
return OK;
}
@@ -992,7 +971,7 @@
DCHECK(!new_entry_);
if (!cache_.get()) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_UNEXPECTED;
}
@@ -1029,7 +1008,7 @@
}
if (result == ERR_CACHE_RACE) {
- TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
+ TransitionToState(STATE_INIT_ENTRY);
return OK;
}
@@ -1055,7 +1034,7 @@
// The entry does not exist, and we are not permitted to create a new entry,
// so we must fail.
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_CACHE_MISS;
}
@@ -1074,9 +1053,8 @@
net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_DOOM_ENTRY,
result);
cache_pending_ = false;
- TransitionToState(result == ERR_CACHE_RACE
- ? STATE_HEADERS_PHASE_CANNOT_PROCEED
- : STATE_CREATE_ENTRY);
+ TransitionToState(result == ERR_CACHE_RACE ? STATE_INIT_ENTRY
+ : STATE_CREATE_ENTRY);
return OK;
}
@@ -1103,7 +1081,7 @@
break;
case ERR_CACHE_RACE:
- TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
+ TransitionToState(STATE_INIT_ENTRY);
break;
default:
@@ -1184,13 +1162,13 @@
new_entry_ = NULL;
if (result == ERR_CACHE_RACE) {
- TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
+ TransitionToState(STATE_INIT_ENTRY);
return OK;
}
if (result == ERR_CACHE_LOCK_TIMEOUT) {
if (mode_ == READ) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_CACHE_MISS;
}
@@ -1204,16 +1182,12 @@
return OK;
}
- // TODO(crbug.com/713354) Access timestamp for histograms only if entry is
- // already written, to avoid data race since cache thread can also access
- // this.
- if (!cache_->IsWritingInProgress(entry_))
- open_entry_last_used_ = entry_->disk_entry->GetLastUsed();
+ open_entry_last_used_ = entry_->disk_entry->GetLastUsed();
// TODO(jkarlin): We should either handle the case or DCHECK.
if (result != OK) {
NOTREACHED();
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return result;
}
@@ -1252,37 +1226,29 @@
return OnCacheReadError(result, true);
}
- // TODO(crbug.com/713354) Only get data size if there is no other transaction
- // currently writing the response body due to the data race mentioned in the
- // associated bug.
- if (!cache_->IsWritingInProgress(entry_)) {
- int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
- int64_t full_response_length = response_.headers->GetContentLength();
+ int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
+ int64_t full_response_length = response_.headers->GetContentLength();
- // Some resources may have slipped in as truncated when they're not.
- if (full_response_length == current_size)
- truncated_ = false;
+ // Some resources may have slipped in as truncated when they're not.
+ if (full_response_length == current_size)
+ truncated_ = false;
- // The state machine's handling of StopCaching unfortunately doesn't deal
- // well with resources that are larger than 2GB when there is a truncated or
- // sparse cache entry. While the state machine is reworked to resolve this,
- // the following logic is put in place to defer such requests to the
- // network. The cache should not be storing multi gigabyte resources. See
- // http://crbug.com/89567.
- if ((truncated_ || response_.headers->response_code() == 206) &&
- !range_requested_ &&
- full_response_length > std::numeric_limits<int32_t>::max()) {
- DCHECK(!partial_);
-
- // Doom the entry so that no other transaction gets added to this entry
- // and avoid a race of not being able to check this condition because
- // writing is in progress.
- cache_->DoneWritingToEntry(entry_, false, this);
- entry_ = nullptr;
- mode_ = NONE;
- TransitionToState(STATE_SEND_REQUEST);
- return OK;
- }
+ // The state machine's handling of StopCaching unfortunately doesn't deal well
+ // with resources that are larger than 2GB when there is a truncated or sparse
+ // cache entry. While the state machine is reworked to resolve this, the
+ // following logic is put in place to defer such requests to the network. The
+ // cache should not be storing multi gigabyte resources. See
+ // http://crbug.com/89567.
+ if ((truncated_ || response_.headers->response_code() == 206) &&
+ !range_requested_ &&
+ full_response_length > std::numeric_limits<int32_t>::max()) {
+ // Does not release the cache entry. If another transaction wants to use
+ // this cache entry while this transaction is active, the second transaction
+ // will fall back to the network after the timeout.
+ DCHECK(!partial_);
+ mode_ = NONE;
+ TransitionToState(STATE_SEND_REQUEST);
+ return OK;
}
if (response_.unused_since_prefetch !=
@@ -1364,7 +1330,7 @@
int HttpCache::Transaction::DoCacheQueryDataComplete(int result) {
DCHECK_EQ(OK, result);
if (!cache_.get()) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_UNEXPECTED;
}
@@ -1374,7 +1340,7 @@
// We may end up here multiple times for a given request.
int HttpCache::Transaction::DoStartPartialCacheValidation() {
if (mode_ == NONE) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return OK;
}
@@ -1391,12 +1357,12 @@
cache_->DoneReadingFromEntry(entry_, this);
entry_ = NULL;
}
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return result;
}
if (result < 0) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return result;
}
@@ -1422,7 +1388,7 @@
int rv =
cache_->network_layer_->CreateTransaction(priority_, &network_trans_);
if (rv != OK) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return rv;
}
network_trans_->SetBeforeNetworkStartCallback(before_network_start_callback_);
@@ -1444,7 +1410,7 @@
int HttpCache::Transaction::DoSendRequestComplete(int result) {
TRACE_EVENT0("io", "HttpCacheTransaction::DoSendRequestComplete");
if (!cache_.get()) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_UNEXPECTED;
}
@@ -1475,7 +1441,7 @@
DoneWritingToEntry(true);
}
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return result;
}
@@ -1489,7 +1455,7 @@
new_response->headers->response_code() == 407) {
SetAuthResponse(*new_response);
if (!reading_) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return OK;
}
@@ -1514,7 +1480,7 @@
mode_ = NONE;
partial_.reset();
ResetNetworkTransaction();
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_CACHE_AUTH_FAILURE_AFTER_READ;
}
@@ -1552,7 +1518,7 @@
int ret = cache_->DoomEntry(cache_key_, NULL);
DCHECK_EQ(OK, ret);
}
- cache_->DoneWritingToEntry(entry_, true, this);
+ cache_->DoneWritingToEntry(entry_, true);
entry_ = NULL;
mode_ = NONE;
}
@@ -1570,7 +1536,7 @@
(request_->method == "GET" || request_->method == "POST")) {
// If there is an active entry it may be destroyed with this transaction.
SetResponse(*new_response_);
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return OK;
}
@@ -1658,6 +1624,7 @@
} else if (entry_ && !handling_206_) {
DCHECK_EQ(READ_WRITE, mode_);
if (!partial_ || partial_->IsLastRange()) {
+ cache_->ConvertWriterToReader(entry_);
mode_ = READ;
}
// We no longer need the network transaction, so destroy it.
@@ -1695,7 +1662,7 @@
DoneWritingToEntry(false);
mode_ = NONE;
new_response_ = NULL;
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return OK;
}
@@ -1716,19 +1683,6 @@
int HttpCache::Transaction::DoCacheWriteResponse() {
TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponse");
TransitionToState(STATE_CACHE_WRITE_RESPONSE_COMPLETE);
-
- // Invalidate any current entry with a successful response if this transaction
- // cannot write to this entry. This transaction then continues to read from
- // the network without writing to the backend.
- bool is_match = response_.headers->response_code() == 304;
- if (entry_ && response_.headers &&
- !cache_->CanTransactionWriteResponseHeaders(entry_, this, is_match)) {
- cache_->DoneWritingToEntry(entry_, false, this);
- entry_ = nullptr;
- mode_ = NONE;
- return OK;
- }
-
return WriteResponseInfoToEntry(truncated_);
}
@@ -1790,12 +1744,10 @@
new_response_ = NULL;
if (!partial_) {
- if (entry_ && entry_->disk_entry->GetDataSize(kMetadataIndex)) {
+ if (entry_ && entry_->disk_entry->GetDataSize(kMetadataIndex))
TransitionToState(STATE_CACHE_READ_METADATA);
- } else {
- DCHECK(!reading_);
- TransitionToState(STATE_FINISH_HEADERS);
- }
+ else
+ TransitionToState(STATE_NONE);
return OK;
}
@@ -1809,61 +1761,11 @@
// We are about to return the headers for a byte-range request to the user,
// so let's fix them.
partial_->FixResponseHeaders(response_.headers.get(), true);
- TransitionToState(STATE_FINISH_HEADERS);
- } else {
- TransitionToState(STATE_FINISH_HEADERS);
- }
- return OK;
-}
-
-int HttpCache::Transaction::DoHeadersPhaseCannotProceed() {
- // If its the Start state machine and it cannot proceed due to a cache
- // failure, restart this transaction.
- DCHECK(!reading_);
- TransitionToState(STATE_INIT_ENTRY);
- cache_entry_status_ = CacheEntryStatus::ENTRY_UNDEFINED;
- entry_ = nullptr;
- mode_ = original_mode_;
- if (network_trans_)
- network_trans_.reset();
- return OK;
-}
-
-int HttpCache::Transaction::DoFinishHeaders(int result) {
- if (!entry_ || result != OK) {
TransitionToState(STATE_NONE);
- return result;
+ } else {
+ TransitionToState(STATE_NONE);
}
-
- TransitionToState(STATE_FINISH_HEADERS_COMPLETE);
-
- // If it was an auth failure or 416, this transaction should continue to be
- // headers_transaction till consumer takes an action, so no need to do
- // anything now.
- if (auth_response_.headers.get() ||
- (new_response_ && new_response_->headers &&
- new_response_->headers->response_code() == 416))
- return OK;
-
- // If there is no response body to be written or read, it does not need to
- // wait.
- if (request_->method == "HEAD")
- return OK;
-
- // If the transaction needs to wait because another transaction is still
- // writing the response body, it will return ERR_IO_PENDING now and the
- // io_callback_ will be invoked when the wait is done.
- return cache_->DoneWithResponseHeaders(entry_, this);
-}
-
-int HttpCache::Transaction::DoFinishHeadersComplete(int rv) {
- if (rv == ERR_CACHE_RACE) {
- TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
- return OK;
- }
-
- TransitionToState(STATE_NONE);
- return rv;
+ return OK;
}
int HttpCache::Transaction::DoCacheReadMetadata() {
@@ -1888,8 +1790,7 @@
result);
if (result != response_.metadata->size())
return OnCacheReadError(result, false);
-
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return OK;
}
@@ -2186,18 +2087,18 @@
// TODO(jkarlin): Either handle this case or DCHECK.
if (response_.headers->response_code() == 206 || partial_) {
NOTREACHED();
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_CACHE_MISS;
}
// We don't have the whole resource.
if (truncated_) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_CACHE_MISS;
}
if (RequiresValidation()) {
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return ERR_CACHE_MISS;
}
@@ -2207,7 +2108,7 @@
if (entry_->disk_entry->GetDataSize(kMetadataIndex))
TransitionToState(STATE_CACHE_READ_METADATA);
else
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return OK;
}
@@ -2667,7 +2568,7 @@
partial_.reset();
}
}
-
+ cache_->ConvertWriterToReader(entry_);
mode_ = READ;
if (request_->method == "HEAD")
@@ -2676,7 +2577,7 @@
if (entry_->disk_entry->GetDataSize(kMetadataIndex))
TransitionToState(STATE_CACHE_READ_METADATA);
else
- TransitionToState(STATE_FINISH_HEADERS);
+ TransitionToState(STATE_NONE);
return OK;
}
@@ -2755,7 +2656,7 @@
RecordHistograms();
- cache_->DoneWritingToEntry(entry_, success, this);
+ cache_->DoneWritingToEntry(entry_, success);
entry_ = NULL;
mode_ = NONE; // switch to 'pass through' mode
}
@@ -2966,14 +2867,10 @@
cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE);
int64_t freshness_periods_since_last_used = 0;
- if (stale_request && !open_entry_last_used_.is_null()) {
- // Note that we are not able to capture those transactions' histograms which
- // when added to entry, the response was being written by another
- // transaction because getting the last used timestamp might lead to a data
- // race in that case. TODO(crbug.com/713354).
-
+ if (stale_request) {
// For stale entries, record how many freshness periods have elapsed since
// the entry was last used.
+ DCHECK(!open_entry_last_used_.is_null());
DCHECK(!stale_entry_freshness_.is_zero());
base::TimeDelta time_since_use = base::Time::Now() - open_entry_last_used_;
freshness_periods_since_last_used =
diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h
index 6811dd4..51c0db7 100644
--- a/net/http/http_cache_transaction.h
+++ b/net/http/http_cache_transaction.h
@@ -164,10 +164,6 @@
int ResumeNetworkStart() override;
void GetConnectionAttempts(ConnectionAttempts* out) const override;
- // Invoked when parallel validation cannot proceed due to response failure
- // and this transaction needs to be restarted.
- void SetValidatingCannotProceed();
-
// Returns the estimate of dynamically allocated memory in bytes.
size_t EstimateMemoryUsage() const;
@@ -224,9 +220,6 @@
STATE_PARTIAL_HEADERS_RECEIVED,
STATE_CACHE_READ_METADATA,
STATE_CACHE_READ_METADATA_COMPLETE,
- STATE_HEADERS_PHASE_CANNOT_PROCEED,
- STATE_FINISH_HEADERS,
- STATE_FINISH_HEADERS_COMPLETE,
// These states are entered from Read/AddTruncatedFlag.
STATE_NETWORK_READ,
@@ -295,9 +288,6 @@
int DoPartialHeadersReceived();
int DoCacheReadMetadata();
int DoCacheReadMetadataComplete(int result);
- int DoHeadersPhaseCannotProceed();
- int DoFinishHeaders(int result);
- int DoFinishHeadersComplete(int result);
int DoNetworkRead();
int DoNetworkReadComplete(int result);
int DoCacheReadData();
@@ -440,12 +430,7 @@
void SyncCacheEntryStatusToResponse();
void RecordHistograms();
- // Called to signal completion of asynchronous IO. Note that this callback is
- // used in the conventional sense where one layer calls the callback of the
- // layer above it e.g. this callback gets called from the network transaction
- // layer. In addition, it is also used for HttpCache layer to let this
- // transaction know when it is out of a queued state in ActiveEntry and can
- // continue its processing.
+ // Called to signal completion of asynchronous IO.
void OnIOComplete(int result);
// When in a DoLoop, use this to set the next state as it verifies that the
@@ -471,7 +456,6 @@
const HttpResponseInfo* new_response_;
std::string cache_key_;
Mode mode_;
- Mode original_mode_; // Used when restarting the transaction.
bool reading_; // We are already reading. Never reverts to false once set.
bool invalid_range_; // We may bypass the cache for this request.
bool truncated_; // We don't have all the response data.
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 626f46a..ca9c5d9 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -147,10 +147,6 @@
EXPECT_TRUE(load_timing_info.receive_headers_end.is_null());
}
-void DeferNetworkStart(bool* defer) {
- *defer = true;
-}
-
class DeleteCacheCompletionCallback : public TestCompletionCallbackBase {
public:
explicit DeleteCacheCompletionCallback(MockHttpCache* cache)
@@ -1354,12 +1350,12 @@
MockHttpRequest request(kSimpleGET_Transaction);
- std::vector<std::unique_ptr<Context>> context_list;
+ std::vector<Context*> context_list;
const int kNumTransactions = 5;
for (int i = 0; i < kNumTransactions; ++i) {
- context_list.push_back(base::MakeUnique<Context>());
- auto& c = context_list[i];
+ context_list.push_back(new Context());
+ Context* c = context_list[i];
c->result = cache.CreateTransaction(&c->trans);
ASSERT_THAT(c->result, IsOk());
@@ -1370,19 +1366,16 @@
}
// All requests are waiting for the active entry.
- for (auto& context : context_list) {
- EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
}
// Allow all requests to move from the Create queue to the active entry.
base::RunLoop().RunUntilIdle();
// The first request should be a writer at this point, and the subsequent
- // requests should have passed the validation phase and waiting for the
- // response to be written to the cache before they can read.
- EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
- EXPECT_EQ(kNumTransactions - 1,
- cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+ // requests should be pending.
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -1390,21 +1383,15 @@
// All requests depend on the writer, and the writer is between Start and
// Read, i.e. idle.
- for (auto& context : context_list) {
- EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
}
for (int i = 0; i < kNumTransactions; ++i) {
- auto& c = context_list[i];
+ Context* c = context_list[i];
if (c->result == ERR_IO_PENDING)
c->result = c->callback.WaitForResult();
-
- if (i > 0) {
- EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
- EXPECT_EQ(kNumTransactions - i,
- cache.GetCountReaders(kSimpleGET_Transaction.url));
- }
-
ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
}
@@ -1413,479 +1400,11 @@
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
EXPECT_EQ(1, cache.disk_cache()->create_count());
-}
-
-// Parallel validation results in 200.
-TEST(HttpCache, SimpleGET_ParallelValidationNoMatch) {
- MockHttpCache cache;
-
- MockHttpRequest request(kSimpleGET_Transaction);
- request.load_flags |= LOAD_VALIDATE_CACHE;
-
- std::vector<std::unique_ptr<Context>> context_list;
- const int kNumTransactions = 5;
for (int i = 0; i < kNumTransactions; ++i) {
- context_list.push_back(base::MakeUnique<Context>());
- auto& c = context_list[i];
-
- c->result = cache.CreateTransaction(&c->trans);
- ASSERT_THAT(c->result, IsOk());
- EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
-
- c->result =
- c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ Context* c = context_list[i];
+ delete c;
}
-
- // All requests are waiting for the active entry.
- for (auto& context : context_list) {
- EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
- }
-
- // Allow all requests to move from the Create queue to the active entry.
- base::RunLoop().RunUntilIdle();
-
- // The first request should be a writer at this point, and the subsequent
- // requests should have passed the validation phase and created their own
- // entries since none of them matched the headers of the earlier one.
- EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
-
- // Note that there are only 3 entries created and not 5 since every other
- // transaction would have gone to the network.
- EXPECT_EQ(5, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(3, cache.disk_cache()->create_count());
-
- // All requests depend on the writer, and the writer is between Start and
- // Read, i.e. idle.
- for (auto& context : context_list) {
- EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
- }
-
- for (auto& context : context_list) {
- if (context->result == ERR_IO_PENDING)
- context->result = context->callback.WaitForResult();
- ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
- }
-
- EXPECT_EQ(5, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(3, cache.disk_cache()->create_count());
-}
-
-// Tests that a GET followed by a DELETE results in DELETE immediately starting
-// the headers phase and the entry is doomed.
-TEST(HttpCache, SimpleGET_ParallelValidationDelete) {
- MockHttpCache cache;
-
- MockHttpRequest request(kSimpleGET_Transaction);
- request.load_flags |= LOAD_VALIDATE_CACHE;
-
- MockHttpRequest delete_request(kSimpleGET_Transaction);
- delete_request.method = "DELETE";
-
- std::vector<std::unique_ptr<Context>> context_list;
- const int kNumTransactions = 2;
-
- for (int i = 0; i < kNumTransactions; ++i) {
- context_list.push_back(base::MakeUnique<Context>());
- auto& c = context_list[i];
-
- MockHttpRequest* this_request = &request;
- if (i == 1)
- this_request = &delete_request;
-
- c->result = cache.CreateTransaction(&c->trans);
- ASSERT_THAT(c->result, IsOk());
- EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
-
- c->result = c->trans->Start(this_request, c->callback.callback(),
- NetLogWithSource());
- }
-
- // All requests are waiting for the active entry.
- for (auto& context : context_list) {
- EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
- }
-
- // Allow all requests to move from the Create queue to the active entry.
- base::RunLoop().RunUntilIdle();
-
- // The first request should be a writer at this point, and the subsequent
- // request should have passed the validation phase and doomed the existing
- // entry.
- EXPECT_TRUE(
- cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
-
- EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
-
- // All requests depend on the writer, and the writer is between Start and
- // Read, i.e. idle.
- for (auto& context : context_list) {
- EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
- }
-
- for (auto& context : context_list) {
- if (context->result == ERR_IO_PENDING)
- context->result = context->callback.WaitForResult();
- ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
- }
-
- EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
-}
-
-// Tests that a transaction which is in validated queue can be destroyed without
-// any impact to other transactions.
-TEST(HttpCache, SimpleGET_ParallelValidationCancelValidated) {
- MockHttpCache cache;
-
- MockHttpRequest request(kSimpleGET_Transaction);
-
- std::vector<std::unique_ptr<Context>> context_list;
- const int kNumTransactions = 2;
-
- for (int i = 0; i < kNumTransactions; ++i) {
- context_list.push_back(base::MakeUnique<Context>());
- auto& c = context_list[i];
-
- c->result = cache.CreateTransaction(&c->trans);
- ASSERT_THAT(c->result, IsOk());
-
- c->result =
- c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
- }
-
- // Allow all requests to move from the Create queue to the active entry.
- base::RunLoop().RunUntilIdle();
-
- // The first request should be a writer at this point, and the subsequent
- // requests should have completed validation.
-
- EXPECT_EQ(1, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
-
- EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
- EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
-
- context_list[1].reset();
-
- EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
-
- // Complete the rest of the transactions.
- for (auto& context : context_list) {
- if (!context)
- continue;
- ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
- }
-
- EXPECT_EQ(1, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
-}
-
-// Tests that a transaction which is in readers can be destroyed without
-// any impact to other transactions.
-TEST(HttpCache, SimpleGET_ParallelValidationCancelReader) {
- MockHttpCache cache;
-
- MockHttpRequest request(kSimpleGET_Transaction);
-
- MockTransaction transaction(kSimpleGET_Transaction);
- transaction.load_flags |= LOAD_VALIDATE_CACHE;
- MockHttpRequest validate_request(transaction);
-
- int kNumTransactions = 4;
- std::vector<std::unique_ptr<Context>> context_list;
-
- for (int i = 0; i < kNumTransactions; ++i) {
- context_list.push_back(base::MakeUnique<Context>());
- auto& c = context_list[i];
-
- c->result = cache.CreateTransaction(&c->trans);
- ASSERT_THAT(c->result, IsOk());
-
- MockHttpRequest* this_request = &request;
- if (i == 3) {
- this_request = &validate_request;
- c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
- }
-
- c->result = c->trans->Start(this_request, c->callback.callback(),
- NetLogWithSource());
- }
-
- // Allow all requests to move from the Create queue to the active entry.
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
-
- EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
- EXPECT_EQ(2, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
- EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
-
- // Complete the response body.
- auto& c = context_list[0];
- ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
-
- // Rest of the transactions should move to readers.
- EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
- EXPECT_EQ(2, cache.GetCountReaders(kSimpleGET_Transaction.url));
- EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
- EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
-
- // Add 2 new transactions.
- kNumTransactions = 6;
-
- for (int i = 4; i < kNumTransactions; ++i) {
- context_list.push_back(base::MakeUnique<Context>());
- auto& c = context_list[i];
-
- c->result = cache.CreateTransaction(&c->trans);
- ASSERT_THAT(c->result, IsOk());
-
- c->result =
- c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
- }
-
- EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
-
- // Delete a reader.
- context_list[1].reset();
-
- // Deleting the reader did not impact any other transaction.
- EXPECT_EQ(1, cache.GetCountReaders(kSimpleGET_Transaction.url));
- EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
- EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
-
- // Resume network start for headers_transaction. It will doom the entry as it
- // will be a 200 and will go to network for the response body.
- auto& context = context_list[3];
- context->trans->ResumeNetworkStart();
-
- // The pending transactions will be added to a new entry.
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
- EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
-
- // Complete the rest of the transactions.
- for (int i = 2; i < kNumTransactions; ++i) {
- auto& c = context_list[i];
- ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
- }
-
- EXPECT_EQ(3, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(2, cache.disk_cache()->create_count());
-}
-
-// Tests that a transaction is in validated queue and writer is destroyed
-// leading to restarting the validated transaction.
-TEST(HttpCache, SimpleGET_ParallelValidationCancelWriter) {
- MockHttpCache cache;
-
- MockHttpRequest request(kSimpleGET_Transaction);
-
- MockTransaction transaction(kSimpleGET_Transaction);
- transaction.load_flags |= LOAD_VALIDATE_CACHE;
- MockHttpRequest validate_request(transaction);
-
- const int kNumTransactions = 3;
- std::vector<std::unique_ptr<Context>> context_list;
-
- for (int i = 0; i < kNumTransactions; ++i) {
- context_list.push_back(base::MakeUnique<Context>());
- auto& c = context_list[i];
-
- c->result = cache.CreateTransaction(&c->trans);
- ASSERT_THAT(c->result, IsOk());
-
- MockHttpRequest* this_request = &request;
- if (i == 2) {
- this_request = &validate_request;
- c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
- }
-
- c->result = c->trans->Start(this_request, c->callback.callback(),
- NetLogWithSource());
- }
-
- // Allow all requests to move from the Create queue to the active entry.
- base::RunLoop().RunUntilIdle();
-
- EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
-
- EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
- EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
- EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
-
- // Deleting the writer at this point will lead to destroying the entry and
- // restarting the remaining transactions which will then create a new entry.
- context_list[0].reset();
-
- // Resume network start for headers_transaction. It should be restarted due to
- // writer cancellation.
- auto& c = context_list[2];
- c->trans->ResumeNetworkStart();
-
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
- EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
-
- // Resume network start for the transaction the second time.
- c->trans->ResumeNetworkStart();
- base::RunLoop().RunUntilIdle();
-
- // Headers transaction would have doomed the new entry created.
- EXPECT_TRUE(
- cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
-
- // Complete the rest of the transactions.
- for (auto& context : context_list) {
- if (!context)
- continue;
- ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
- }
-
- EXPECT_EQ(4, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(2, cache.disk_cache()->create_count());
-}
-
-// Tests that a transaction is currently in headers phase and is destroyed
-// leading to destroying the entry.
-TEST(HttpCache, SimpleGET_ParallelValidationCancelHeaders) {
- MockHttpCache cache;
-
- MockHttpRequest request(kSimpleGET_Transaction);
-
- const int kNumTransactions = 2;
- std::vector<std::unique_ptr<Context>> context_list;
-
- for (int i = 0; i < kNumTransactions; ++i) {
- context_list.push_back(base::MakeUnique<Context>());
- auto& c = context_list[i];
-
- c->result = cache.CreateTransaction(&c->trans);
- ASSERT_THAT(c->result, IsOk());
-
- if (i == 0)
- c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
-
- c->result =
- c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
- }
-
- base::RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
- EXPECT_EQ(1, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
-
- EXPECT_EQ(1, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
-
- // Delete the headers transaction.
- context_list[0].reset();
-
- base::RunLoop().RunUntilIdle();
-
- // Complete the rest of the transactions.
- for (auto& context : context_list) {
- if (!context)
- continue;
- ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
- }
-
- EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(2, cache.disk_cache()->create_count());
-}
-
-// Similar to the above test, except here cache write fails and the
-// validated transactions should be restarted.
-TEST(HttpCache, SimpleGET_ParallelValidationFailWrite) {
- MockHttpCache cache;
-
- MockHttpRequest request(kSimpleGET_Transaction);
-
- const int kNumTransactions = 5;
- std::vector<std::unique_ptr<Context>> context_list;
-
- for (int i = 0; i < kNumTransactions; ++i) {
- context_list.push_back(base::MakeUnique<Context>());
- auto& c = context_list[i];
-
- c->result = cache.CreateTransaction(&c->trans);
- ASSERT_THAT(c->result, IsOk());
- EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
-
- c->result =
- c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
- }
-
- // All requests are waiting for the active entry.
- for (auto& context : context_list) {
- EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
- }
-
- // Allow all requests to move from the Create queue to the active entry.
- base::RunLoop().RunUntilIdle();
-
- // The first request should be a writer at this point, and the subsequent
- // requests should have passed the validation phase and waiting for the
- // response to be written to the cache before they can read.
- EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
- EXPECT_EQ(4, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
-
- // All requests depend on the writer, and the writer is between Start and
- // Read, i.e. idle.
- for (auto& context : context_list) {
- EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
- }
-
- // The first request should be a writer at this point, and the subsequent
- // requests should have passed the validation phase and waiting for the
- // response to be written to the cache before they can read.
-
- EXPECT_EQ(1, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
-
- // Fail the request.
- cache.disk_cache()->set_soft_failures(true);
- // We have to open the entry again to propagate the failure flag.
- disk_cache::Entry* en;
- cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en);
- en->Close();
-
- for (int i = 0; i < kNumTransactions; ++i) {
- auto& c = context_list[i];
- if (c->result == ERR_IO_PENDING)
- c->result = c->callback.WaitForResult();
- if (i == 1) {
- // The earlier entry must be destroyed and its disk entry doomed.
- EXPECT_TRUE(
- cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
- }
- ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
- }
-
- // Since validated transactions were restarted and new entry read/write
- // operations would also fail, all requests would have gone to the network.
- EXPECT_EQ(5, cache.network_layer()->transaction_count());
- EXPECT_EQ(1, cache.disk_cache()->open_count());
- EXPECT_EQ(5, cache.disk_cache()->create_count());
}
// This is a test for http://code.google.com/p/chromium/issues/detail?id=4769.
@@ -1932,12 +1451,11 @@
c->result = c->callback.WaitForResult();
ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
- // Now all transactions should be waiting for read to be invoked. Two readers
- // are because of the load flags and remaining two transactions were converted
- // to readers after skipping validation. Note that the remaining two went on
- // to process the headers in parallel with readers present on the entry.
+ // Now we have 2 active readers and two queued transactions.
+
EXPECT_EQ(LOAD_STATE_IDLE, context_list[2]->trans->GetLoadState());
- EXPECT_EQ(LOAD_STATE_IDLE, context_list[3]->trans->GetLoadState());
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE,
+ context_list[3]->trans->GetLoadState());
c = context_list[1];
ASSERT_THAT(c->result, IsError(ERR_IO_PENDING));
@@ -2002,16 +1520,12 @@
NetLogWithSource());
}
- base::RunLoop().RunUntilIdle();
-
// The first request should be a writer at this point, and the two subsequent
// requests should be pending. The last request doomed the first entry.
EXPECT_EQ(2, cache.network_layer()->transaction_count());
- // Cancel the second transaction. Note that this and the 3rd transactions
- // would have completed their headers phase and would be waiting in the
- // done_headers_queue when the 2nd transaction is cancelled.
+ // Cancel the first queued transaction.
context_list[1].reset();
for (int i = 0; i < kNumTransactions; ++i) {
@@ -2053,12 +1567,11 @@
base::RunLoop().RunUntilIdle();
// The first request should be a writer at this point, and the subsequent
- // requests should have completed validation. Since the validation does not
- // result in a match, a new entry would be created.
+ // requests should be pending.
- EXPECT_EQ(3, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(2, cache.disk_cache()->create_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
// Now, make sure that the second request asks for the entry not to be stored.
request_handler.set_no_store(true);
@@ -2112,9 +1625,6 @@
if (c->result == ERR_IO_PENDING)
c->result = c->callback.WaitForResult();
// Destroy only the first transaction.
- // This should lead to all transactions to restart, even those that have
- // validated themselves and were waiting for the writer transaction to
- // complete writing to the cache.
if (i == 0) {
delete c;
context_list[i] = NULL;
@@ -3030,8 +2540,6 @@
// be returned.
// (4) loads |kUrl| from cache only -- expects |cached_response_2| to be
// returned.
-// The entry will be created once and will be opened for the 3 subsequent
-// requests.
static void ConditionalizedRequestUpdatesCacheHelper(
const Response& net_response_1,
const Response& net_response_2,
@@ -6607,14 +6115,12 @@
EXPECT_EQ(5, c->callback.GetResult(rv));
// Cancel the requests.
- // Since |pending| is currently validating the already written headers
- // it will be restarted as well.
delete c;
delete pending;
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(1, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
base::RunLoop().RunUntilIdle();
RemoveMockTransaction(&transaction);
@@ -7339,6 +6845,46 @@
EXPECT_EQ(1, cache.disk_cache()->create_count());
}
+// Tests that if a metadata writer transaction hits cache lock timeout, it will
+// error out.
+TEST(HttpCache, WriteMetadata_CacheLockTimeout) {
+ MockHttpCache cache;
+
+ // Write to the cache
+ HttpResponseInfo response;
+ RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+ &response);
+ EXPECT_FALSE(response.metadata.get());
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+ Context c1;
+ ASSERT_THAT(cache.CreateTransaction(&c1.trans), IsOk());
+ ASSERT_EQ(ERR_IO_PENDING, c1.trans->Start(&request, c1.callback.callback(),
+ NetLogWithSource()));
+
+ cache.SimulateCacheLockTimeout();
+
+ // Write meta data to the same entry.
+ scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(50));
+ memset(buf->data(), 0, buf->size());
+ base::strlcpy(buf->data(), "Hi there", buf->size());
+ cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
+ DEFAULT_PRIORITY, response.response_time,
+ buf.get(), buf->size());
+
+ // Release the buffer before the operation takes place.
+ buf = NULL;
+
+ // Makes sure we finish pending operations.
+ base::RunLoop().RunUntilIdle();
+
+ RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+ &response);
+
+ // The writer transaction should fail due to cache lock timeout.
+ ASSERT_FALSE(response.metadata.get());
+}
+
// Tests that we ignore VARY checks when writing metadata since the request
// headers for the WriteMetadata transaction are made up.
TEST(HttpCache, WriteMetadata_IgnoreVary) {
@@ -7666,6 +7212,10 @@
EXPECT_THAT(callback.GetResult(rv), IsOk());
trans->StopCaching();
+
+ scoped_refptr<IOBuffer> buf(new IOBuffer(256));
+ rv = trans->Read(buf.get(), 10, callback.callback());
+ EXPECT_EQ(callback.GetResult(rv), 10);
}
RemoveMockTransaction(&mock_transaction);
diff --git a/net/http/http_transaction_test_util.cc b/net/http/http_transaction_test_util.cc
index fa42dd7..a5b5746 100644
--- a/net/http/http_transaction_test_util.cc
+++ b/net/http/http_transaction_test_util.cc
@@ -409,6 +409,13 @@
if (!t)
return ERR_FAILED;
+ if (!before_network_start_callback_.is_null()) {
+ bool defer = false;
+ before_network_start_callback_.Run(&defer);
+ if (defer)
+ return net::ERR_IO_PENDING;
+ }
+
test_mode_ = t->test_mode;
// Return immediately if we're returning an error.
@@ -459,16 +466,6 @@
if (request_->load_flags & LOAD_PREFETCH)
response_.unused_since_prefetch = true;
- // Pause and resume.
- if (!before_network_start_callback_.is_null()) {
- bool defer = false;
- before_network_start_callback_.Run(&defer);
- if (defer) {
- callback_ = callback;
- return net::ERR_IO_PENDING;
- }
- }
-
if (test_mode_ & TEST_MODE_SYNC_NET_START)
return OK;
@@ -485,9 +482,8 @@
const BeforeHeadersSentCallback& callback) {}
int MockNetworkTransaction::ResumeNetworkStart() {
- DCHECK(!callback_.is_null());
- CallbackLater(callback_, OK);
- return ERR_IO_PENDING;
+ // Should not get here.
+ return ERR_FAILED;
}
void MockNetworkTransaction::GetConnectionAttempts(
diff --git a/net/http/http_transaction_test_util.h b/net/http/http_transaction_test_util.h
index dbd2670..e936ac9d 100644
--- a/net/http/http_transaction_test_util.h
+++ b/net/http/http_transaction_test_util.h
@@ -280,8 +280,6 @@
bool done_reading_called_;
- CompletionCallback callback_; // used for pause and restart.
-
base::WeakPtrFactory<MockNetworkTransaction> weak_factory_;
};
diff --git a/net/http/mock_http_cache.cc b/net/http/mock_http_cache.cc
index e676122..47125a30 100644
--- a/net/http/mock_http_cache.cc
+++ b/net/http/mock_http_cache.cc
@@ -534,13 +534,6 @@
FROM_HERE, base::Bind(&CallbackForwader, callback, result));
}
-bool MockDiskCache::IsDiskEntryDoomed(const std::string& key) {
- auto it = entries_.find(key);
- if (it == entries_.end())
- return false;
- return it->second->is_doomed();
-}
-
//-----------------------------------------------------------------------------
int MockBackendFactory::CreateBackend(
@@ -654,41 +647,6 @@
g_test_mode = test_mode;
}
-bool MockHttpCache::IsWriterPresent(const std::string& key) {
- HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
- if (entry)
- return entry->writer;
- return false;
-}
-
-bool MockHttpCache::IsHeadersTransactionPresent(const std::string& key) {
- HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
- if (entry)
- return entry->headers_transaction;
- return false;
-}
-
-int MockHttpCache::GetCountReaders(const std::string& key) {
- HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
- if (entry)
- return entry->readers.size();
- return false;
-}
-
-int MockHttpCache::GetCountAddToEntryQueue(const std::string& key) {
- HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
- if (entry)
- return entry->add_to_entry_queue.size();
- return false;
-}
-
-int MockHttpCache::GetCountDoneHeadersQueue(const std::string& key) {
- HttpCache::ActiveEntry* entry = http_cache_.FindActiveEntry(key);
- if (entry)
- return entry->done_headers_queue.size();
- return false;
-}
-
//-----------------------------------------------------------------------------
int MockDiskCacheNoCB::CreateEntry(const std::string& key,
diff --git a/net/http/mock_http_cache.h b/net/http/mock_http_cache.h
index 7718f351..a24200b 100644
--- a/net/http/mock_http_cache.h
+++ b/net/http/mock_http_cache.h
@@ -158,8 +158,6 @@
void ReleaseAll();
- bool IsDiskEntryDoomed(const std::string& key);
-
private:
using EntryMap = std::unordered_map<std::string, MockDiskEntry*>;
class NotImplementedIterator;
@@ -236,14 +234,6 @@
// the test! (by setting test_mode to zero).
static void SetTestMode(int test_mode);
- // Functions to test the state of ActiveEntry.
-
- bool IsWriterPresent(const std::string& key);
- bool IsHeadersTransactionPresent(const std::string& key);
- int GetCountReaders(const std::string& key);
- int GetCountAddToEntryQueue(const std::string& key);
- int GetCountDoneHeadersQueue(const std::string& key);
-
private:
HttpCache http_cache_;
};
diff --git a/net/url_request/url_request_quic_unittest.cc b/net/url_request/url_request_quic_unittest.cc
index 8c3f560..ff887060 100644
--- a/net/url_request/url_request_quic_unittest.cc
+++ b/net/url_request/url_request_quic_unittest.cc
@@ -305,14 +305,15 @@
EXPECT_TRUE(entries[0].GetStringValue("push_url", &value));
EXPECT_EQ(value, push_url_1);
- EXPECT_TRUE(entries[1].GetStringValue("push_url", &value));
+ // No net error code for this lookup transaction, the push is found.
+ EXPECT_FALSE(entries[1].GetIntegerValue("net_error", &net_error));
+
+ EXPECT_TRUE(entries[2].GetStringValue("push_url", &value));
EXPECT_EQ(value, push_url_2);
// Net error code -400 is found for this lookup transaction, the push is not
// found in the cache.
- EXPECT_TRUE(entries[2].GetIntegerValue("net_error", &net_error));
+ EXPECT_TRUE(entries[3].GetIntegerValue("net_error", &net_error));
EXPECT_EQ(net_error, -400);
- // No net error code for this lookup transaction, the push is found.
- EXPECT_FALSE(entries[3].GetIntegerValue("net_error", &net_error));
// Verify the reset error count received on the server side.
EXPECT_LE(1u, GetRstErrorCountReceivedByServer(QUIC_STREAM_CANCELLED));
@@ -391,13 +392,11 @@
EXPECT_TRUE(entries[0].GetStringValue("push_url", &value));
EXPECT_EQ(value, push_url_1);
-
- EXPECT_TRUE(entries[1].GetStringValue("push_url", &value));
- EXPECT_EQ(value, push_url_2);
-
// No net error code for this lookup transaction, the push is found.
- EXPECT_FALSE(entries[2].GetIntegerValue("net_error", &net_error));
+ EXPECT_FALSE(entries[1].GetIntegerValue("net_error", &net_error));
+ EXPECT_TRUE(entries[2].GetStringValue("push_url", &value));
+ EXPECT_EQ(value, push_url_2);
// No net error code for this lookup transaction, the push is found.
EXPECT_FALSE(entries[3].GetIntegerValue("net_error", &net_error));
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 341f870..7282a0e5 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -4138,14 +4138,14 @@
context.CreateRequest(url, DEFAULT_PRIORITY, &d));
r->Start();
- base::RunLoop().Run();
-
{
HttpRequestHeaders headers;
EXPECT_TRUE(r->GetFullRequestHeaders(&headers));
- EXPECT_TRUE(headers.HasHeader("Authorization"));
+ EXPECT_FALSE(headers.HasHeader("Authorization"));
}
+ base::RunLoop().Run();
+
EXPECT_EQ(OK, d.request_status());
EXPECT_EQ(200, r->GetResponseCode());
EXPECT_TRUE(d.auth_required_called());