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.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 file_util::Delete(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
, 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
), backend(NULL
), writer(NULL
) {}
114 disk_cache::Entry
* disk_entry
;
115 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
) {
598 HttpRequestInfo temp_info
;
600 temp_info
.method
= "GET";
601 std::string key
= GenerateCacheKey(&temp_info
);
603 // Defer to DoomEntry if there is an active entry, otherwise call
604 // AsyncDoomEntry without triggering a callback.
605 if (active_entries_
.count(key
))
606 DoomEntry(key
, NULL
);
608 AsyncDoomEntry(key
, NULL
);
611 void HttpCache::FinalizeDoomedEntry(ActiveEntry
* entry
) {
612 DCHECK(entry
->doomed
);
613 DCHECK(!entry
->writer
);
614 DCHECK(entry
->readers
.empty());
615 DCHECK(entry
->pending_queue
.empty());
617 ActiveEntriesSet::iterator it
= doomed_entries_
.find(entry
);
618 DCHECK(it
!= doomed_entries_
.end());
619 doomed_entries_
.erase(it
);
624 HttpCache::ActiveEntry
* HttpCache::FindActiveEntry(const std::string
& key
) {
625 ActiveEntriesMap::const_iterator it
= active_entries_
.find(key
);
626 return it
!= active_entries_
.end() ? it
->second
: NULL
;
629 HttpCache::ActiveEntry
* HttpCache::ActivateEntry(
630 disk_cache::Entry
* disk_entry
) {
631 DCHECK(!FindActiveEntry(disk_entry
->GetKey()));
632 ActiveEntry
* entry
= new ActiveEntry(disk_entry
);
633 active_entries_
[disk_entry
->GetKey()] = entry
;
637 void HttpCache::DeactivateEntry(ActiveEntry
* entry
) {
638 DCHECK(!entry
->will_process_pending_queue
);
639 DCHECK(!entry
->doomed
);
640 DCHECK(!entry
->writer
);
641 DCHECK(entry
->disk_entry
);
642 DCHECK(entry
->readers
.empty());
643 DCHECK(entry
->pending_queue
.empty());
645 std::string key
= entry
->disk_entry
->GetKey();
647 return SlowDeactivateEntry(entry
);
649 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
650 DCHECK(it
!= active_entries_
.end());
651 DCHECK(it
->second
== entry
);
653 active_entries_
.erase(it
);
657 // We don't know this entry's key so we have to find it without it.
658 void HttpCache::SlowDeactivateEntry(ActiveEntry
* entry
) {
659 for (ActiveEntriesMap::iterator it
= active_entries_
.begin();
660 it
!= active_entries_
.end(); ++it
) {
661 if (it
->second
== entry
) {
662 active_entries_
.erase(it
);
669 HttpCache::PendingOp
* HttpCache::GetPendingOp(const std::string
& key
) {
670 DCHECK(!FindActiveEntry(key
));
672 PendingOpsMap::const_iterator it
= pending_ops_
.find(key
);
673 if (it
!= pending_ops_
.end())
676 PendingOp
* operation
= new PendingOp();
677 pending_ops_
[key
] = operation
;
681 void HttpCache::DeletePendingOp(PendingOp
* pending_op
) {
683 if (pending_op
->disk_entry
)
684 key
= pending_op
->disk_entry
->GetKey();
687 PendingOpsMap::iterator it
= pending_ops_
.find(key
);
688 DCHECK(it
!= pending_ops_
.end());
689 pending_ops_
.erase(it
);
691 for (PendingOpsMap::iterator it
= pending_ops_
.begin();
692 it
!= pending_ops_
.end(); ++it
) {
693 if (it
->second
== pending_op
) {
694 pending_ops_
.erase(it
);
699 DCHECK(pending_op
->pending_queue
.empty());
704 int HttpCache::OpenEntry(const std::string
& key
, ActiveEntry
** entry
,
705 Transaction
* trans
) {
706 ActiveEntry
* active_entry
= FindActiveEntry(key
);
708 *entry
= active_entry
;
712 WorkItem
* item
= new WorkItem(WI_OPEN_ENTRY
, trans
, entry
);
713 PendingOp
* pending_op
= GetPendingOp(key
);
714 if (pending_op
->writer
) {
715 pending_op
->pending_queue
.push_back(item
);
716 return ERR_IO_PENDING
;
719 DCHECK(pending_op
->pending_queue
.empty());
721 pending_op
->writer
= item
;
722 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
723 AsWeakPtr(), pending_op
);
725 int rv
= disk_cache_
->OpenEntry(key
, &(pending_op
->disk_entry
),
726 pending_op
->callback
);
727 if (rv
!= ERR_IO_PENDING
) {
728 item
->ClearTransaction();
729 pending_op
->callback
.Run(rv
);
735 int HttpCache::CreateEntry(const std::string
& key
, ActiveEntry
** entry
,
736 Transaction
* trans
) {
737 if (FindActiveEntry(key
)) {
738 return ERR_CACHE_RACE
;
741 WorkItem
* item
= new WorkItem(WI_CREATE_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_
->CreateEntry(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 void HttpCache::DestroyEntry(ActiveEntry
* entry
) {
766 FinalizeDoomedEntry(entry
);
768 DeactivateEntry(entry
);
772 int HttpCache::AddTransactionToEntry(ActiveEntry
* entry
, Transaction
* trans
) {
774 DCHECK(entry
->disk_entry
);
776 // We implement a basic reader/writer lock for the disk cache entry. If
777 // there is already a writer, then everyone has to wait for the writer to
778 // finish before they can access the cache entry. There can be multiple
781 // NOTE: If the transaction can only write, then the entry should not be in
782 // use (since any existing entry should have already been doomed).
784 if (entry
->writer
|| entry
->will_process_pending_queue
) {
785 entry
->pending_queue
.push_back(trans
);
786 return ERR_IO_PENDING
;
789 if (trans
->mode() & Transaction::WRITE
) {
790 // transaction needs exclusive access to the entry
791 if (entry
->readers
.empty()) {
792 entry
->writer
= trans
;
794 entry
->pending_queue
.push_back(trans
);
795 return ERR_IO_PENDING
;
798 // transaction needs read access to the entry
799 entry
->readers
.push_back(trans
);
802 // We do this before calling EntryAvailable to force any further calls to
803 // AddTransactionToEntry to add their transaction to the pending queue, which
804 // ensures FIFO ordering.
805 if (!entry
->writer
&& !entry
->pending_queue
.empty())
806 ProcessPendingQueue(entry
);
811 void HttpCache::DoneWithEntry(ActiveEntry
* entry
, Transaction
* trans
,
813 // If we already posted a task to move on to the next transaction and this was
814 // the writer, there is nothing to cancel.
815 if (entry
->will_process_pending_queue
&& entry
->readers
.empty())
819 DCHECK(trans
== entry
->writer
);
821 // Assume there was a failure.
822 bool success
= false;
824 DCHECK(entry
->disk_entry
);
825 // This is a successful operation in the sense that we want to keep the
827 success
= trans
->AddTruncatedFlag();
828 // The previous operation may have deleted the entry.
832 DoneWritingToEntry(entry
, success
);
834 DoneReadingFromEntry(entry
, trans
);
838 void HttpCache::DoneWritingToEntry(ActiveEntry
* entry
, bool success
) {
839 DCHECK(entry
->readers
.empty());
841 entry
->writer
= NULL
;
844 ProcessPendingQueue(entry
);
846 DCHECK(!entry
->will_process_pending_queue
);
848 // We failed to create this entry.
849 TransactionList pending_queue
;
850 pending_queue
.swap(entry
->pending_queue
);
852 entry
->disk_entry
->Doom();
855 // We need to do something about these pending entries, which now need to
856 // be added to a new entry.
857 while (!pending_queue
.empty()) {
858 // ERR_CACHE_RACE causes the transaction to restart the whole process.
859 pending_queue
.front()->io_callback().Run(ERR_CACHE_RACE
);
860 pending_queue
.pop_front();
865 void HttpCache::DoneReadingFromEntry(ActiveEntry
* entry
, Transaction
* trans
) {
866 DCHECK(!entry
->writer
);
868 TransactionList::iterator it
=
869 std::find(entry
->readers
.begin(), entry
->readers
.end(), trans
);
870 DCHECK(it
!= entry
->readers
.end());
872 entry
->readers
.erase(it
);
874 ProcessPendingQueue(entry
);
877 void HttpCache::ConvertWriterToReader(ActiveEntry
* entry
) {
878 DCHECK(entry
->writer
);
879 DCHECK(entry
->writer
->mode() == Transaction::READ_WRITE
);
880 DCHECK(entry
->readers
.empty());
882 Transaction
* trans
= entry
->writer
;
884 entry
->writer
= NULL
;
885 entry
->readers
.push_back(trans
);
887 ProcessPendingQueue(entry
);
890 LoadState
HttpCache::GetLoadStateForPendingTransaction(
891 const Transaction
* trans
) {
892 ActiveEntriesMap::const_iterator i
= active_entries_
.find(trans
->key());
893 if (i
== active_entries_
.end()) {
894 // If this is really a pending transaction, and it is not part of
895 // active_entries_, we should be creating the backend or the entry.
896 return LOAD_STATE_WAITING_FOR_CACHE
;
899 Transaction
* writer
= i
->second
->writer
;
900 return writer
? writer
->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE
;
903 void HttpCache::RemovePendingTransaction(Transaction
* trans
) {
904 ActiveEntriesMap::const_iterator i
= active_entries_
.find(trans
->key());
906 if (i
!= active_entries_
.end())
907 found
= RemovePendingTransactionFromEntry(i
->second
, trans
);
912 if (building_backend_
) {
913 PendingOpsMap::const_iterator j
= pending_ops_
.find(std::string());
914 if (j
!= pending_ops_
.end())
915 found
= RemovePendingTransactionFromPendingOp(j
->second
, trans
);
921 PendingOpsMap::const_iterator j
= pending_ops_
.find(trans
->key());
922 if (j
!= pending_ops_
.end())
923 found
= RemovePendingTransactionFromPendingOp(j
->second
, trans
);
928 ActiveEntriesSet::iterator k
= doomed_entries_
.begin();
929 for (; k
!= doomed_entries_
.end() && !found
; ++k
)
930 found
= RemovePendingTransactionFromEntry(*k
, trans
);
932 DCHECK(found
) << "Pending transaction not found";
935 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry
* entry
,
936 Transaction
* trans
) {
937 TransactionList
& pending_queue
= entry
->pending_queue
;
939 TransactionList::iterator j
=
940 find(pending_queue
.begin(), pending_queue
.end(), trans
);
941 if (j
== pending_queue
.end())
944 pending_queue
.erase(j
);
948 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp
* pending_op
,
949 Transaction
* trans
) {
950 if (pending_op
->writer
->Matches(trans
)) {
951 pending_op
->writer
->ClearTransaction();
952 pending_op
->writer
->ClearEntry();
955 WorkItemList
& pending_queue
= pending_op
->pending_queue
;
957 WorkItemList::iterator it
= pending_queue
.begin();
958 for (; it
!= pending_queue
.end(); ++it
) {
959 if ((*it
)->Matches(trans
)) {
961 pending_queue
.erase(it
);
968 void HttpCache::ProcessPendingQueue(ActiveEntry
* entry
) {
969 // Multiple readers may finish with an entry at once, so we want to batch up
970 // calls to OnProcessPendingQueue. This flag also tells us that we should
971 // not delete the entry before OnProcessPendingQueue runs.
972 if (entry
->will_process_pending_queue
)
974 entry
->will_process_pending_queue
= true;
976 base::MessageLoop::current()->PostTask(
978 base::Bind(&HttpCache::OnProcessPendingQueue
, AsWeakPtr(), entry
));
981 void HttpCache::OnProcessPendingQueue(ActiveEntry
* entry
) {
982 entry
->will_process_pending_queue
= false;
983 DCHECK(!entry
->writer
);
985 // If no one is interested in this entry, then we can deactivate it.
986 if (entry
->pending_queue
.empty()) {
987 if (entry
->readers
.empty())
992 // Promote next transaction from the pending queue.
993 Transaction
* next
= entry
->pending_queue
.front();
994 if ((next
->mode() & Transaction::WRITE
) && !entry
->readers
.empty())
995 return; // Have to wait.
997 entry
->pending_queue
.erase(entry
->pending_queue
.begin());
999 int rv
= AddTransactionToEntry(entry
, next
);
1000 if (rv
!= ERR_IO_PENDING
) {
1001 next
->io_callback().Run(rv
);
1005 void HttpCache::OnIOComplete(int result
, PendingOp
* pending_op
) {
1006 WorkItemOperation op
= pending_op
->writer
->operation();
1008 // Completing the creation of the backend is simpler than the other cases.
1009 if (op
== WI_CREATE_BACKEND
)
1010 return OnBackendCreated(result
, pending_op
);
1012 scoped_ptr
<WorkItem
> item(pending_op
->writer
);
1013 bool fail_requests
= false;
1015 ActiveEntry
* entry
= NULL
;
1018 if (op
== WI_DOOM_ENTRY
) {
1019 // Anything after a Doom has to be restarted.
1020 fail_requests
= true;
1021 } else if (item
->IsValid()) {
1022 key
= pending_op
->disk_entry
->GetKey();
1023 entry
= ActivateEntry(pending_op
->disk_entry
);
1025 // The writer transaction is gone.
1026 if (op
== WI_CREATE_ENTRY
)
1027 pending_op
->disk_entry
->Doom();
1028 pending_op
->disk_entry
->Close();
1029 pending_op
->disk_entry
= NULL
;
1030 fail_requests
= true;
1034 // We are about to notify a bunch of transactions, and they may decide to
1035 // re-issue a request (or send a different one). If we don't delete
1036 // pending_op, the new request will be appended to the end of the list, and
1037 // we'll see it again from this point before it has a chance to complete (and
1038 // we'll be messing out the request order). The down side is that if for some
1039 // reason notifying request A ends up cancelling request B (for the same key),
1040 // we won't find request B anywhere (because it would be in a local variable
1041 // here) and that's bad. If there is a chance for that to happen, we'll have
1042 // to move the callback used to be a CancelableCallback. By the way, for this
1043 // to happen the action (to cancel B) has to be synchronous to the
1044 // notification for request A.
1045 WorkItemList pending_items
;
1046 pending_items
.swap(pending_op
->pending_queue
);
1047 DeletePendingOp(pending_op
);
1049 item
->NotifyTransaction(result
, entry
);
1051 while (!pending_items
.empty()) {
1052 item
.reset(pending_items
.front());
1053 pending_items
.pop_front();
1055 if (item
->operation() == WI_DOOM_ENTRY
) {
1056 // A queued doom request is always a race.
1057 fail_requests
= true;
1058 } else if (result
== OK
) {
1059 entry
= FindActiveEntry(key
);
1061 fail_requests
= true;
1064 if (fail_requests
) {
1065 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1069 if (item
->operation() == WI_CREATE_ENTRY
) {
1071 // A second Create request, but the first request succeeded.
1072 item
->NotifyTransaction(ERR_CACHE_CREATE_FAILURE
, NULL
);
1074 if (op
!= WI_CREATE_ENTRY
) {
1075 // Failed Open followed by a Create.
1076 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1077 fail_requests
= true;
1079 item
->NotifyTransaction(result
, entry
);
1083 if (op
== WI_CREATE_ENTRY
&& result
!= OK
) {
1084 // Failed Create followed by an Open.
1085 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1086 fail_requests
= true;
1088 item
->NotifyTransaction(result
, entry
);
1095 void HttpCache::OnPendingOpComplete(const base::WeakPtr
<HttpCache
>& cache
,
1096 PendingOp
* pending_op
,
1099 cache
->OnIOComplete(rv
, pending_op
);
1101 // The callback was cancelled so we should delete the pending_op that
1102 // was used with this callback.
1107 void HttpCache::OnBackendCreated(int result
, PendingOp
* pending_op
) {
1108 scoped_ptr
<WorkItem
> item(pending_op
->writer
);
1109 WorkItemOperation op
= item
->operation();
1110 DCHECK_EQ(WI_CREATE_BACKEND
, op
);
1112 // We don't need the callback anymore.
1113 pending_op
->callback
.Reset();
1114 disk_cache::Backend
* backend
= pending_op
->backend
;
1116 if (backend_factory_
.get()) {
1117 // We may end up calling OnBackendCreated multiple times if we have pending
1118 // work items. The first call saves the backend and releases the factory,
1119 // and the last call clears building_backend_.
1120 backend_factory_
.reset(); // Reclaim memory.
1122 disk_cache_
.reset(backend
);
1125 if (!pending_op
->pending_queue
.empty()) {
1126 WorkItem
* pending_item
= pending_op
->pending_queue
.front();
1127 pending_op
->pending_queue
.pop_front();
1128 DCHECK_EQ(WI_CREATE_BACKEND
, pending_item
->operation());
1130 // We want to process a single callback at a time, because the cache may
1131 // go away from the callback.
1132 pending_op
->writer
= pending_item
;
1134 base::MessageLoop::current()->PostTask(
1137 &HttpCache::OnBackendCreated
, AsWeakPtr(), result
, pending_op
));
1139 building_backend_
= false;
1140 DeletePendingOp(pending_op
);
1143 // The cache may be gone when we return from the callback.
1144 if (!item
->DoCallback(result
, backend
))
1145 item
->NotifyTransaction(result
, NULL
);