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/disk_cache_based_quic_server_info.h"
37 #include "net/http/http_cache_transaction.h"
38 #include "net/http/http_network_layer.h"
39 #include "net/http/http_network_session.h"
40 #include "net/http/http_request_info.h"
41 #include "net/http/http_response_headers.h"
42 #include "net/http/http_response_info.h"
43 #include "net/http/http_util.h"
44 #include "net/quic/crypto/quic_server_info.h"
48 // Adaptor to delete a file on a worker thread.
49 void DeletePath(base::FilePath path) {
50 base::DeleteFile(path, false);
57 HttpCache::DefaultBackend::DefaultBackend(CacheType type,
58 BackendType backend_type,
59 const base::FilePath& path,
61 base::MessageLoopProxy* thread)
63 backend_type_(backend_type),
65 max_bytes_(max_bytes),
69 HttpCache::DefaultBackend::~DefaultBackend() {}
72 HttpCache::BackendFactory* HttpCache::DefaultBackend::InMemory(int max_bytes) {
73 return new DefaultBackend(MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT,
74 base::FilePath(), max_bytes, NULL);
77 int HttpCache::DefaultBackend::CreateBackend(
78 NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend,
79 const CompletionCallback& callback) {
80 DCHECK_GE(max_bytes_, 0);
81 return disk_cache::CreateCacheBackend(type_,
92 //-----------------------------------------------------------------------------
94 HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* entry)
97 will_process_pending_queue(false),
101 HttpCache::ActiveEntry::~ActiveEntry() {
108 //-----------------------------------------------------------------------------
110 // This structure keeps track of work items that are attempting to create or
111 // open cache entries or the backend itself.
112 struct HttpCache::PendingOp {
113 PendingOp() : disk_entry(NULL), writer(NULL) {}
116 disk_cache::Entry* disk_entry;
117 scoped_ptr<disk_cache::Backend> backend;
119 CompletionCallback callback; // BackendCallback.
120 WorkItemList pending_queue;
123 //-----------------------------------------------------------------------------
125 // The type of operation represented by a work item.
126 enum WorkItemOperation {
133 // A work item encapsulates a single request to the backend with all the
134 // information needed to complete that request.
135 class HttpCache::WorkItem {
137 WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
138 : operation_(operation),
142 WorkItem(WorkItemOperation operation, Transaction* trans,
143 const net::CompletionCallback& cb, disk_cache::Backend** backend)
144 : operation_(operation),
151 // Calls back the transaction with the result of the operation.
152 void NotifyTransaction(int result, ActiveEntry* entry) {
153 DCHECK(!entry || entry->disk_entry);
157 trans_->io_callback().Run(result);
160 // Notifies the caller about the operation completion. Returns true if the
161 // callback was invoked.
162 bool DoCallback(int result, disk_cache::Backend* backend) {
165 if (!callback_.is_null()) {
166 callback_.Run(result);
172 WorkItemOperation operation() { return operation_; }
173 void ClearTransaction() { trans_ = NULL; }
174 void ClearEntry() { entry_ = NULL; }
175 void ClearCallback() { callback_.Reset(); }
176 bool Matches(Transaction* trans) const { return trans == trans_; }
177 bool IsValid() const { return trans_ || entry_ || !callback_.is_null(); }
180 WorkItemOperation operation_;
182 ActiveEntry** entry_;
183 net::CompletionCallback callback_; // User callback.
184 disk_cache::Backend** backend_;
187 //-----------------------------------------------------------------------------
189 // This class encapsulates a transaction whose only purpose is to write metadata
191 class HttpCache::MetadataWriter {
193 explicit MetadataWriter(HttpCache::Transaction* trans)
194 : transaction_(trans),
201 // Implements the bulk of HttpCache::WriteMetadata.
202 void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
206 void VerifyResponse(int result);
208 void OnIOComplete(int result);
210 scoped_ptr<HttpCache::Transaction> transaction_;
212 scoped_refptr<IOBuffer> buf_;
214 base::Time expected_response_time_;
215 HttpRequestInfo request_info_;
216 DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
219 void HttpCache::MetadataWriter::Write(const GURL& url,
220 base::Time expected_response_time,
221 IOBuffer* buf, int buf_len) {
222 DCHECK_GT(buf_len, 0);
225 request_info_.url = url;
226 request_info_.method = "GET";
227 request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
229 expected_response_time_ = expected_response_time;
234 int rv = transaction_->Start(
236 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)),
238 if (rv != ERR_IO_PENDING)
242 void HttpCache::MetadataWriter::VerifyResponse(int result) {
245 return SelfDestroy();
247 const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
248 DCHECK(response_info->was_cached);
249 if (response_info->response_time != expected_response_time_)
250 return SelfDestroy();
252 result = transaction_->WriteMetadata(
255 base::Bind(&MetadataWriter::OnIOComplete, base::Unretained(this)));
256 if (result != ERR_IO_PENDING)
260 void HttpCache::MetadataWriter::SelfDestroy() {
264 void HttpCache::MetadataWriter::OnIOComplete(int result) {
266 return VerifyResponse(result);
270 //-----------------------------------------------------------------------------
272 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
274 QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
275 : http_cache_(http_cache) {
278 virtual QuicServerInfo* GetForHost(const std::string& hostname) OVERRIDE {
279 return new DiskCacheBasedQuicServerInfo(hostname, http_cache_);
283 HttpCache* const http_cache_;
286 //-----------------------------------------------------------------------------
287 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
288 BackendFactory* backend_factory)
289 : net_log_(params.net_log),
290 backend_factory_(backend_factory),
291 building_backend_(false),
293 quic_server_info_factory_(new QuicServerInfoFactoryAdaptor(this)),
294 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))) {
298 HttpCache::HttpCache(HttpNetworkSession* session,
299 BackendFactory* backend_factory)
300 : net_log_(session->net_log()),
301 backend_factory_(backend_factory),
302 building_backend_(false),
304 quic_server_info_factory_(new QuicServerInfoFactoryAdaptor(this)),
305 network_layer_(new HttpNetworkLayer(session)) {
308 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
310 BackendFactory* backend_factory)
312 backend_factory_(backend_factory),
313 building_backend_(false),
315 network_layer_(network_layer) {
318 HttpCache::~HttpCache() {
319 // If we have any active entries remaining, then we need to deactivate them.
320 // We may have some pending calls to OnProcessPendingQueue, but since those
321 // won't run (due to our destruction), we can simply ignore the corresponding
322 // will_process_pending_queue flag.
323 while (!active_entries_.empty()) {
324 ActiveEntry* entry = active_entries_.begin()->second;
325 entry->will_process_pending_queue = false;
326 entry->pending_queue.clear();
327 entry->readers.clear();
328 entry->writer = NULL;
329 DeactivateEntry(entry);
332 STLDeleteElements(&doomed_entries_);
334 // Before deleting pending_ops_, we have to make sure that the disk cache is
335 // done with said operations, or it will attempt to use deleted data.
338 PendingOpsMap::iterator pending_it = pending_ops_.begin();
339 for (; pending_it != pending_ops_.end(); ++pending_it) {
340 // We are not notifying the transactions about the cache going away, even
341 // though they are waiting for a callback that will never fire.
342 PendingOp* pending_op = pending_it->second;
343 delete pending_op->writer;
344 bool delete_pending_op = true;
345 if (building_backend_) {
346 // If we don't have a backend, when its construction finishes it will
347 // deliver the callbacks.
348 if (!pending_op->callback.is_null()) {
349 // If not null, the callback will delete the pending operation later.
350 delete_pending_op = false;
353 pending_op->callback.Reset();
356 STLDeleteElements(&pending_op->pending_queue);
357 if (delete_pending_op)
362 int HttpCache::GetBackend(disk_cache::Backend** backend,
363 const CompletionCallback& callback) {
364 DCHECK(!callback.is_null());
366 if (disk_cache_.get()) {
367 *backend = disk_cache_.get();
371 return CreateBackend(backend, callback);
374 disk_cache::Backend* HttpCache::GetCurrentBackend() const {
375 return disk_cache_.get();
379 bool HttpCache::ParseResponseInfo(const char* data, int len,
380 HttpResponseInfo* response_info,
381 bool* response_truncated) {
382 Pickle pickle(data, len);
383 return response_info->InitFromPickle(pickle, response_truncated);
386 void HttpCache::WriteMetadata(const GURL& url,
387 RequestPriority priority,
388 base::Time expected_response_time,
394 // Do lazy initialization of disk cache if needed.
395 if (!disk_cache_.get()) {
396 // We don't care about the result.
397 CreateBackend(NULL, net::CompletionCallback());
400 HttpCache::Transaction* trans =
401 new HttpCache::Transaction(priority, this);
402 MetadataWriter* writer = new MetadataWriter(trans);
404 // The writer will self destruct when done.
405 writer->Write(url, expected_response_time, buf, buf_len);
408 void HttpCache::CloseAllConnections() {
409 net::HttpNetworkLayer* network =
410 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
411 HttpNetworkSession* session = network->GetSession();
413 session->CloseAllConnections();
416 void HttpCache::CloseIdleConnections() {
417 net::HttpNetworkLayer* network =
418 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
419 HttpNetworkSession* session = network->GetSession();
421 session->CloseIdleConnections();
424 void HttpCache::OnExternalCacheHit(const GURL& url,
425 const std::string& http_method) {
426 if (!disk_cache_.get())
429 HttpRequestInfo request_info;
430 request_info.url = url;
431 request_info.method = http_method;
432 std::string key = GenerateCacheKey(&request_info);
433 disk_cache_->OnExternalCacheHit(key);
436 void HttpCache::InitializeInfiniteCache(const base::FilePath& path) {
437 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
439 base::WorkerPool::PostTask(FROM_HERE, base::Bind(&DeletePath, path), true);
442 int HttpCache::CreateTransaction(RequestPriority priority,
443 scoped_ptr<HttpTransaction>* trans) {
444 // Do lazy initialization of disk cache if needed.
445 if (!disk_cache_.get()) {
446 // We don't care about the result.
447 CreateBackend(NULL, net::CompletionCallback());
450 trans->reset(new HttpCache::Transaction(priority, this));
454 HttpCache* HttpCache::GetCache() {
458 HttpNetworkSession* HttpCache::GetSession() {
459 net::HttpNetworkLayer* network =
460 static_cast<net::HttpNetworkLayer*>(network_layer_.get());
461 return network->GetSession();
464 scoped_ptr<HttpTransactionFactory>
465 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
466 scoped_ptr<HttpTransactionFactory> new_network_layer) {
467 scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass());
468 network_layer_ = new_network_layer.Pass();
469 return old_network_layer.Pass();
472 //-----------------------------------------------------------------------------
474 int HttpCache::CreateBackend(disk_cache::Backend** backend,
475 const net::CompletionCallback& callback) {
476 if (!backend_factory_.get())
479 building_backend_ = true;
481 scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
484 // This is the only operation that we can do that is not related to any given
485 // entry, so we use an empty key for it.
486 PendingOp* pending_op = GetPendingOp(std::string());
487 if (pending_op->writer) {
488 if (!callback.is_null())
489 pending_op->pending_queue.push_back(item.release());
490 return ERR_IO_PENDING;
493 DCHECK(pending_op->pending_queue.empty());
495 pending_op->writer = item.release();
496 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
497 AsWeakPtr(), pending_op);
499 int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend,
500 pending_op->callback);
501 if (rv != ERR_IO_PENDING) {
502 pending_op->writer->ClearCallback();
503 pending_op->callback.Run(rv);
509 int HttpCache::GetBackendForTransaction(Transaction* trans) {
510 if (disk_cache_.get())
513 if (!building_backend_)
516 WorkItem* item = new WorkItem(
517 WI_CREATE_BACKEND, trans, net::CompletionCallback(), NULL);
518 PendingOp* pending_op = GetPendingOp(std::string());
519 DCHECK(pending_op->writer);
520 pending_op->pending_queue.push_back(item);
521 return ERR_IO_PENDING;
524 // Generate a key that can be used inside the cache.
525 std::string HttpCache::GenerateCacheKey(const HttpRequestInfo* request) {
526 // Strip out the reference, username, and password sections of the URL.
527 std::string url = HttpUtil::SpecForRequest(request->url);
529 DCHECK(mode_ != DISABLE);
530 if (mode_ == NORMAL) {
531 // No valid URL can begin with numerals, so we should not have to worry
532 // about collisions with normal URLs.
533 if (request->upload_data_stream &&
534 request->upload_data_stream->identifier()) {
535 url.insert(0, base::StringPrintf(
536 "%" PRId64 "/", request->upload_data_stream->identifier()));
541 // In playback and record mode, we cache everything.
543 // Lazily initialize.
544 if (playback_cache_map_ == NULL)
545 playback_cache_map_.reset(new PlaybackCacheMap());
547 // Each time we request an item from the cache, we tag it with a
548 // generation number. During playback, multiple fetches for the same
549 // item will use the same generation number and pull the proper
550 // instance of an URL from the cache.
552 DCHECK(playback_cache_map_ != NULL);
553 if (playback_cache_map_->find(url) != playback_cache_map_->end())
554 generation = (*playback_cache_map_)[url];
555 (*playback_cache_map_)[url] = generation + 1;
557 // The key into the cache is GENERATION # + METHOD + URL.
558 std::string result = base::IntToString(generation);
559 result.append(request->method);
564 void HttpCache::DoomActiveEntry(const std::string& key) {
565 ActiveEntriesMap::iterator it = active_entries_.find(key);
566 if (it == active_entries_.end())
569 // This is not a performance critical operation, this is handling an error
570 // condition so it is OK to look up the entry again.
571 int rv = DoomEntry(key, NULL);
575 int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
576 // Need to abandon the ActiveEntry, but any transaction attached to the entry
577 // should not be impacted. Dooming an entry only means that it will no
578 // longer be returned by FindActiveEntry (and it will also be destroyed once
579 // all consumers are finished with the entry).
580 ActiveEntriesMap::iterator it = active_entries_.find(key);
581 if (it == active_entries_.end()) {
583 return AsyncDoomEntry(key, trans);
586 ActiveEntry* entry = it->second;
587 active_entries_.erase(it);
589 // We keep track of doomed entries so that we can ensure that they are
590 // cleaned up properly when the cache is destroyed.
591 doomed_entries_.insert(entry);
593 entry->disk_entry->Doom();
594 entry->doomed = true;
596 DCHECK(entry->writer || !entry->readers.empty());
600 int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
601 WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
602 PendingOp* pending_op = GetPendingOp(key);
603 if (pending_op->writer) {
604 pending_op->pending_queue.push_back(item);
605 return ERR_IO_PENDING;
608 DCHECK(pending_op->pending_queue.empty());
610 pending_op->writer = item;
611 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
612 AsWeakPtr(), pending_op);
614 int rv = disk_cache_->DoomEntry(key, pending_op->callback);
615 if (rv != ERR_IO_PENDING) {
616 item->ClearTransaction();
617 pending_op->callback.Run(rv);
623 void HttpCache::DoomMainEntryForUrl(const GURL& url) {
627 HttpRequestInfo temp_info;
629 temp_info.method = "GET";
630 std::string key = GenerateCacheKey(&temp_info);
632 // Defer to DoomEntry if there is an active entry, otherwise call
633 // AsyncDoomEntry without triggering a callback.
634 if (active_entries_.count(key))
635 DoomEntry(key, NULL);
637 AsyncDoomEntry(key, NULL);
640 void HttpCache::FinalizeDoomedEntry(ActiveEntry* entry) {
641 DCHECK(entry->doomed);
642 DCHECK(!entry->writer);
643 DCHECK(entry->readers.empty());
644 DCHECK(entry->pending_queue.empty());
646 ActiveEntriesSet::iterator it = doomed_entries_.find(entry);
647 DCHECK(it != doomed_entries_.end());
648 doomed_entries_.erase(it);
653 HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) {
654 ActiveEntriesMap::const_iterator it = active_entries_.find(key);
655 return it != active_entries_.end() ? it->second : NULL;
658 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
659 disk_cache::Entry* disk_entry) {
660 DCHECK(!FindActiveEntry(disk_entry->GetKey()));
661 ActiveEntry* entry = new ActiveEntry(disk_entry);
662 active_entries_[disk_entry->GetKey()] = entry;
666 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
667 DCHECK(!entry->will_process_pending_queue);
668 DCHECK(!entry->doomed);
669 DCHECK(!entry->writer);
670 DCHECK(entry->disk_entry);
671 DCHECK(entry->readers.empty());
672 DCHECK(entry->pending_queue.empty());
674 std::string key = entry->disk_entry->GetKey();
676 return SlowDeactivateEntry(entry);
678 ActiveEntriesMap::iterator it = active_entries_.find(key);
679 DCHECK(it != active_entries_.end());
680 DCHECK(it->second == entry);
682 active_entries_.erase(it);
686 // We don't know this entry's key so we have to find it without it.
687 void HttpCache::SlowDeactivateEntry(ActiveEntry* entry) {
688 for (ActiveEntriesMap::iterator it = active_entries_.begin();
689 it != active_entries_.end(); ++it) {
690 if (it->second == entry) {
691 active_entries_.erase(it);
698 HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
699 DCHECK(!FindActiveEntry(key));
701 PendingOpsMap::const_iterator it = pending_ops_.find(key);
702 if (it != pending_ops_.end())
705 PendingOp* operation = new PendingOp();
706 pending_ops_[key] = operation;
710 void HttpCache::DeletePendingOp(PendingOp* pending_op) {
712 if (pending_op->disk_entry)
713 key = pending_op->disk_entry->GetKey();
716 PendingOpsMap::iterator it = pending_ops_.find(key);
717 DCHECK(it != pending_ops_.end());
718 pending_ops_.erase(it);
720 for (PendingOpsMap::iterator it = pending_ops_.begin();
721 it != pending_ops_.end(); ++it) {
722 if (it->second == pending_op) {
723 pending_ops_.erase(it);
728 DCHECK(pending_op->pending_queue.empty());
733 int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
734 Transaction* trans) {
735 ActiveEntry* active_entry = FindActiveEntry(key);
737 *entry = active_entry;
741 WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
742 PendingOp* pending_op = GetPendingOp(key);
743 if (pending_op->writer) {
744 pending_op->pending_queue.push_back(item);
745 return ERR_IO_PENDING;
748 DCHECK(pending_op->pending_queue.empty());
750 pending_op->writer = item;
751 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
752 AsWeakPtr(), pending_op);
754 int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry),
755 pending_op->callback);
756 if (rv != ERR_IO_PENDING) {
757 item->ClearTransaction();
758 pending_op->callback.Run(rv);
764 int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
765 Transaction* trans) {
766 if (FindActiveEntry(key)) {
767 return ERR_CACHE_RACE;
770 WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
771 PendingOp* pending_op = GetPendingOp(key);
772 if (pending_op->writer) {
773 pending_op->pending_queue.push_back(item);
774 return ERR_IO_PENDING;
777 DCHECK(pending_op->pending_queue.empty());
779 pending_op->writer = item;
780 pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete,
781 AsWeakPtr(), pending_op);
783 int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
784 pending_op->callback);
785 if (rv != ERR_IO_PENDING) {
786 item->ClearTransaction();
787 pending_op->callback.Run(rv);
793 void HttpCache::DestroyEntry(ActiveEntry* entry) {
795 FinalizeDoomedEntry(entry);
797 DeactivateEntry(entry);
801 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
803 DCHECK(entry->disk_entry);
805 // We implement a basic reader/writer lock for the disk cache entry. If
806 // there is already a writer, then everyone has to wait for the writer to
807 // finish before they can access the cache entry. There can be multiple
810 // NOTE: If the transaction can only write, then the entry should not be in
811 // use (since any existing entry should have already been doomed).
813 if (entry->writer || entry->will_process_pending_queue) {
814 entry->pending_queue.push_back(trans);
815 return ERR_IO_PENDING;
818 if (trans->mode() & Transaction::WRITE) {
819 // transaction needs exclusive access to the entry
820 if (entry->readers.empty()) {
821 entry->writer = trans;
823 entry->pending_queue.push_back(trans);
824 return ERR_IO_PENDING;
827 // transaction needs read access to the entry
828 entry->readers.push_back(trans);
831 // We do this before calling EntryAvailable to force any further calls to
832 // AddTransactionToEntry to add their transaction to the pending queue, which
833 // ensures FIFO ordering.
834 if (!entry->writer && !entry->pending_queue.empty())
835 ProcessPendingQueue(entry);
840 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
842 // If we already posted a task to move on to the next transaction and this was
843 // the writer, there is nothing to cancel.
844 if (entry->will_process_pending_queue && entry->readers.empty())
848 DCHECK(trans == entry->writer);
850 // Assume there was a failure.
851 bool success = false;
853 DCHECK(entry->disk_entry);
854 // This is a successful operation in the sense that we want to keep the
856 success = trans->AddTruncatedFlag();
857 // The previous operation may have deleted the entry.
861 DoneWritingToEntry(entry, success);
863 DoneReadingFromEntry(entry, trans);
867 void HttpCache::DoneWritingToEntry(ActiveEntry* entry, bool success) {
868 DCHECK(entry->readers.empty());
870 entry->writer = NULL;
873 ProcessPendingQueue(entry);
875 DCHECK(!entry->will_process_pending_queue);
877 // We failed to create this entry.
878 TransactionList pending_queue;
879 pending_queue.swap(entry->pending_queue);
881 entry->disk_entry->Doom();
884 // We need to do something about these pending entries, which now need to
885 // be added to a new entry.
886 while (!pending_queue.empty()) {
887 // ERR_CACHE_RACE causes the transaction to restart the whole process.
888 pending_queue.front()->io_callback().Run(ERR_CACHE_RACE);
889 pending_queue.pop_front();
894 void HttpCache::DoneReadingFromEntry(ActiveEntry* entry, Transaction* trans) {
895 DCHECK(!entry->writer);
897 TransactionList::iterator it =
898 std::find(entry->readers.begin(), entry->readers.end(), trans);
899 DCHECK(it != entry->readers.end());
901 entry->readers.erase(it);
903 ProcessPendingQueue(entry);
906 void HttpCache::ConvertWriterToReader(ActiveEntry* entry) {
907 DCHECK(entry->writer);
908 DCHECK(entry->writer->mode() == Transaction::READ_WRITE);
909 DCHECK(entry->readers.empty());
911 Transaction* trans = entry->writer;
913 entry->writer = NULL;
914 entry->readers.push_back(trans);
916 ProcessPendingQueue(entry);
919 LoadState HttpCache::GetLoadStateForPendingTransaction(
920 const Transaction* trans) {
921 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
922 if (i == active_entries_.end()) {
923 // If this is really a pending transaction, and it is not part of
924 // active_entries_, we should be creating the backend or the entry.
925 return LOAD_STATE_WAITING_FOR_CACHE;
928 Transaction* writer = i->second->writer;
929 return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
932 void HttpCache::RemovePendingTransaction(Transaction* trans) {
933 ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
935 if (i != active_entries_.end())
936 found = RemovePendingTransactionFromEntry(i->second, trans);
941 if (building_backend_) {
942 PendingOpsMap::const_iterator j = pending_ops_.find(std::string());
943 if (j != pending_ops_.end())
944 found = RemovePendingTransactionFromPendingOp(j->second, trans);
950 PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
951 if (j != pending_ops_.end())
952 found = RemovePendingTransactionFromPendingOp(j->second, trans);
957 ActiveEntriesSet::iterator k = doomed_entries_.begin();
958 for (; k != doomed_entries_.end() && !found; ++k)
959 found = RemovePendingTransactionFromEntry(*k, trans);
961 DCHECK(found) << "Pending transaction not found";
964 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry* entry,
965 Transaction* trans) {
966 TransactionList& pending_queue = entry->pending_queue;
968 TransactionList::iterator j =
969 find(pending_queue.begin(), pending_queue.end(), trans);
970 if (j == pending_queue.end())
973 pending_queue.erase(j);
977 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
978 Transaction* trans) {
979 if (pending_op->writer->Matches(trans)) {
980 pending_op->writer->ClearTransaction();
981 pending_op->writer->ClearEntry();
984 WorkItemList& pending_queue = pending_op->pending_queue;
986 WorkItemList::iterator it = pending_queue.begin();
987 for (; it != pending_queue.end(); ++it) {
988 if ((*it)->Matches(trans)) {
990 pending_queue.erase(it);
997 void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
998 // Multiple readers may finish with an entry at once, so we want to batch up
999 // calls to OnProcessPendingQueue. This flag also tells us that we should
1000 // not delete the entry before OnProcessPendingQueue runs.
1001 if (entry->will_process_pending_queue)
1003 entry->will_process_pending_queue = true;
1005 base::MessageLoop::current()->PostTask(
1007 base::Bind(&HttpCache::OnProcessPendingQueue, AsWeakPtr(), entry));
1010 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
1011 entry->will_process_pending_queue = false;
1012 DCHECK(!entry->writer);
1014 // If no one is interested in this entry, then we can deactivate it.
1015 if (entry->pending_queue.empty()) {
1016 if (entry->readers.empty())
1017 DestroyEntry(entry);
1021 // Promote next transaction from the pending queue.
1022 Transaction* next = entry->pending_queue.front();
1023 if ((next->mode() & Transaction::WRITE) && !entry->readers.empty())
1024 return; // Have to wait.
1026 entry->pending_queue.erase(entry->pending_queue.begin());
1028 int rv = AddTransactionToEntry(entry, next);
1029 if (rv != ERR_IO_PENDING) {
1030 next->io_callback().Run(rv);
1034 void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
1035 WorkItemOperation op = pending_op->writer->operation();
1037 // Completing the creation of the backend is simpler than the other cases.
1038 if (op == WI_CREATE_BACKEND)
1039 return OnBackendCreated(result, pending_op);
1041 scoped_ptr<WorkItem> item(pending_op->writer);
1042 bool fail_requests = false;
1044 ActiveEntry* entry = NULL;
1047 if (op == WI_DOOM_ENTRY) {
1048 // Anything after a Doom has to be restarted.
1049 fail_requests = true;
1050 } else if (item->IsValid()) {
1051 key = pending_op->disk_entry->GetKey();
1052 entry = ActivateEntry(pending_op->disk_entry);
1054 // The writer transaction is gone.
1055 if (op == WI_CREATE_ENTRY)
1056 pending_op->disk_entry->Doom();
1057 pending_op->disk_entry->Close();
1058 pending_op->disk_entry = NULL;
1059 fail_requests = true;
1063 // We are about to notify a bunch of transactions, and they may decide to
1064 // re-issue a request (or send a different one). If we don't delete
1065 // pending_op, the new request will be appended to the end of the list, and
1066 // we'll see it again from this point before it has a chance to complete (and
1067 // we'll be messing out the request order). The down side is that if for some
1068 // reason notifying request A ends up cancelling request B (for the same key),
1069 // we won't find request B anywhere (because it would be in a local variable
1070 // here) and that's bad. If there is a chance for that to happen, we'll have
1071 // to move the callback used to be a CancelableCallback. By the way, for this
1072 // to happen the action (to cancel B) has to be synchronous to the
1073 // notification for request A.
1074 WorkItemList pending_items;
1075 pending_items.swap(pending_op->pending_queue);
1076 DeletePendingOp(pending_op);
1078 item->NotifyTransaction(result, entry);
1080 while (!pending_items.empty()) {
1081 item.reset(pending_items.front());
1082 pending_items.pop_front();
1084 if (item->operation() == WI_DOOM_ENTRY) {
1085 // A queued doom request is always a race.
1086 fail_requests = true;
1087 } else if (result == OK) {
1088 entry = FindActiveEntry(key);
1090 fail_requests = true;
1093 if (fail_requests) {
1094 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1098 if (item->operation() == WI_CREATE_ENTRY) {
1100 // A second Create request, but the first request succeeded.
1101 item->NotifyTransaction(ERR_CACHE_CREATE_FAILURE, NULL);
1103 if (op != WI_CREATE_ENTRY) {
1104 // Failed Open followed by a Create.
1105 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1106 fail_requests = true;
1108 item->NotifyTransaction(result, entry);
1112 if (op == WI_CREATE_ENTRY && result != OK) {
1113 // Failed Create followed by an Open.
1114 item->NotifyTransaction(ERR_CACHE_RACE, NULL);
1115 fail_requests = true;
1117 item->NotifyTransaction(result, entry);
1124 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
1125 PendingOp* pending_op,
1128 cache->OnIOComplete(rv, pending_op);
1130 // The callback was cancelled so we should delete the pending_op that
1131 // was used with this callback.
1136 void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
1137 scoped_ptr<WorkItem> item(pending_op->writer);
1138 WorkItemOperation op = item->operation();
1139 DCHECK_EQ(WI_CREATE_BACKEND, op);
1141 // We don't need the callback anymore.
1142 pending_op->callback.Reset();
1144 if (backend_factory_.get()) {
1145 // We may end up calling OnBackendCreated multiple times if we have pending
1146 // work items. The first call saves the backend and releases the factory,
1147 // and the last call clears building_backend_.
1148 backend_factory_.reset(); // Reclaim memory.
1150 disk_cache_ = pending_op->backend.Pass();
1153 if (!pending_op->pending_queue.empty()) {
1154 WorkItem* pending_item = pending_op->pending_queue.front();
1155 pending_op->pending_queue.pop_front();
1156 DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
1158 // We want to process a single callback at a time, because the cache may
1159 // go away from the callback.
1160 pending_op->writer = pending_item;
1162 base::MessageLoop::current()->PostTask(
1165 &HttpCache::OnBackendCreated, AsWeakPtr(), result, pending_op));
1167 building_backend_ = false;
1168 DeletePendingOp(pending_op);
1171 // The cache may be gone when we return from the callback.
1172 if (!item->DoCallback(result, disk_cache_.get()))
1173 item->NotifyTransaction(result, NULL);