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
* GetForServer(
279 const QuicServerId
& server_id
) OVERRIDE
{
280 return new DiskCacheBasedQuicServerInfo(server_id
, http_cache_
);
284 HttpCache
* const http_cache_
;
287 //-----------------------------------------------------------------------------
288 HttpCache::HttpCache(const net::HttpNetworkSession::Params
& params
,
289 BackendFactory
* backend_factory
)
290 : net_log_(params
.net_log
),
291 backend_factory_(backend_factory
),
292 building_backend_(false),
294 quic_server_info_factory_(params
.enable_quic_persist_server_info
?
295 new QuicServerInfoFactoryAdaptor(this) : NULL
),
296 network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params
))) {
297 HttpNetworkSession
* session
= network_layer_
->GetSession();
298 session
->quic_stream_factory()->set_quic_server_info_factory(
299 quic_server_info_factory_
.get());
303 // This call doesn't change the shared |session|'s QuicServerInfoFactory because
304 // |session| is shared.
305 HttpCache::HttpCache(HttpNetworkSession
* session
,
306 BackendFactory
* backend_factory
)
307 : net_log_(session
->net_log()),
308 backend_factory_(backend_factory
),
309 building_backend_(false),
311 network_layer_(new HttpNetworkLayer(session
)) {
314 HttpCache::HttpCache(HttpTransactionFactory
* network_layer
,
316 BackendFactory
* backend_factory
)
318 backend_factory_(backend_factory
),
319 building_backend_(false),
321 network_layer_(network_layer
) {
324 HttpCache::~HttpCache() {
325 // If we have any active entries remaining, then we need to deactivate them.
326 // We may have some pending calls to OnProcessPendingQueue, but since those
327 // won't run (due to our destruction), we can simply ignore the corresponding
328 // will_process_pending_queue flag.
329 while (!active_entries_
.empty()) {
330 ActiveEntry
* entry
= active_entries_
.begin()->second
;
331 entry
->will_process_pending_queue
= false;
332 entry
->pending_queue
.clear();
333 entry
->readers
.clear();
334 entry
->writer
= NULL
;
335 DeactivateEntry(entry
);
338 STLDeleteElements(&doomed_entries_
);
340 // Before deleting pending_ops_, we have to make sure that the disk cache is
341 // done with said operations, or it will attempt to use deleted data.
344 PendingOpsMap::iterator pending_it
= pending_ops_
.begin();
345 for (; pending_it
!= pending_ops_
.end(); ++pending_it
) {
346 // We are not notifying the transactions about the cache going away, even
347 // though they are waiting for a callback that will never fire.
348 PendingOp
* pending_op
= pending_it
->second
;
349 delete pending_op
->writer
;
350 bool delete_pending_op
= true;
351 if (building_backend_
) {
352 // If we don't have a backend, when its construction finishes it will
353 // deliver the callbacks.
354 if (!pending_op
->callback
.is_null()) {
355 // If not null, the callback will delete the pending operation later.
356 delete_pending_op
= false;
359 pending_op
->callback
.Reset();
362 STLDeleteElements(&pending_op
->pending_queue
);
363 if (delete_pending_op
)
368 int HttpCache::GetBackend(disk_cache::Backend
** backend
,
369 const CompletionCallback
& callback
) {
370 DCHECK(!callback
.is_null());
372 if (disk_cache_
.get()) {
373 *backend
= disk_cache_
.get();
377 return CreateBackend(backend
, callback
);
380 disk_cache::Backend
* HttpCache::GetCurrentBackend() const {
381 return disk_cache_
.get();
385 bool HttpCache::ParseResponseInfo(const char* data
, int len
,
386 HttpResponseInfo
* response_info
,
387 bool* response_truncated
) {
388 Pickle
pickle(data
, len
);
389 return response_info
->InitFromPickle(pickle
, response_truncated
);
392 void HttpCache::WriteMetadata(const GURL
& url
,
393 RequestPriority priority
,
394 base::Time expected_response_time
,
400 // Do lazy initialization of disk cache if needed.
401 if (!disk_cache_
.get()) {
402 // We don't care about the result.
403 CreateBackend(NULL
, net::CompletionCallback());
406 HttpCache::Transaction
* trans
=
407 new HttpCache::Transaction(priority
, this);
408 MetadataWriter
* writer
= new MetadataWriter(trans
);
410 // The writer will self destruct when done.
411 writer
->Write(url
, expected_response_time
, buf
, buf_len
);
414 void HttpCache::CloseAllConnections() {
415 net::HttpNetworkLayer
* network
=
416 static_cast<net::HttpNetworkLayer
*>(network_layer_
.get());
417 HttpNetworkSession
* session
= network
->GetSession();
419 session
->CloseAllConnections();
422 void HttpCache::CloseIdleConnections() {
423 net::HttpNetworkLayer
* network
=
424 static_cast<net::HttpNetworkLayer
*>(network_layer_
.get());
425 HttpNetworkSession
* session
= network
->GetSession();
427 session
->CloseIdleConnections();
430 void HttpCache::OnExternalCacheHit(const GURL
& url
,
431 const std::string
& http_method
) {
432 if (!disk_cache_
.get())
435 HttpRequestInfo request_info
;
436 request_info
.url
= url
;
437 request_info
.method
= http_method
;
438 std::string key
= GenerateCacheKey(&request_info
);
439 disk_cache_
->OnExternalCacheHit(key
);
442 void HttpCache::InitializeInfiniteCache(const base::FilePath
& path
) {
443 if (base::FieldTrialList::FindFullName("InfiniteCache") != "Yes")
445 base::WorkerPool::PostTask(FROM_HERE
, base::Bind(&DeletePath
, path
), true);
448 int HttpCache::CreateTransaction(RequestPriority priority
,
449 scoped_ptr
<HttpTransaction
>* trans
) {
450 // Do lazy initialization of disk cache if needed.
451 if (!disk_cache_
.get()) {
452 // We don't care about the result.
453 CreateBackend(NULL
, net::CompletionCallback());
456 trans
->reset(new HttpCache::Transaction(priority
, this));
460 HttpCache
* HttpCache::GetCache() {
464 HttpNetworkSession
* HttpCache::GetSession() {
465 net::HttpNetworkLayer
* network
=
466 static_cast<net::HttpNetworkLayer
*>(network_layer_
.get());
467 return network
->GetSession();
470 scoped_ptr
<HttpTransactionFactory
>
471 HttpCache::SetHttpNetworkTransactionFactoryForTesting(
472 scoped_ptr
<HttpTransactionFactory
> new_network_layer
) {
473 scoped_ptr
<HttpTransactionFactory
> old_network_layer(network_layer_
.Pass());
474 network_layer_
= new_network_layer
.Pass();
475 return old_network_layer
.Pass();
478 //-----------------------------------------------------------------------------
480 int HttpCache::CreateBackend(disk_cache::Backend
** backend
,
481 const net::CompletionCallback
& callback
) {
482 if (!backend_factory_
.get())
485 building_backend_
= true;
487 scoped_ptr
<WorkItem
> item(new WorkItem(WI_CREATE_BACKEND
, NULL
, callback
,
490 // This is the only operation that we can do that is not related to any given
491 // entry, so we use an empty key for it.
492 PendingOp
* pending_op
= GetPendingOp(std::string());
493 if (pending_op
->writer
) {
494 if (!callback
.is_null())
495 pending_op
->pending_queue
.push_back(item
.release());
496 return ERR_IO_PENDING
;
499 DCHECK(pending_op
->pending_queue
.empty());
501 pending_op
->writer
= item
.release();
502 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
503 AsWeakPtr(), pending_op
);
505 int rv
= backend_factory_
->CreateBackend(net_log_
, &pending_op
->backend
,
506 pending_op
->callback
);
507 if (rv
!= ERR_IO_PENDING
) {
508 pending_op
->writer
->ClearCallback();
509 pending_op
->callback
.Run(rv
);
515 int HttpCache::GetBackendForTransaction(Transaction
* trans
) {
516 if (disk_cache_
.get())
519 if (!building_backend_
)
522 WorkItem
* item
= new WorkItem(
523 WI_CREATE_BACKEND
, trans
, net::CompletionCallback(), NULL
);
524 PendingOp
* pending_op
= GetPendingOp(std::string());
525 DCHECK(pending_op
->writer
);
526 pending_op
->pending_queue
.push_back(item
);
527 return ERR_IO_PENDING
;
530 // Generate a key that can be used inside the cache.
531 std::string
HttpCache::GenerateCacheKey(const HttpRequestInfo
* request
) {
532 // Strip out the reference, username, and password sections of the URL.
533 std::string url
= HttpUtil::SpecForRequest(request
->url
);
535 DCHECK(mode_
!= DISABLE
);
536 if (mode_
== NORMAL
) {
537 // No valid URL can begin with numerals, so we should not have to worry
538 // about collisions with normal URLs.
539 if (request
->upload_data_stream
&&
540 request
->upload_data_stream
->identifier()) {
541 url
.insert(0, base::StringPrintf(
542 "%" PRId64
"/", request
->upload_data_stream
->identifier()));
547 // In playback and record mode, we cache everything.
549 // Lazily initialize.
550 if (playback_cache_map_
== NULL
)
551 playback_cache_map_
.reset(new PlaybackCacheMap());
553 // Each time we request an item from the cache, we tag it with a
554 // generation number. During playback, multiple fetches for the same
555 // item will use the same generation number and pull the proper
556 // instance of an URL from the cache.
558 DCHECK(playback_cache_map_
!= NULL
);
559 if (playback_cache_map_
->find(url
) != playback_cache_map_
->end())
560 generation
= (*playback_cache_map_
)[url
];
561 (*playback_cache_map_
)[url
] = generation
+ 1;
563 // The key into the cache is GENERATION # + METHOD + URL.
564 std::string result
= base::IntToString(generation
);
565 result
.append(request
->method
);
570 void HttpCache::DoomActiveEntry(const std::string
& key
) {
571 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
572 if (it
== active_entries_
.end())
575 // This is not a performance critical operation, this is handling an error
576 // condition so it is OK to look up the entry again.
577 int rv
= DoomEntry(key
, NULL
);
581 int HttpCache::DoomEntry(const std::string
& key
, Transaction
* trans
) {
582 // Need to abandon the ActiveEntry, but any transaction attached to the entry
583 // should not be impacted. Dooming an entry only means that it will no
584 // longer be returned by FindActiveEntry (and it will also be destroyed once
585 // all consumers are finished with the entry).
586 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
587 if (it
== active_entries_
.end()) {
589 return AsyncDoomEntry(key
, trans
);
592 ActiveEntry
* entry
= it
->second
;
593 active_entries_
.erase(it
);
595 // We keep track of doomed entries so that we can ensure that they are
596 // cleaned up properly when the cache is destroyed.
597 doomed_entries_
.insert(entry
);
599 entry
->disk_entry
->Doom();
600 entry
->doomed
= true;
602 DCHECK(entry
->writer
|| !entry
->readers
.empty());
606 int HttpCache::AsyncDoomEntry(const std::string
& key
, Transaction
* trans
) {
607 WorkItem
* item
= new WorkItem(WI_DOOM_ENTRY
, trans
, NULL
);
608 PendingOp
* pending_op
= GetPendingOp(key
);
609 if (pending_op
->writer
) {
610 pending_op
->pending_queue
.push_back(item
);
611 return ERR_IO_PENDING
;
614 DCHECK(pending_op
->pending_queue
.empty());
616 pending_op
->writer
= item
;
617 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
618 AsWeakPtr(), pending_op
);
620 int rv
= disk_cache_
->DoomEntry(key
, pending_op
->callback
);
621 if (rv
!= ERR_IO_PENDING
) {
622 item
->ClearTransaction();
623 pending_op
->callback
.Run(rv
);
629 void HttpCache::DoomMainEntryForUrl(const GURL
& url
) {
633 HttpRequestInfo temp_info
;
635 temp_info
.method
= "GET";
636 std::string key
= GenerateCacheKey(&temp_info
);
638 // Defer to DoomEntry if there is an active entry, otherwise call
639 // AsyncDoomEntry without triggering a callback.
640 if (active_entries_
.count(key
))
641 DoomEntry(key
, NULL
);
643 AsyncDoomEntry(key
, NULL
);
646 void HttpCache::FinalizeDoomedEntry(ActiveEntry
* entry
) {
647 DCHECK(entry
->doomed
);
648 DCHECK(!entry
->writer
);
649 DCHECK(entry
->readers
.empty());
650 DCHECK(entry
->pending_queue
.empty());
652 ActiveEntriesSet::iterator it
= doomed_entries_
.find(entry
);
653 DCHECK(it
!= doomed_entries_
.end());
654 doomed_entries_
.erase(it
);
659 HttpCache::ActiveEntry
* HttpCache::FindActiveEntry(const std::string
& key
) {
660 ActiveEntriesMap::const_iterator it
= active_entries_
.find(key
);
661 return it
!= active_entries_
.end() ? it
->second
: NULL
;
664 HttpCache::ActiveEntry
* HttpCache::ActivateEntry(
665 disk_cache::Entry
* disk_entry
) {
666 DCHECK(!FindActiveEntry(disk_entry
->GetKey()));
667 ActiveEntry
* entry
= new ActiveEntry(disk_entry
);
668 active_entries_
[disk_entry
->GetKey()] = entry
;
672 void HttpCache::DeactivateEntry(ActiveEntry
* entry
) {
673 DCHECK(!entry
->will_process_pending_queue
);
674 DCHECK(!entry
->doomed
);
675 DCHECK(!entry
->writer
);
676 DCHECK(entry
->disk_entry
);
677 DCHECK(entry
->readers
.empty());
678 DCHECK(entry
->pending_queue
.empty());
680 std::string key
= entry
->disk_entry
->GetKey();
682 return SlowDeactivateEntry(entry
);
684 ActiveEntriesMap::iterator it
= active_entries_
.find(key
);
685 DCHECK(it
!= active_entries_
.end());
686 DCHECK(it
->second
== entry
);
688 active_entries_
.erase(it
);
692 // We don't know this entry's key so we have to find it without it.
693 void HttpCache::SlowDeactivateEntry(ActiveEntry
* entry
) {
694 for (ActiveEntriesMap::iterator it
= active_entries_
.begin();
695 it
!= active_entries_
.end(); ++it
) {
696 if (it
->second
== entry
) {
697 active_entries_
.erase(it
);
704 HttpCache::PendingOp
* HttpCache::GetPendingOp(const std::string
& key
) {
705 DCHECK(!FindActiveEntry(key
));
707 PendingOpsMap::const_iterator it
= pending_ops_
.find(key
);
708 if (it
!= pending_ops_
.end())
711 PendingOp
* operation
= new PendingOp();
712 pending_ops_
[key
] = operation
;
716 void HttpCache::DeletePendingOp(PendingOp
* pending_op
) {
718 if (pending_op
->disk_entry
)
719 key
= pending_op
->disk_entry
->GetKey();
722 PendingOpsMap::iterator it
= pending_ops_
.find(key
);
723 DCHECK(it
!= pending_ops_
.end());
724 pending_ops_
.erase(it
);
726 for (PendingOpsMap::iterator it
= pending_ops_
.begin();
727 it
!= pending_ops_
.end(); ++it
) {
728 if (it
->second
== pending_op
) {
729 pending_ops_
.erase(it
);
734 DCHECK(pending_op
->pending_queue
.empty());
739 int HttpCache::OpenEntry(const std::string
& key
, ActiveEntry
** entry
,
740 Transaction
* trans
) {
741 ActiveEntry
* active_entry
= FindActiveEntry(key
);
743 *entry
= active_entry
;
747 WorkItem
* item
= new WorkItem(WI_OPEN_ENTRY
, trans
, entry
);
748 PendingOp
* pending_op
= GetPendingOp(key
);
749 if (pending_op
->writer
) {
750 pending_op
->pending_queue
.push_back(item
);
751 return ERR_IO_PENDING
;
754 DCHECK(pending_op
->pending_queue
.empty());
756 pending_op
->writer
= item
;
757 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
758 AsWeakPtr(), pending_op
);
760 int rv
= disk_cache_
->OpenEntry(key
, &(pending_op
->disk_entry
),
761 pending_op
->callback
);
762 if (rv
!= ERR_IO_PENDING
) {
763 item
->ClearTransaction();
764 pending_op
->callback
.Run(rv
);
770 int HttpCache::CreateEntry(const std::string
& key
, ActiveEntry
** entry
,
771 Transaction
* trans
) {
772 if (FindActiveEntry(key
)) {
773 return ERR_CACHE_RACE
;
776 WorkItem
* item
= new WorkItem(WI_CREATE_ENTRY
, trans
, entry
);
777 PendingOp
* pending_op
= GetPendingOp(key
);
778 if (pending_op
->writer
) {
779 pending_op
->pending_queue
.push_back(item
);
780 return ERR_IO_PENDING
;
783 DCHECK(pending_op
->pending_queue
.empty());
785 pending_op
->writer
= item
;
786 pending_op
->callback
= base::Bind(&HttpCache::OnPendingOpComplete
,
787 AsWeakPtr(), pending_op
);
789 int rv
= disk_cache_
->CreateEntry(key
, &(pending_op
->disk_entry
),
790 pending_op
->callback
);
791 if (rv
!= ERR_IO_PENDING
) {
792 item
->ClearTransaction();
793 pending_op
->callback
.Run(rv
);
799 void HttpCache::DestroyEntry(ActiveEntry
* entry
) {
801 FinalizeDoomedEntry(entry
);
803 DeactivateEntry(entry
);
807 int HttpCache::AddTransactionToEntry(ActiveEntry
* entry
, Transaction
* trans
) {
809 DCHECK(entry
->disk_entry
);
811 // We implement a basic reader/writer lock for the disk cache entry. If
812 // there is already a writer, then everyone has to wait for the writer to
813 // finish before they can access the cache entry. There can be multiple
816 // NOTE: If the transaction can only write, then the entry should not be in
817 // use (since any existing entry should have already been doomed).
819 if (entry
->writer
|| entry
->will_process_pending_queue
) {
820 entry
->pending_queue
.push_back(trans
);
821 return ERR_IO_PENDING
;
824 if (trans
->mode() & Transaction::WRITE
) {
825 // transaction needs exclusive access to the entry
826 if (entry
->readers
.empty()) {
827 entry
->writer
= trans
;
829 entry
->pending_queue
.push_back(trans
);
830 return ERR_IO_PENDING
;
833 // transaction needs read access to the entry
834 entry
->readers
.push_back(trans
);
837 // We do this before calling EntryAvailable to force any further calls to
838 // AddTransactionToEntry to add their transaction to the pending queue, which
839 // ensures FIFO ordering.
840 if (!entry
->writer
&& !entry
->pending_queue
.empty())
841 ProcessPendingQueue(entry
);
846 void HttpCache::DoneWithEntry(ActiveEntry
* entry
, Transaction
* trans
,
848 // If we already posted a task to move on to the next transaction and this was
849 // the writer, there is nothing to cancel.
850 if (entry
->will_process_pending_queue
&& entry
->readers
.empty())
854 DCHECK(trans
== entry
->writer
);
856 // Assume there was a failure.
857 bool success
= false;
859 DCHECK(entry
->disk_entry
);
860 // This is a successful operation in the sense that we want to keep the
862 success
= trans
->AddTruncatedFlag();
863 // The previous operation may have deleted the entry.
867 DoneWritingToEntry(entry
, success
);
869 DoneReadingFromEntry(entry
, trans
);
873 void HttpCache::DoneWritingToEntry(ActiveEntry
* entry
, bool success
) {
874 DCHECK(entry
->readers
.empty());
876 entry
->writer
= NULL
;
879 ProcessPendingQueue(entry
);
881 DCHECK(!entry
->will_process_pending_queue
);
883 // We failed to create this entry.
884 TransactionList pending_queue
;
885 pending_queue
.swap(entry
->pending_queue
);
887 entry
->disk_entry
->Doom();
890 // We need to do something about these pending entries, which now need to
891 // be added to a new entry.
892 while (!pending_queue
.empty()) {
893 // ERR_CACHE_RACE causes the transaction to restart the whole process.
894 pending_queue
.front()->io_callback().Run(ERR_CACHE_RACE
);
895 pending_queue
.pop_front();
900 void HttpCache::DoneReadingFromEntry(ActiveEntry
* entry
, Transaction
* trans
) {
901 DCHECK(!entry
->writer
);
903 TransactionList::iterator it
=
904 std::find(entry
->readers
.begin(), entry
->readers
.end(), trans
);
905 DCHECK(it
!= entry
->readers
.end());
907 entry
->readers
.erase(it
);
909 ProcessPendingQueue(entry
);
912 void HttpCache::ConvertWriterToReader(ActiveEntry
* entry
) {
913 DCHECK(entry
->writer
);
914 DCHECK(entry
->writer
->mode() == Transaction::READ_WRITE
);
915 DCHECK(entry
->readers
.empty());
917 Transaction
* trans
= entry
->writer
;
919 entry
->writer
= NULL
;
920 entry
->readers
.push_back(trans
);
922 ProcessPendingQueue(entry
);
925 LoadState
HttpCache::GetLoadStateForPendingTransaction(
926 const Transaction
* trans
) {
927 ActiveEntriesMap::const_iterator i
= active_entries_
.find(trans
->key());
928 if (i
== active_entries_
.end()) {
929 // If this is really a pending transaction, and it is not part of
930 // active_entries_, we should be creating the backend or the entry.
931 return LOAD_STATE_WAITING_FOR_CACHE
;
934 Transaction
* writer
= i
->second
->writer
;
935 return writer
? writer
->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE
;
938 void HttpCache::RemovePendingTransaction(Transaction
* trans
) {
939 ActiveEntriesMap::const_iterator i
= active_entries_
.find(trans
->key());
941 if (i
!= active_entries_
.end())
942 found
= RemovePendingTransactionFromEntry(i
->second
, trans
);
947 if (building_backend_
) {
948 PendingOpsMap::const_iterator j
= pending_ops_
.find(std::string());
949 if (j
!= pending_ops_
.end())
950 found
= RemovePendingTransactionFromPendingOp(j
->second
, trans
);
956 PendingOpsMap::const_iterator j
= pending_ops_
.find(trans
->key());
957 if (j
!= pending_ops_
.end())
958 found
= RemovePendingTransactionFromPendingOp(j
->second
, trans
);
963 ActiveEntriesSet::iterator k
= doomed_entries_
.begin();
964 for (; k
!= doomed_entries_
.end() && !found
; ++k
)
965 found
= RemovePendingTransactionFromEntry(*k
, trans
);
967 DCHECK(found
) << "Pending transaction not found";
970 bool HttpCache::RemovePendingTransactionFromEntry(ActiveEntry
* entry
,
971 Transaction
* trans
) {
972 TransactionList
& pending_queue
= entry
->pending_queue
;
974 TransactionList::iterator j
=
975 find(pending_queue
.begin(), pending_queue
.end(), trans
);
976 if (j
== pending_queue
.end())
979 pending_queue
.erase(j
);
983 bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp
* pending_op
,
984 Transaction
* trans
) {
985 if (pending_op
->writer
->Matches(trans
)) {
986 pending_op
->writer
->ClearTransaction();
987 pending_op
->writer
->ClearEntry();
990 WorkItemList
& pending_queue
= pending_op
->pending_queue
;
992 WorkItemList::iterator it
= pending_queue
.begin();
993 for (; it
!= pending_queue
.end(); ++it
) {
994 if ((*it
)->Matches(trans
)) {
996 pending_queue
.erase(it
);
1003 void HttpCache::ProcessPendingQueue(ActiveEntry
* entry
) {
1004 // Multiple readers may finish with an entry at once, so we want to batch up
1005 // calls to OnProcessPendingQueue. This flag also tells us that we should
1006 // not delete the entry before OnProcessPendingQueue runs.
1007 if (entry
->will_process_pending_queue
)
1009 entry
->will_process_pending_queue
= true;
1011 base::MessageLoop::current()->PostTask(
1013 base::Bind(&HttpCache::OnProcessPendingQueue
, AsWeakPtr(), entry
));
1016 void HttpCache::OnProcessPendingQueue(ActiveEntry
* entry
) {
1017 entry
->will_process_pending_queue
= false;
1018 DCHECK(!entry
->writer
);
1020 // If no one is interested in this entry, then we can deactivate it.
1021 if (entry
->pending_queue
.empty()) {
1022 if (entry
->readers
.empty())
1023 DestroyEntry(entry
);
1027 // Promote next transaction from the pending queue.
1028 Transaction
* next
= entry
->pending_queue
.front();
1029 if ((next
->mode() & Transaction::WRITE
) && !entry
->readers
.empty())
1030 return; // Have to wait.
1032 entry
->pending_queue
.erase(entry
->pending_queue
.begin());
1034 int rv
= AddTransactionToEntry(entry
, next
);
1035 if (rv
!= ERR_IO_PENDING
) {
1036 next
->io_callback().Run(rv
);
1040 void HttpCache::OnIOComplete(int result
, PendingOp
* pending_op
) {
1041 WorkItemOperation op
= pending_op
->writer
->operation();
1043 // Completing the creation of the backend is simpler than the other cases.
1044 if (op
== WI_CREATE_BACKEND
)
1045 return OnBackendCreated(result
, pending_op
);
1047 scoped_ptr
<WorkItem
> item(pending_op
->writer
);
1048 bool fail_requests
= false;
1050 ActiveEntry
* entry
= NULL
;
1053 if (op
== WI_DOOM_ENTRY
) {
1054 // Anything after a Doom has to be restarted.
1055 fail_requests
= true;
1056 } else if (item
->IsValid()) {
1057 key
= pending_op
->disk_entry
->GetKey();
1058 entry
= ActivateEntry(pending_op
->disk_entry
);
1060 // The writer transaction is gone.
1061 if (op
== WI_CREATE_ENTRY
)
1062 pending_op
->disk_entry
->Doom();
1063 pending_op
->disk_entry
->Close();
1064 pending_op
->disk_entry
= NULL
;
1065 fail_requests
= true;
1069 // We are about to notify a bunch of transactions, and they may decide to
1070 // re-issue a request (or send a different one). If we don't delete
1071 // pending_op, the new request will be appended to the end of the list, and
1072 // we'll see it again from this point before it has a chance to complete (and
1073 // we'll be messing out the request order). The down side is that if for some
1074 // reason notifying request A ends up cancelling request B (for the same key),
1075 // we won't find request B anywhere (because it would be in a local variable
1076 // here) and that's bad. If there is a chance for that to happen, we'll have
1077 // to move the callback used to be a CancelableCallback. By the way, for this
1078 // to happen the action (to cancel B) has to be synchronous to the
1079 // notification for request A.
1080 WorkItemList pending_items
;
1081 pending_items
.swap(pending_op
->pending_queue
);
1082 DeletePendingOp(pending_op
);
1084 item
->NotifyTransaction(result
, entry
);
1086 while (!pending_items
.empty()) {
1087 item
.reset(pending_items
.front());
1088 pending_items
.pop_front();
1090 if (item
->operation() == WI_DOOM_ENTRY
) {
1091 // A queued doom request is always a race.
1092 fail_requests
= true;
1093 } else if (result
== OK
) {
1094 entry
= FindActiveEntry(key
);
1096 fail_requests
= true;
1099 if (fail_requests
) {
1100 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1104 if (item
->operation() == WI_CREATE_ENTRY
) {
1106 // A second Create request, but the first request succeeded.
1107 item
->NotifyTransaction(ERR_CACHE_CREATE_FAILURE
, NULL
);
1109 if (op
!= WI_CREATE_ENTRY
) {
1110 // Failed Open followed by a Create.
1111 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1112 fail_requests
= true;
1114 item
->NotifyTransaction(result
, entry
);
1118 if (op
== WI_CREATE_ENTRY
&& result
!= OK
) {
1119 // Failed Create followed by an Open.
1120 item
->NotifyTransaction(ERR_CACHE_RACE
, NULL
);
1121 fail_requests
= true;
1123 item
->NotifyTransaction(result
, entry
);
1130 void HttpCache::OnPendingOpComplete(const base::WeakPtr
<HttpCache
>& cache
,
1131 PendingOp
* pending_op
,
1134 cache
->OnIOComplete(rv
, pending_op
);
1136 // The callback was cancelled so we should delete the pending_op that
1137 // was used with this callback.
1142 void HttpCache::OnBackendCreated(int result
, PendingOp
* pending_op
) {
1143 scoped_ptr
<WorkItem
> item(pending_op
->writer
);
1144 WorkItemOperation op
= item
->operation();
1145 DCHECK_EQ(WI_CREATE_BACKEND
, op
);
1147 // We don't need the callback anymore.
1148 pending_op
->callback
.Reset();
1150 if (backend_factory_
.get()) {
1151 // We may end up calling OnBackendCreated multiple times if we have pending
1152 // work items. The first call saves the backend and releases the factory,
1153 // and the last call clears building_backend_.
1154 backend_factory_
.reset(); // Reclaim memory.
1156 disk_cache_
= pending_op
->backend
.Pass();
1159 if (!pending_op
->pending_queue
.empty()) {
1160 WorkItem
* pending_item
= pending_op
->pending_queue
.front();
1161 pending_op
->pending_queue
.pop_front();
1162 DCHECK_EQ(WI_CREATE_BACKEND
, pending_item
->operation());
1164 // We want to process a single callback at a time, because the cache may
1165 // go away from the callback.
1166 pending_op
->writer
= pending_item
;
1168 base::MessageLoop::current()->PostTask(
1171 &HttpCache::OnBackendCreated
, AsWeakPtr(), result
, pending_op
));
1173 building_backend_
= false;
1174 DeletePendingOp(pending_op
);
1177 // The cache may be gone when we return from the callback.
1178 if (!item
->DoCallback(result
, disk_cache_
.get()))
1179 item
->NotifyTransaction(result
, NULL
);