1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/http/http_cache.h"
9 #include "base/compiler_specific.h"
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/callback.h"
18 #include "base/file_util.h"
19 #include "base/format_macros.h"
20 #include "base/location.h"
21 #include "base/memory/ref_counted.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/metrics/field_trial.h"
24 #include "base/pickle.h"
25 #include "base/stl_util.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/threading/worker_pool.h"
30 #include "net/base/cache_type.h"
31 #include "net/base/io_buffer.h"
32 #include "net/base/load_flags.h"
33 #include "net/base/net_errors.h"
34 #include "net/base/upload_data_stream.h"
35 #include "net/disk_cache/disk_cache.h"
36 #include "net/http/http_cache_transaction.h"
37 #include "net/http/http_network_layer.h"
38 #include "net/http/http_network_session.h"
39 #include "net/http/http_request_info.h"
40 #include "net/http/http_response_headers.h"
41 #include "net/http/http_response_info.h"
42 #include "net/http/http_util.h"
46 // Adaptor to delete a file on a worker thread.
47 void DeletePath(base::FilePath path) {
48 base::DeleteFile(path, false);
55 HttpCache::DefaultBackend::DefaultBackend(CacheType type,
56 BackendType backend_type,
57 const base::FilePath& path,
59 base::MessageLoopProxy* thread)
61 backend_type_(backend_type),
63 max_bytes_(max_bytes),
67 HttpCache::DefaultBackend::~DefaultBackend() {}
70 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
71 return new DefaultBackend(MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT,
72 base::FilePath(), max_bytes, NULL);
75 int HttpCache::DefaultBackend::CreateBackend(
76 NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend,
77 const CompletionCallback& callback) {
78 DCHECK_GE(max_bytes_, 0);
79 return disk_cache::CreateCacheBackend(type_,
90 //-----------------------------------------------------------------------------
92 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
95 will_process_pending_queue(false),
99 HttpCache::ActiveEntry::~ActiveEntry() {
106 //-----------------------------------------------------------------------------
108 // This structure keeps track of work items that are attempting to create or
109 // open cache entries or the backend itself.
110 struct HttpCache::PendingOp {
111 PendingOp() : disk_entry(NULL), writer(NULL) {}
114 disk_cache::Entry* disk_entry;
115 scoped_ptr<disk_cache::Backend> backend;
117 CompletionCallback callback; // BackendCallback.
118 WorkItemList pending_queue;
121 //-----------------------------------------------------------------------------
123 // The type of operation represented by a work item.
124 enum WorkItemOperation {
131 // A work item encapsulates a single request to the backend with all the
132 // information needed to complete that request.
133 class HttpCache::WorkItem {
135 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
136 : operation_(operation),
140 WorkItem(WorkItemOperation operation, Transaction* trans,
141 const net::CompletionCallback& cb, disk_cache::Backend** backend)
142 : operation_(operation),
149 // Calls back the transaction with the result of the operation.
150 void NotifyTransaction(int result, ActiveEntry* entry) {
151 DCHECK(!entry || entry->disk_entry);
155 trans_->io_callback().Run(result);
158 // Notifies the caller about the operation completion. Returns true if the
159 // callback was invoked.
160 bool DoCallback(int result, disk_cache::Backend* backend) {
163 if (!callback_.is_null()) {
164 callback_.Run(result);
170 WorkItemOperation operation() { return operation_; }
171 void ClearTransaction() { trans_ = NULL; }
172 void ClearEntry() { entry_ = NULL; }
173 void ClearCallback() { callback_.Reset(); }
174 bool Matches(Transaction* trans) const { return trans == trans_; }
175 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); }
178 WorkItemOperation operation_;
180 ActiveEntry** entry_;
181 net::CompletionCallback callback_; // User callback.
182 disk_cache::Backend** backend_;
185 //-----------------------------------------------------------------------------
187 // This class encapsulates a transaction whose only purpose is to write metadata
189 class HttpCache::MetadataWriter {
191 explicit MetadataWriter(HttpCache::Transaction* trans)
192 : transaction_(trans),
199 // Implements the bulk of HttpCache::WriteMetadata.
200 void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
204 void VerifyResponse(int result);
206 void OnIOComplete(int result);
208 scoped_ptr<HttpCache::Transaction> transaction_;
210 scoped_refptr<IOBuffer> buf_;
212 base::Time expected_response_time_;
213 HttpRequestInfo request_info_;
214 DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
217 void HttpCache::MetadataWriter::Write(const GURL& url,
218 base::Time expected_response_time,
219 IOBuffer* buf, int buf_len) {
220 DCHECK_GT(buf_len, 0);
223 request_info_.url = url;
224 request_info_.method = "GET";
225 request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
227 expected_response_time_ = expected_response_time;
232 int rv = transaction_->Start(
234 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)),
236 if (rv != ERR_IO_PENDING)
240 void HttpCache::MetadataWriter::VerifyResponse(int result) {
243 return SelfDestroy();
245 const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
246 DCHECK(response_info->was_cached);
247 if (response_info->response_time != expected_response_time_)
248 return SelfDestroy();
250 result = transaction_->WriteMetadata(
253 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)));
254 if (result != ERR_IO_PENDING)
258 void HttpCache::MetadataWriter::SelfDestroy() {
262 void HttpCache::MetadataWriter::OnIOComplete(int result) {
264 return VerifyResponse(result);
268 //-----------------------------------------------------------------------------
270 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
271 BackendFactory* backend_factory)
272 : net_log_(params.net_log),
273 backend_factory_(backend_factory),
274 building_backend_(false),
276 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))) {
280 HttpCache::HttpCache(HttpNetworkSession* session,
281 BackendFactory* backend_factory)
282 : net_log_(session->net_log()),
283 backend_factory_(backend_factory),
284 building_backend_(false),
286 network_layer_(new HttpNetworkLayer(session)) {
289 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
291 BackendFactory* backend_factory)
293 backend_factory_(backend_factory),
294 building_backend_(false),
296 network_layer_(network_layer) {
299 HttpCache::~HttpCache() {
300 // If we have any active entries remaining, then we need to deactivate them.
301 // We may have some pending calls to OnProcessPendingQueue, but since those
302 // won't run (due to our destruction), we can simply ignore the corresponding
303 // will_process_pending_queue flag.
304 while (!active_entries_.empty()) {
305 ActiveEntry* entry = active_entries_.begin()->second;
306 entry->will_process_pending_queue = false;
307 entry->pending_queue.clear();
308 entry->readers.clear();
309 entry->writer = NULL;
310 DeactivateEntry(entry);
313 STLDeleteElements(&doomed_entries_);
315 // Before deleting pending_ops_, we have to make sure that the disk cache is
316 // done with said operations, or it will attempt to use deleted data.
319 PendingOpsMap::iterator pending_it = pending_ops_.begin();
320 for (; pending_it != pending_ops_.end(); ++pending_it) {
321 // We are not notifying the transactions about the cache going away, even
322 // though they are waiting for a callback that will never fire.
323 PendingOp* pending_op = pending_it->second;
324 delete pending_op->writer;
325 bool delete_pending_op = true;
326 if (building_backend_) {
327 // If we don't have a backend, when its construction finishes it will
328 // deliver the callbacks.
329 if (!pending_op->callback.is_null()) {
330 // If not null, the callback will delete the pending operation later.
331 delete_pending_op = false;
334 pending_op->callback.Reset();
337 STLDeleteElements(&pending_op->pending_queue);
338 if (delete_pending_op)
343 int HttpCache::GetBackend(disk_cache::Backend** backend,
344 const CompletionCallback& callback) {
345 DCHECK(!callback.is_null());
347 if (disk_cache_.get()) {
348 *backend = disk_cache_.get();
352 return CreateBackend(backend, callback);
355 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
356 return disk_cache_.get();
360 bool HttpCache::ParseResponseInfo(const char* data, int len,
361 HttpResponseInfo* response_info,
362 bool* response_truncated) {
363 Pickle pickle(data, len);
364 return response_info->InitFromPickle(pickle, response_truncated);
367 void HttpCache::WriteMetadata(const GURL& url,
368 RequestPriority priority,
369 base::Time expected_response_time,
375 // Do lazy initialization of disk cache if needed.
376 if (!disk_cache_.get()) {
377 // We don't care about the result.
378 CreateBackend(NULL, net::CompletionCallback());
381 HttpCache::Transaction* trans =
382 new HttpCache::Transaction(priority, this, NULL);
383 MetadataWriter* writer = new MetadataWriter(trans);
385 // The writer will self destruct when done.
386 writer->Write(url, expected_response_time, buf, buf_len);
389 void HttpCache::CloseAllConnections() {
390 net::HttpNetworkLayer* network =
391 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
392 HttpNetworkSession* session = network->GetSession();
394 session->CloseAllConnections();
397 void HttpCache::CloseIdleConnections() {
398 net::HttpNetworkLayer* network =
399 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
400 HttpNetworkSession* session = network->GetSession();
402 session->CloseIdleConnections();
405 void HttpCache::OnExternalCacheHit(const GURL& url,
406 const std::string& http_method) {
407 if (!disk_cache_.get())
410 HttpRequestInfo request_info;
411 request_info.url = url;
412 request_info.method = http_method;
413 std::string key = GenerateCacheKey(&request_info);
414 disk_cache_->OnExternalCacheHit(key);
417 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
418 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
420 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true);
423 int HttpCache::CreateTransaction(RequestPriority priority,
424 scoped_ptr<HttpTransaction>* trans,
425 HttpTransactionDelegate* delegate) {
426 // Do lazy initialization of disk cache if needed.
427 if (!disk_cache_.get()) {
428 // We don't care about the result.
429 CreateBackend(NULL, net::CompletionCallback());
432 trans->reset(new HttpCache::Transaction(priority, this, delegate));
436 HttpCache* HttpCache::GetCache() {
440 HttpNetworkSession* HttpCache::GetSession() {
441 net::HttpNetworkLayer* network =
442 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
443 return network->GetSession();
446 //-----------------------------------------------------------------------------
448 int HttpCache::CreateBackend(disk_cache::Backend** backend,
449 const net::CompletionCallback& callback) {
450 if (!backend_factory_.get())
453 building_backend_ = true;
455 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
458 // This is the only operation that we can do that is not related to any given
459 // entry, so we use an empty key for it.
460 PendingOp* pending_op = GetPendingOp(std::string());
461 if (pending_op->writer) {
462 if (!callback.is_null())
463 pending_op->pending_queue.push_back(item.release());
464 return ERR_IO_PENDING;
467 DCHECK(pending_op->pending_queue.empty());
469 pending_op->writer = item.release();
470 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
471 AsWeakPtr(), pending_op);
473 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
474 pending_op->callback);
475 if (rv != ERR_IO_PENDING) {
476 pending_op->writer->ClearCallback();
477 pending_op->callback.Run(rv);
483 int HttpCache::GetBackendForTransaction(Transaction* trans) {
484 if (disk_cache_.get())
487 if (!building_backend_)
490 WorkItem* item = new WorkItem(
491 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL);
492 PendingOp* pending_op = GetPendingOp(std::string());
493 DCHECK(pending_op->writer);
494 pending_op->pending_queue.push_back(item);
495 return ERR_IO_PENDING;
498 // Generate a key that can be used inside the cache.
499 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
500 // Strip out the reference, username, and password sections of the URL.
501 std::string url = HttpUtil::SpecForRequest(request->url);
503 DCHECK(mode_ != DISABLE);
504 if (mode_ == NORMAL) {
505 // No valid URL can begin with numerals, so we should not have to worry
506 // about collisions with normal URLs.
507 if (request->upload_data_stream &&
508 request->upload_data_stream->identifier()) {
509 url.insert(0, base::StringPrintf(
510 "%" PRId64 "/", request->upload_data_stream->identifier()));
515 // In playback and record mode, we cache everything.
517 // Lazily initialize.
518 if (playback_cache_map_ == NULL)
519 playback_cache_map_.reset(new PlaybackCacheMap());
521 // Each time we request an item from the cache, we tag it with a
522 // generation number. During playback, multiple fetches for the same
523 // item will use the same generation number and pull the proper
524 // instance of an URL from the cache.
526 DCHECK(playback_cache_map_ != NULL);
527 if (playback_cache_map_->find(url) != playback_cache_map_->end())
528 generation = (*playback_cache_map_)[url];
529 (*playback_cache_map_)[url] = generation + 1;
531 // The key into the cache is GENERATION # + METHOD + URL.
532 std::string result = base::IntToString(generation);
533 result.append(request->method);
538 void HttpCache::DoomActiveEntry(const std::string& key) {
539 ActiveEntriesMap::iterator it = active_entries_.find(key);
540 if (it == active_entries_.end())
543 // This is not a performance critical operation, this is handling an error
544 // condition so it is OK to look up the entry again.
545 int rv = DoomEntry(key, NULL);
549 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
550 // Need to abandon the ActiveEntry, but any transaction attached to the entry
551 // should not be impacted. Dooming an entry only means that it will no
552 // longer be returned by FindActiveEntry (and it will also be destroyed once
553 // all consumers are finished with the entry).
554 ActiveEntriesMap::iterator it = active_entries_.find(key);
555 if (it == active_entries_.end()) {
557 return AsyncDoomEntry(key, trans);
560 ActiveEntry* entry = it->second;
561 active_entries_.erase(it);
563 // We keep track of doomed entries so that we can ensure that they are
564 // cleaned up properly when the cache is destroyed.
565 doomed_entries_.insert(entry);
567 entry->disk_entry->Doom();
568 entry->doomed = true;
570 DCHECK(entry->writer || !entry->readers.empty());
574 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
575 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
576 PendingOp* pending_op = GetPendingOp(key);
577 if (pending_op->writer) {
578 pending_op->pending_queue.push_back(item);
579 return ERR_IO_PENDING;
582 DCHECK(pending_op->pending_queue.empty());
584 pending_op->writer = item;
585 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
586 AsWeakPtr(), pending_op);
588 int rv = disk_cache_->DoomEntry(key, pending_op->callback);
589 if (rv != ERR_IO_PENDING) {
590 item->ClearTransaction();
591 pending_op->callback.Run(rv);
597 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
601 HttpRequestInfo temp_info;
603 temp_info.method = "GET";
604 std::string key = GenerateCacheKey(&temp_info);
606 // Defer to DoomEntry if there is an active entry, otherwise call
607 // AsyncDoomEntry without triggering a callback.
608 if (active_entries_.count(key))
609 DoomEntry(key, NULL);
611 AsyncDoomEntry(key, NULL);
614 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
615 DCHECK(entry->doomed);
616 DCHECK(!entry->writer);
617 DCHECK(entry->readers.empty());
618 DCHECK(entry->pending_queue.empty());
620 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
621 DCHECK(it != doomed_entries_.end());
622 doomed_entries_.erase(it);
627 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
628 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
629 return it != active_entries_.end() ? it->second : NULL;
632 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
633 disk_cache::Entry* disk_entry) {
634 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
635 ActiveEntry* entry = new ActiveEntry(disk_entry);
636 active_entries_[disk_entry->GetKey()] = entry;
640 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
641 DCHECK(!entry->will_process_pending_queue);
642 DCHECK(!entry->doomed);
643 DCHECK(!entry->writer);
644 DCHECK(entry->disk_entry);
645 DCHECK(entry->readers.empty());
646 DCHECK(entry->pending_queue.empty());
648 std::string key = entry->disk_entry->GetKey();
650 return SlowDeactivateEntry(entry);
652 ActiveEntriesMap::iterator it = active_entries_.find(key);
653 DCHECK(it != active_entries_.end());
654 DCHECK(it->second == entry);
656 active_entries_.erase(it);
660 // We don't know this entry's key so we have to find it without it.
661 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
662 for (ActiveEntriesMap::iterator it = active_entries_.begin();
663 it != active_entries_.end(); ++it) {
664 if (it->second == entry) {
665 active_entries_.erase(it);
672 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
673 DCHECK(!FindActiveEntry(key));
675 PendingOpsMap::const_iterator it = pending_ops_.find(key);
676 if (it != pending_ops_.end())
679 PendingOp* operation = new PendingOp();
680 pending_ops_[key] = operation;
684 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
686 if (pending_op->disk_entry)
687 key = pending_op->disk_entry->GetKey();
690 PendingOpsMap::iterator it = pending_ops_.find(key);
691 DCHECK(it != pending_ops_.end());
692 pending_ops_.erase(it);
694 for (PendingOpsMap::iterator it = pending_ops_.begin();
695 it != pending_ops_.end(); ++it) {
696 if (it->second == pending_op) {
697 pending_ops_.erase(it);
702 DCHECK(pending_op->pending_queue.empty());
707 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
708 Transaction* trans) {
709 ActiveEntry* active_entry = FindActiveEntry(key);
711 *entry = active_entry;
715 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
716 PendingOp* pending_op = GetPendingOp(key);
717 if (pending_op->writer) {
718 pending_op->pending_queue.push_back(item);
719 return ERR_IO_PENDING;
722 DCHECK(pending_op->pending_queue.empty());
724 pending_op->writer = item;
725 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
726 AsWeakPtr(), pending_op);
728 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
729 pending_op->callback);
730 if (rv != ERR_IO_PENDING) {
731 item->ClearTransaction();
732 pending_op->callback.Run(rv);
738 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
739 Transaction* trans) {
740 if (FindActiveEntry(key)) {
741 return ERR_CACHE_RACE;
744 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
745 PendingOp* pending_op = GetPendingOp(key);
746 if (pending_op->writer) {
747 pending_op->pending_queue.push_back(item);
748 return ERR_IO_PENDING;
751 DCHECK(pending_op->pending_queue.empty());
753 pending_op->writer = item;
754 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
755 AsWeakPtr(), pending_op);
757 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
758 pending_op->callback);
759 if (rv != ERR_IO_PENDING) {
760 item->ClearTransaction();
761 pending_op->callback.Run(rv);
767 void HttpCache::DestroyEntry(ActiveEntry* entry) {
769 FinalizeDoomedEntry(entry);
771 DeactivateEntry(entry);
775 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
777 DCHECK(entry->disk_entry);
779 // We implement a basic reader/writer lock for the disk cache entry. If
780 // there is already a writer, then everyone has to wait for the writer to
781 // finish before they can access the cache entry. There can be multiple
784 // NOTE: If the transaction can only write, then the entry should not be in
785 // use (since any existing entry should have already been doomed).
787 if (entry->writer || entry->will_process_pending_queue) {
788 entry->pending_queue.push_back(trans);
789 return ERR_IO_PENDING;
792 if (trans->mode() & Transaction::WRITE) {
793 // transaction needs exclusive access to the entry
794 if (entry->readers.empty()) {
795 entry->writer = trans;
797 entry->pending_queue.push_back(trans);
798 return ERR_IO_PENDING;
801 // transaction needs read access to the entry
802 entry->readers.push_back(trans);
805 // We do this before calling EntryAvailable to force any further calls to
806 // AddTransactionToEntry to add their transaction to the pending queue, which
807 // ensures FIFO ordering.
808 if (!entry->writer && !entry->pending_queue.empty())
809 ProcessPendingQueue(entry);
814 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
816 // If we already posted a task to move on to the next transaction and this was
817 // the writer, there is nothing to cancel.
818 if (entry->will_process_pending_queue && entry->readers.empty())
822 DCHECK(trans == entry->writer);
824 // Assume there was a failure.
825 bool success = false;
827 DCHECK(entry->disk_entry);
828 // This is a successful operation in the sense that we want to keep the
830 success = trans->AddTruncatedFlag();
831 // The previous operation may have deleted the entry.
835 DoneWritingToEntry(entry, success);
837 DoneReadingFromEntry(entry, trans);
841 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
842 DCHECK(entry->readers.empty());
844 entry->writer = NULL;
847 ProcessPendingQueue(entry);
849 DCHECK(!entry->will_process_pending_queue);
851 // We failed to create this entry.
852 TransactionList pending_queue;
853 pending_queue.swap(entry->pending_queue);
855 entry->disk_entry->Doom();
858 // We need to do something about these pending entries, which now need to
859 // be added to a new entry.
860 while (!pending_queue.empty()) {
861 // ERR_CACHE_RACE causes the transaction to restart the whole process.
862 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
863 pending_queue.pop_front();
868 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
869 DCHECK(!entry->writer);
871 TransactionList::iterator it =
872 std::find(entry->readers.begin(), entry->readers.end(), trans);
873 DCHECK(it != entry->readers.end());
875 entry->readers.erase(it);
877 ProcessPendingQueue(entry);
880 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
881 DCHECK(entry->writer);
882 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
883 DCHECK(entry->readers.empty());
885 Transaction* trans = entry->writer;
887 entry->writer = NULL;
888 entry->readers.push_back(trans);
890 ProcessPendingQueue(entry);
893 LoadState HttpCache::GetLoadStateForPendingTransaction(
894 const Transaction* trans) {
895 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
896 if (i == active_entries_.end()) {
897 // If this is really a pending transaction, and it is not part of
898 // active_entries_, we should be creating the backend or the entry.
899 return LOAD_STATE_WAITING_FOR_CACHE;
902 Transaction* writer = i->second->writer;
903 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
906 void HttpCache::RemovePendingTransaction(Transaction* trans) {
907 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
909 if (i != active_entries_.end())
910 found = RemovePendingTransactionFromEntry(i->second, trans);
915 if (building_backend_) {
916 PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
917 if (j != pending_ops_.end())
918 found = RemovePendingTransactionFromPendingOp(j->second, trans);
924 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
925 if (j != pending_ops_.end())
926 found = RemovePendingTransactionFromPendingOp(j->second, trans);
931 ActiveEntriesSet::iterator k = doomed_entries_.begin();
932 for (; k != doomed_entries_.end() && !found; ++k)
933 found = RemovePendingTransactionFromEntry(*k, trans);
935 DCHECK(found) << "Pending transaction not found";
938 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
939 Transaction* trans) {
940 TransactionList& pending_queue = entry->pending_queue;
942 TransactionList::iterator j =
943 find(pending_queue.begin(), pending_queue.end(), trans);
944 if (j == pending_queue.end())
947 pending_queue.erase(j);
951 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
952 Transaction* trans) {
953 if (pending_op->writer->Matches(trans)) {
954 pending_op->writer->ClearTransaction();
955 pending_op->writer->ClearEntry();
958 WorkItemList& pending_queue = pending_op->pending_queue;
960 WorkItemList::iterator it = pending_queue.begin();
961 for (; it != pending_queue.end(); ++it) {
962 if ((*it)->Matches(trans)) {
964 pending_queue.erase(it);
971 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
972 // Multiple readers may finish with an entry at once, so we want to batch up
973 // calls to OnProcessPendingQueue. This flag also tells us that we should
974 // not delete the entry before OnProcessPendingQueue runs.
975 if (entry->will_process_pending_queue)
977 entry->will_process_pending_queue = true;
979 base::MessageLoop::current()->PostTask(
981 base::Bind(&HttpCache::OnProcessPendingQueue, AsWeakPtr(), entry));
984 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
985 entry->will_process_pending_queue = false;
986 DCHECK(!entry->writer);
988 // If no one is interested in this entry, then we can deactivate it.
989 if (entry->pending_queue.empty()) {
990 if (entry->readers.empty())
995 // Promote next transaction from the pending queue.
996 Transaction* next = entry->pending_queue.front();
997 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
998 return; // Have to wait.
1000 entry->pending_queue.erase(entry->pending_queue.begin());
1002 int rv = AddTransactionToEntry(entry, next);
1003 if (rv != ERR_IO_PENDING) {
1004 next->io_callback().Run(rv);
1008 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1009 WorkItemOperation op = pending_op->writer->operation();
1011 // Completing the creation of the backend is simpler than the other cases.
1012 if (op == WI_CREATE_BACKEND)
1013 return OnBackendCreated(result, pending_op);
1015 scoped_ptr<WorkItem> item(pending_op->writer);
1016 bool fail_requests = false;
1018 ActiveEntry* entry = NULL;
1021 if (op == WI_DOOM_ENTRY) {
1022 // Anything after a Doom has to be restarted.
1023 fail_requests = true;
1024 } else if (item->IsValid()) {
1025 key = pending_op->disk_entry->GetKey();
1026 entry = ActivateEntry(pending_op->disk_entry);
1028 // The writer transaction is gone.
1029 if (op == WI_CREATE_ENTRY)
1030 pending_op->disk_entry->Doom();
1031 pending_op->disk_entry->Close();
1032 pending_op->disk_entry = NULL;
1033 fail_requests = true;
1037 // We are about to notify a bunch of transactions, and they may decide to
1038 // re-issue a request (or send a different one). If we don't delete
1039 // pending_op, the new request will be appended to the end of the list, and
1040 // we'll see it again from this point before it has a chance to complete (and
1041 // we'll be messing out the request order). The down side is that if for some
1042 // reason notifying request A ends up cancelling request B (for the same key),
1043 // we won't find request B anywhere (because it would be in a local variable
1044 // here) and that's bad. If there is a chance for that to happen, we'll have
1045 // to move the callback used to be a CancelableCallback. By the way, for this
1046 // to happen the action (to cancel B) has to be synchronous to the
1047 // notification for request A.
1048 WorkItemList pending_items;
1049 pending_items.swap(pending_op->pending_queue);
1050 DeletePendingOp(pending_op);
1052 item->NotifyTransaction(result, entry);
1054 while (!pending_items.empty()) {
1055 item.reset(pending_items.front());
1056 pending_items.pop_front();
1058 if (item->operation() == WI_DOOM_ENTRY) {
1059 // A queued doom request is always a race.
1060 fail_requests = true;
1061 } else if (result == OK) {
1062 entry = FindActiveEntry(key);
1064 fail_requests = true;
1067 if (fail_requests) {
1068 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1072 if (item->operation() == WI_CREATE_ENTRY) {
1074 // A second Create request, but the first request succeeded.
1075 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1077 if (op != WI_CREATE_ENTRY) {
1078 // Failed Open followed by a Create.
1079 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1080 fail_requests = true;
1082 item->NotifyTransaction(result, entry);
1086 if (op == WI_CREATE_ENTRY && result != OK) {
1087 // Failed Create followed by an Open.
1088 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1089 fail_requests = true;
1091 item->NotifyTransaction(result, entry);
1098 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1099 PendingOp* pending_op,
1102 cache->OnIOComplete(rv, pending_op);
1104 // The callback was cancelled so we should delete the pending_op that
1105 // was used with this callback.
1110 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1111 scoped_ptr<WorkItem> item(pending_op->writer);
1112 WorkItemOperation op = item->operation();
1113 DCHECK_EQ(WI_CREATE_BACKEND, op);
1115 // We don't need the callback anymore.
1116 pending_op->callback.Reset();
1118 if (backend_factory_.get()) {
1119 // We may end up calling OnBackendCreated multiple times if we have pending
1120 // work items. The first call saves the backend and releases the factory,
1121 // and the last call clears building_backend_.
1122 backend_factory_.reset(); // Reclaim memory.
1124 disk_cache_ = pending_op->backend.Pass();
1127 if (!pending_op->pending_queue.empty()) {
1128 WorkItem* pending_item = pending_op->pending_queue.front();
1129 pending_op->pending_queue.pop_front();
1130 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1132 // We want to process a single callback at a time, because the cache may
1133 // go away from the callback.
1134 pending_op->writer = pending_item;
1136 base::MessageLoop::current()->PostTask(
1139 &HttpCache::OnBackendCreated, AsWeakPtr(), result, pending_op));
1141 building_backend_ = false;
1142 DeletePendingOp(pending_op);
1145 // The cache may be gone when we return from the callback.
1146 if (!item->DoCallback(result, disk_cache_.get()))
1147 item->NotifyTransaction(result, NULL);