1 // Copyright 2014 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/disk_cache_based_quic_server_info.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "net/base/completion_callback.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16 #include "net/http/http_cache.h"
17 #include "net/http/http_network_session.h"
18 #include "net/quic/quic_server_id.h"
22 // Some APIs inside disk_cache take a handle that the caller must keep alive
23 // until the API has finished its asynchronous execution.
25 // Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the
26 // operation completes causing a use-after-free.
28 // This data shim struct is meant to provide a location for the disk_cache
29 // APIs to write into even if the originating DiskCacheBasedQuicServerInfo
30 // object has been deleted. The lifetime for instances of this struct
31 // should be bound to the CompletionCallback that is passed to the disk_cache
32 // API. We do this by binding an instance of this struct to an unused
33 // parameter for OnIOComplete() using base::Owned().
35 // This is a hack. A better fix is to make it so that the disk_cache APIs
36 // take a Callback to a mutator for setting the output value rather than
37 // writing into a raw handle. Then the caller can just pass in a Callback
38 // bound to WeakPtr for itself. This callback would correctly "no-op" itself
39 // when the DiskCacheBasedQuicServerInfo object is deleted.
41 // TODO(ajwong): Change disk_cache's API to return results via Callback.
42 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim
{
43 CacheOperationDataShim() : backend(NULL
), entry(NULL
) {}
45 disk_cache::Backend
* backend
;
46 disk_cache::Entry
* entry
;
49 DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo(
50 const QuicServerId
& server_id
,
51 HttpCache
* http_cache
)
52 : QuicServerInfo(server_id
),
53 data_shim_(new CacheOperationDataShim()),
57 server_id_(server_id
),
58 http_cache_(http_cache
),
61 last_failure_(NO_FAILURE
),
64 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete
,
65 weak_factory_
.GetWeakPtr(),
66 base::Owned(data_shim_
)); // Ownership assigned.
69 void DiskCacheBasedQuicServerInfo::Start() {
70 DCHECK(CalledOnValidThread());
71 DCHECK_EQ(GET_BACKEND
, state_
);
72 DCHECK_EQ(last_failure_
, NO_FAILURE
);
73 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_START
);
74 load_start_time_
= base::TimeTicks::Now();
78 int DiskCacheBasedQuicServerInfo::WaitForDataReady(
79 const CompletionCallback
& callback
) {
80 DCHECK(CalledOnValidThread());
81 DCHECK_NE(GET_BACKEND
, state_
);
82 wait_for_data_start_time_
= base::TimeTicks::Now();
84 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY
);
86 wait_for_data_end_time_
= base::TimeTicks::Now();
91 if (!callback
.is_null()) {
92 // Prevent a new callback for WaitForDataReady overwriting an existing
93 // pending callback (|wait_for_ready_callback_|).
94 if (!wait_for_ready_callback_
.is_null()) {
95 RecordQuicServerInfoFailure(WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE
);
96 return ERR_INVALID_ARGUMENT
;
98 wait_for_ready_callback_
= callback
;
101 return ERR_IO_PENDING
;
104 void DiskCacheBasedQuicServerInfo::CancelWaitForDataReadyCallback() {
105 DCHECK(CalledOnValidThread());
107 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL
);
108 if (!wait_for_ready_callback_
.is_null()) {
110 wait_for_ready_callback_
.Reset();
114 bool DiskCacheBasedQuicServerInfo::IsDataReady() {
118 bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() {
119 // The data can be persisted if it has been loaded from the disk cache
120 // and there are no pending writes.
121 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_READY_TO_PERSIST
);
122 if (ready_
&& new_data_
.empty())
124 RecordQuicServerInfoFailure(READY_TO_PERSIST_FAILURE
);
128 void DiskCacheBasedQuicServerInfo::Persist() {
129 DCHECK(CalledOnValidThread());
130 if (!IsReadyToPersist()) {
131 // Handle updates while a write is pending or if we haven't loaded from disk
132 // cache. Save the data to be written into a temporary buffer and then
133 // persist that data when we are ready to persist.
134 pending_write_data_
= Serialize();
140 void DiskCacheBasedQuicServerInfo::PersistInternal() {
141 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
142 tracked_objects::ScopedTracker
tracking_profile(
143 FROM_HERE_WITH_EXPLICIT_FUNCTION(
144 "422516 DiskCacheBasedQuicServerInfo::PersistInternal"));
146 DCHECK(CalledOnValidThread());
147 DCHECK_NE(GET_BACKEND
, state_
);
148 DCHECK(new_data_
.empty());
150 DCHECK(wait_for_ready_callback_
.is_null());
152 if (pending_write_data_
.empty()) {
153 new_data_
= Serialize();
155 new_data_
= pending_write_data_
;
156 pending_write_data_
.clear();
159 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST
);
161 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE
);
165 state_
= CREATE_OR_OPEN
;
169 void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() {
170 DCHECK(CalledOnValidThread());
171 DCHECK_NE(GET_BACKEND
, state_
);
173 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT
);
175 RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE
);
179 backend_
->OnExternalCacheHit(key());
182 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
183 DCHECK(wait_for_ready_callback_
.is_null());
188 std::string
DiskCacheBasedQuicServerInfo::key() const {
189 return "quicserverinfo:" + server_id_
.ToString();
192 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim
* unused
,
194 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
195 tracked_objects::ScopedTracker
tracking_profile(
196 FROM_HERE_WITH_EXPLICIT_FUNCTION(
197 "422516 DiskCacheBasedQuicServerInfo::OnIOComplete"));
199 DCHECK_NE(NONE
, state_
);
201 if (rv
== ERR_IO_PENDING
)
204 base::WeakPtr
<DiskCacheBasedQuicServerInfo
> weak_this
=
205 weak_factory_
.GetWeakPtr();
207 if (!wait_for_ready_callback_
.is_null()) {
208 wait_for_data_end_time_
= base::TimeTicks::Now();
210 base::ResetAndReturn(&wait_for_ready_callback_
).Run(rv
);
212 // |wait_for_ready_callback_| could delete the object if there is an error.
213 // Check if |weak_this| still exists before accessing it.
214 if (weak_this
.get() && ready_
&& !pending_write_data_
.empty()) {
215 DCHECK_EQ(NONE
, state_
);
220 int DiskCacheBasedQuicServerInfo::DoLoop(int rv
) {
226 case GET_BACKEND_COMPLETE
:
227 rv
= DoGetBackendComplete(rv
);
233 rv
= DoOpenComplete(rv
);
239 rv
= DoReadComplete(rv
);
241 case WAIT_FOR_DATA_READY_DONE
:
242 rv
= DoWaitForDataReadyDone();
245 rv
= DoCreateOrOpen();
247 case CREATE_OR_OPEN_COMPLETE
:
248 rv
= DoCreateOrOpenComplete(rv
);
254 rv
= DoWriteComplete(rv
);
263 } while (rv
!= ERR_IO_PENDING
&& state_
!= NONE
);
268 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv
) {
269 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
270 tracked_objects::ScopedTracker
tracking_profile(
271 FROM_HERE_WITH_EXPLICIT_FUNCTION(
272 "422516 DiskCacheBasedQuicServerInfo::DoGetBackendComplete"));
275 backend_
= data_shim_
->backend
;
278 RecordQuicServerInfoFailure(GET_BACKEND_FAILURE
);
279 state_
= WAIT_FOR_DATA_READY_DONE
;
284 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv
) {
285 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
286 tracked_objects::ScopedTracker
tracking_profile(
287 FROM_HERE_WITH_EXPLICIT_FUNCTION(
288 "422516 DiskCacheBasedQuicServerInfo::DoOpenComplete"));
291 entry_
= data_shim_
->entry
;
295 RecordQuicServerInfoFailure(OPEN_FAILURE
);
296 state_
= WAIT_FOR_DATA_READY_DONE
;
302 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv
) {
303 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
304 tracked_objects::ScopedTracker
tracking_profile(
305 FROM_HERE_WITH_EXPLICIT_FUNCTION(
306 "422516 DiskCacheBasedQuicServerInfo::DoReadComplete"));
309 data_
.assign(read_buffer_
->data(), rv
);
311 RecordQuicServerInfoFailure(READ_FAILURE
);
313 state_
= WAIT_FOR_DATA_READY_DONE
;
317 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv
) {
318 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
319 tracked_objects::ScopedTracker
tracking_profile(
320 FROM_HERE_WITH_EXPLICIT_FUNCTION(
321 "422516 DiskCacheBasedQuicServerInfo::DoWriteComplete"));
324 RecordQuicServerInfoFailure(WRITE_FAILURE
);
329 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv
) {
330 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
331 tracked_objects::ScopedTracker
tracking_profile(
332 FROM_HERE_WITH_EXPLICIT_FUNCTION(
333 "422516 DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete"));
336 RecordQuicServerInfoFailure(CREATE_OR_OPEN_FAILURE
);
340 entry_
= data_shim_
->entry
;
349 int DiskCacheBasedQuicServerInfo::DoGetBackend() {
350 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
351 tracked_objects::ScopedTracker
tracking_profile(
352 FROM_HERE_WITH_EXPLICIT_FUNCTION(
353 "422516 DiskCacheBasedQuicServerInfo::DoGetBackend"));
355 state_
= GET_BACKEND_COMPLETE
;
356 return http_cache_
->GetBackend(&data_shim_
->backend
, io_callback_
);
359 int DiskCacheBasedQuicServerInfo::DoOpen() {
360 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
361 tracked_objects::ScopedTracker
tracking_profile(
362 FROM_HERE_WITH_EXPLICIT_FUNCTION(
363 "422516 DiskCacheBasedQuicServerInfo::DoOpen"));
365 state_
= OPEN_COMPLETE
;
366 return backend_
->OpenEntry(key(), &data_shim_
->entry
, io_callback_
);
369 int DiskCacheBasedQuicServerInfo::DoRead() {
370 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
371 tracked_objects::ScopedTracker
tracking_profile(
372 FROM_HERE_WITH_EXPLICIT_FUNCTION(
373 "422516 DiskCacheBasedQuicServerInfo::DoRead"));
375 const int32 size
= entry_
->GetDataSize(0 /* index */);
377 state_
= WAIT_FOR_DATA_READY_DONE
;
381 read_buffer_
= new IOBuffer(size
);
382 state_
= READ_COMPLETE
;
383 return entry_
->ReadData(
384 0 /* index */, 0 /* offset */, read_buffer_
.get(), size
, io_callback_
);
387 int DiskCacheBasedQuicServerInfo::DoWrite() {
388 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
389 tracked_objects::ScopedTracker
tracking_profile(
390 FROM_HERE_WITH_EXPLICIT_FUNCTION(
391 "422516 DiskCacheBasedQuicServerInfo::DoWrite"));
393 write_buffer_
= new IOBuffer(new_data_
.size());
394 memcpy(write_buffer_
->data(), new_data_
.data(), new_data_
.size());
395 state_
= WRITE_COMPLETE
;
397 return entry_
->WriteData(0 /* index */,
402 true /* truncate */);
405 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
406 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
407 tracked_objects::ScopedTracker
tracking_profile(
408 FROM_HERE_WITH_EXPLICIT_FUNCTION(
409 "422516 DiskCacheBasedQuicServerInfo::DoCreateOrOpen"));
411 state_
= CREATE_OR_OPEN_COMPLETE
;
416 return backend_
->OpenEntry(key(), &data_shim_
->entry
, io_callback_
);
419 return backend_
->CreateEntry(key(), &data_shim_
->entry
, io_callback_
);
422 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
423 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
424 tracked_objects::ScopedTracker
tracking_profile(
425 FROM_HERE_WITH_EXPLICIT_FUNCTION(
426 "422516 DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone"));
431 // We close the entry because, if we shutdown before ::Persist is called,
432 // then we might leak a cache reference, which causes a DCHECK on shutdown.
437 RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PARSE
);
440 RecordQuicServerInfoFailure(PARSE_NO_DATA_FAILURE
);
442 RecordQuicServerInfoFailure(PARSE_FAILURE
);
445 UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime",
446 base::TimeTicks::Now() - load_start_time_
);
450 int DiskCacheBasedQuicServerInfo::DoSetDone() {
459 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus(
460 QuicServerInfoAPICall call
) {
462 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.NoBackend", call
,
463 QUIC_SERVER_INFO_NUM_OF_API_CALLS
);
464 } else if (backend_
->GetCacheType() == net::MEMORY_CACHE
) {
465 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.MemoryCache", call
,
466 QUIC_SERVER_INFO_NUM_OF_API_CALLS
);
468 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.DiskCache", call
,
469 QUIC_SERVER_INFO_NUM_OF_API_CALLS
);
473 void DiskCacheBasedQuicServerInfo::RecordLastFailure() {
474 if (last_failure_
!= NO_FAILURE
) {
475 UMA_HISTOGRAM_ENUMERATION(
476 "Net.QuicDiskCache.FailureReason.WaitForDataReady",
477 last_failure_
, NUM_OF_FAILURES
);
479 last_failure_
= NO_FAILURE
;
482 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoFailure(
483 FailureReason failure
) {
484 last_failure_
= failure
;
487 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.NoBackend",
488 failure
, NUM_OF_FAILURES
);
489 } else if (backend_
->GetCacheType() == net::MEMORY_CACHE
) {
490 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.MemoryCache",
491 failure
, NUM_OF_FAILURES
);
493 UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache",
494 failure
, NUM_OF_FAILURES
);