1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker // The eviction policy is a very simple pure LRU, so the elements at the end of
6*6777b538SAndroid Build Coastguard Worker // the list are evicted until kCleanUpMargin free space is available. There is
7*6777b538SAndroid Build Coastguard Worker // only one list in use (Rankings::NO_USE), and elements are sent to the front
8*6777b538SAndroid Build Coastguard Worker // of the list whenever they are accessed.
9*6777b538SAndroid Build Coastguard Worker
10*6777b538SAndroid Build Coastguard Worker // The new (in-development) eviction policy adds re-use as a factor to evict
11*6777b538SAndroid Build Coastguard Worker // an entry. The story so far:
12*6777b538SAndroid Build Coastguard Worker
13*6777b538SAndroid Build Coastguard Worker // Entries are linked on separate lists depending on how often they are used.
14*6777b538SAndroid Build Coastguard Worker // When we see an element for the first time, it goes to the NO_USE list; if
15*6777b538SAndroid Build Coastguard Worker // the object is reused later on, we move it to the LOW_USE list, until it is
16*6777b538SAndroid Build Coastguard Worker // used kHighUse times, at which point it is moved to the HIGH_USE list.
17*6777b538SAndroid Build Coastguard Worker // Whenever an element is evicted, we move it to the DELETED list so that if the
18*6777b538SAndroid Build Coastguard Worker // element is accessed again, we remember the fact that it was already stored
19*6777b538SAndroid Build Coastguard Worker // and maybe in the future we don't evict that element.
20*6777b538SAndroid Build Coastguard Worker
21*6777b538SAndroid Build Coastguard Worker // When we have to evict an element, first we try to use the last element from
22*6777b538SAndroid Build Coastguard Worker // the NO_USE list, then we move to the LOW_USE and only then we evict an entry
23*6777b538SAndroid Build Coastguard Worker // from the HIGH_USE. We attempt to keep entries on the cache for at least
24*6777b538SAndroid Build Coastguard Worker // kTargetTime hours (with frequently accessed items stored for longer periods),
25*6777b538SAndroid Build Coastguard Worker // but if we cannot do that, we fall-back to keep each list roughly the same
26*6777b538SAndroid Build Coastguard Worker // size so that we have a chance to see an element again and move it to another
27*6777b538SAndroid Build Coastguard Worker // list.
28*6777b538SAndroid Build Coastguard Worker
29*6777b538SAndroid Build Coastguard Worker #include "net/disk_cache/blockfile/eviction.h"
30*6777b538SAndroid Build Coastguard Worker
31*6777b538SAndroid Build Coastguard Worker #include <stdint.h>
32*6777b538SAndroid Build Coastguard Worker
33*6777b538SAndroid Build Coastguard Worker #include <limits>
34*6777b538SAndroid Build Coastguard Worker
35*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
36*6777b538SAndroid Build Coastguard Worker #include "base/compiler_specific.h"
37*6777b538SAndroid Build Coastguard Worker #include "base/functional/bind.h"
38*6777b538SAndroid Build Coastguard Worker #include "base/location.h"
39*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_macros.h"
40*6777b538SAndroid Build Coastguard Worker #include "base/notreached.h"
41*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
42*6777b538SAndroid Build Coastguard Worker #include "base/task/single_thread_task_runner.h"
43*6777b538SAndroid Build Coastguard Worker #include "base/time/time.h"
44*6777b538SAndroid Build Coastguard Worker #include "net/base/tracing.h"
45*6777b538SAndroid Build Coastguard Worker #include "net/disk_cache/blockfile/backend_impl.h"
46*6777b538SAndroid Build Coastguard Worker #include "net/disk_cache/blockfile/disk_format.h"
47*6777b538SAndroid Build Coastguard Worker #include "net/disk_cache/blockfile/entry_impl.h"
48*6777b538SAndroid Build Coastguard Worker #include "net/disk_cache/blockfile/experiments.h"
49*6777b538SAndroid Build Coastguard Worker
50*6777b538SAndroid Build Coastguard Worker using base::Time;
51*6777b538SAndroid Build Coastguard Worker using base::TimeTicks;
52*6777b538SAndroid Build Coastguard Worker
53*6777b538SAndroid Build Coastguard Worker namespace {
54*6777b538SAndroid Build Coastguard Worker
55*6777b538SAndroid Build Coastguard Worker const int kCleanUpMargin = 1024 * 1024;
56*6777b538SAndroid Build Coastguard Worker const int kHighUse = 10; // Reuse count to be on the HIGH_USE list.
57*6777b538SAndroid Build Coastguard Worker const int kTargetTime = 24 * 7; // Time to be evicted (hours since last use).
58*6777b538SAndroid Build Coastguard Worker const int kMaxDelayedTrims = 60;
59*6777b538SAndroid Build Coastguard Worker
LowWaterAdjust(int high_water)60*6777b538SAndroid Build Coastguard Worker int LowWaterAdjust(int high_water) {
61*6777b538SAndroid Build Coastguard Worker if (high_water < kCleanUpMargin)
62*6777b538SAndroid Build Coastguard Worker return 0;
63*6777b538SAndroid Build Coastguard Worker
64*6777b538SAndroid Build Coastguard Worker return high_water - kCleanUpMargin;
65*6777b538SAndroid Build Coastguard Worker }
66*6777b538SAndroid Build Coastguard Worker
FallingBehind(int current_size,int max_size)67*6777b538SAndroid Build Coastguard Worker bool FallingBehind(int current_size, int max_size) {
68*6777b538SAndroid Build Coastguard Worker return current_size > max_size - kCleanUpMargin * 20;
69*6777b538SAndroid Build Coastguard Worker }
70*6777b538SAndroid Build Coastguard Worker
71*6777b538SAndroid Build Coastguard Worker } // namespace
72*6777b538SAndroid Build Coastguard Worker
73*6777b538SAndroid Build Coastguard Worker namespace disk_cache {
74*6777b538SAndroid Build Coastguard Worker
75*6777b538SAndroid Build Coastguard Worker // The real initialization happens during Init(), init_ is the only member that
76*6777b538SAndroid Build Coastguard Worker // has to be initialized here.
77*6777b538SAndroid Build Coastguard Worker Eviction::Eviction() = default;
78*6777b538SAndroid Build Coastguard Worker
79*6777b538SAndroid Build Coastguard Worker Eviction::~Eviction() = default;
80*6777b538SAndroid Build Coastguard Worker
Init(BackendImpl * backend)81*6777b538SAndroid Build Coastguard Worker void Eviction::Init(BackendImpl* backend) {
82*6777b538SAndroid Build Coastguard Worker // We grab a bunch of info from the backend to make the code a little cleaner
83*6777b538SAndroid Build Coastguard Worker // when we're actually doing work.
84*6777b538SAndroid Build Coastguard Worker backend_ = backend;
85*6777b538SAndroid Build Coastguard Worker rankings_ = &backend->rankings_;
86*6777b538SAndroid Build Coastguard Worker header_ = &backend_->data_->header;
87*6777b538SAndroid Build Coastguard Worker max_size_ = LowWaterAdjust(backend_->max_size_);
88*6777b538SAndroid Build Coastguard Worker index_size_ = backend->mask_ + 1;
89*6777b538SAndroid Build Coastguard Worker new_eviction_ = backend->new_eviction_;
90*6777b538SAndroid Build Coastguard Worker first_trim_ = true;
91*6777b538SAndroid Build Coastguard Worker trimming_ = false;
92*6777b538SAndroid Build Coastguard Worker delay_trim_ = false;
93*6777b538SAndroid Build Coastguard Worker trim_delays_ = 0;
94*6777b538SAndroid Build Coastguard Worker init_ = true;
95*6777b538SAndroid Build Coastguard Worker test_mode_ = false;
96*6777b538SAndroid Build Coastguard Worker }
97*6777b538SAndroid Build Coastguard Worker
Stop()98*6777b538SAndroid Build Coastguard Worker void Eviction::Stop() {
99*6777b538SAndroid Build Coastguard Worker // It is possible for the backend initialization to fail, in which case this
100*6777b538SAndroid Build Coastguard Worker // object was never initialized... and there is nothing to do.
101*6777b538SAndroid Build Coastguard Worker if (!init_)
102*6777b538SAndroid Build Coastguard Worker return;
103*6777b538SAndroid Build Coastguard Worker
104*6777b538SAndroid Build Coastguard Worker // We want to stop further evictions, so let's pretend that we are busy from
105*6777b538SAndroid Build Coastguard Worker // this point on.
106*6777b538SAndroid Build Coastguard Worker DCHECK(!trimming_);
107*6777b538SAndroid Build Coastguard Worker trimming_ = true;
108*6777b538SAndroid Build Coastguard Worker ptr_factory_.InvalidateWeakPtrs();
109*6777b538SAndroid Build Coastguard Worker }
110*6777b538SAndroid Build Coastguard Worker
TrimCache(bool empty)111*6777b538SAndroid Build Coastguard Worker void Eviction::TrimCache(bool empty) {
112*6777b538SAndroid Build Coastguard Worker TRACE_EVENT0("disk_cache", "Eviction::TrimCache");
113*6777b538SAndroid Build Coastguard Worker if (backend_->disabled_ || trimming_)
114*6777b538SAndroid Build Coastguard Worker return;
115*6777b538SAndroid Build Coastguard Worker
116*6777b538SAndroid Build Coastguard Worker if (!empty && !ShouldTrim())
117*6777b538SAndroid Build Coastguard Worker return PostDelayedTrim();
118*6777b538SAndroid Build Coastguard Worker
119*6777b538SAndroid Build Coastguard Worker if (new_eviction_)
120*6777b538SAndroid Build Coastguard Worker return TrimCacheV2(empty);
121*6777b538SAndroid Build Coastguard Worker
122*6777b538SAndroid Build Coastguard Worker trimming_ = true;
123*6777b538SAndroid Build Coastguard Worker TimeTicks start = TimeTicks::Now();
124*6777b538SAndroid Build Coastguard Worker Rankings::ScopedRankingsBlock node(rankings_);
125*6777b538SAndroid Build Coastguard Worker Rankings::ScopedRankingsBlock next(
126*6777b538SAndroid Build Coastguard Worker rankings_, rankings_->GetPrev(node.get(), Rankings::NO_USE));
127*6777b538SAndroid Build Coastguard Worker int deleted_entries = 0;
128*6777b538SAndroid Build Coastguard Worker int target_size = empty ? 0 : max_size_;
129*6777b538SAndroid Build Coastguard Worker while ((header_->num_bytes > target_size || test_mode_) && next.get()) {
130*6777b538SAndroid Build Coastguard Worker // The iterator could be invalidated within EvictEntry().
131*6777b538SAndroid Build Coastguard Worker if (!next->HasData())
132*6777b538SAndroid Build Coastguard Worker break;
133*6777b538SAndroid Build Coastguard Worker node.reset(next.release());
134*6777b538SAndroid Build Coastguard Worker next.reset(rankings_->GetPrev(node.get(), Rankings::NO_USE));
135*6777b538SAndroid Build Coastguard Worker if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) {
136*6777b538SAndroid Build Coastguard Worker // This entry is not being used by anybody.
137*6777b538SAndroid Build Coastguard Worker // Do NOT use node as an iterator after this point.
138*6777b538SAndroid Build Coastguard Worker rankings_->TrackRankingsBlock(node.get(), false);
139*6777b538SAndroid Build Coastguard Worker if (EvictEntry(node.get(), empty, Rankings::NO_USE) && !test_mode_)
140*6777b538SAndroid Build Coastguard Worker deleted_entries++;
141*6777b538SAndroid Build Coastguard Worker
142*6777b538SAndroid Build Coastguard Worker if (!empty && test_mode_)
143*6777b538SAndroid Build Coastguard Worker break;
144*6777b538SAndroid Build Coastguard Worker }
145*6777b538SAndroid Build Coastguard Worker if (!empty && (deleted_entries > 20 ||
146*6777b538SAndroid Build Coastguard Worker (TimeTicks::Now() - start).InMilliseconds() > 20)) {
147*6777b538SAndroid Build Coastguard Worker base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
148*6777b538SAndroid Build Coastguard Worker FROM_HERE, base::BindOnce(&Eviction::TrimCache,
149*6777b538SAndroid Build Coastguard Worker ptr_factory_.GetWeakPtr(), false));
150*6777b538SAndroid Build Coastguard Worker break;
151*6777b538SAndroid Build Coastguard Worker }
152*6777b538SAndroid Build Coastguard Worker }
153*6777b538SAndroid Build Coastguard Worker
154*6777b538SAndroid Build Coastguard Worker trimming_ = false;
155*6777b538SAndroid Build Coastguard Worker return;
156*6777b538SAndroid Build Coastguard Worker }
157*6777b538SAndroid Build Coastguard Worker
UpdateRank(EntryImpl * entry,bool modified)158*6777b538SAndroid Build Coastguard Worker void Eviction::UpdateRank(EntryImpl* entry, bool modified) {
159*6777b538SAndroid Build Coastguard Worker if (new_eviction_)
160*6777b538SAndroid Build Coastguard Worker return UpdateRankV2(entry, modified);
161*6777b538SAndroid Build Coastguard Worker
162*6777b538SAndroid Build Coastguard Worker rankings_->UpdateRank(entry->rankings(), modified, GetListForEntry(entry));
163*6777b538SAndroid Build Coastguard Worker }
164*6777b538SAndroid Build Coastguard Worker
OnOpenEntry(EntryImpl * entry)165*6777b538SAndroid Build Coastguard Worker void Eviction::OnOpenEntry(EntryImpl* entry) {
166*6777b538SAndroid Build Coastguard Worker if (new_eviction_)
167*6777b538SAndroid Build Coastguard Worker return OnOpenEntryV2(entry);
168*6777b538SAndroid Build Coastguard Worker }
169*6777b538SAndroid Build Coastguard Worker
OnCreateEntry(EntryImpl * entry)170*6777b538SAndroid Build Coastguard Worker void Eviction::OnCreateEntry(EntryImpl* entry) {
171*6777b538SAndroid Build Coastguard Worker if (new_eviction_)
172*6777b538SAndroid Build Coastguard Worker return OnCreateEntryV2(entry);
173*6777b538SAndroid Build Coastguard Worker
174*6777b538SAndroid Build Coastguard Worker rankings_->Insert(entry->rankings(), true, GetListForEntry(entry));
175*6777b538SAndroid Build Coastguard Worker }
176*6777b538SAndroid Build Coastguard Worker
OnDoomEntry(EntryImpl * entry)177*6777b538SAndroid Build Coastguard Worker void Eviction::OnDoomEntry(EntryImpl* entry) {
178*6777b538SAndroid Build Coastguard Worker if (new_eviction_)
179*6777b538SAndroid Build Coastguard Worker return OnDoomEntryV2(entry);
180*6777b538SAndroid Build Coastguard Worker
181*6777b538SAndroid Build Coastguard Worker if (entry->LeaveRankingsBehind())
182*6777b538SAndroid Build Coastguard Worker return;
183*6777b538SAndroid Build Coastguard Worker
184*6777b538SAndroid Build Coastguard Worker rankings_->Remove(entry->rankings(), GetListForEntry(entry), true);
185*6777b538SAndroid Build Coastguard Worker }
186*6777b538SAndroid Build Coastguard Worker
OnDestroyEntry(EntryImpl * entry)187*6777b538SAndroid Build Coastguard Worker void Eviction::OnDestroyEntry(EntryImpl* entry) {
188*6777b538SAndroid Build Coastguard Worker if (new_eviction_)
189*6777b538SAndroid Build Coastguard Worker return OnDestroyEntryV2(entry);
190*6777b538SAndroid Build Coastguard Worker }
191*6777b538SAndroid Build Coastguard Worker
SetTestMode()192*6777b538SAndroid Build Coastguard Worker void Eviction::SetTestMode() {
193*6777b538SAndroid Build Coastguard Worker test_mode_ = true;
194*6777b538SAndroid Build Coastguard Worker }
195*6777b538SAndroid Build Coastguard Worker
TrimDeletedList(bool empty)196*6777b538SAndroid Build Coastguard Worker void Eviction::TrimDeletedList(bool empty) {
197*6777b538SAndroid Build Coastguard Worker TRACE_EVENT0("disk_cache", "Eviction::TrimDeletedList");
198*6777b538SAndroid Build Coastguard Worker
199*6777b538SAndroid Build Coastguard Worker DCHECK(test_mode_ && new_eviction_);
200*6777b538SAndroid Build Coastguard Worker TrimDeleted(empty);
201*6777b538SAndroid Build Coastguard Worker }
202*6777b538SAndroid Build Coastguard Worker
PostDelayedTrim()203*6777b538SAndroid Build Coastguard Worker void Eviction::PostDelayedTrim() {
204*6777b538SAndroid Build Coastguard Worker // Prevent posting multiple tasks.
205*6777b538SAndroid Build Coastguard Worker if (delay_trim_)
206*6777b538SAndroid Build Coastguard Worker return;
207*6777b538SAndroid Build Coastguard Worker delay_trim_ = true;
208*6777b538SAndroid Build Coastguard Worker trim_delays_++;
209*6777b538SAndroid Build Coastguard Worker base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
210*6777b538SAndroid Build Coastguard Worker FROM_HERE,
211*6777b538SAndroid Build Coastguard Worker base::BindOnce(&Eviction::DelayedTrim, ptr_factory_.GetWeakPtr()),
212*6777b538SAndroid Build Coastguard Worker base::Milliseconds(1000));
213*6777b538SAndroid Build Coastguard Worker }
214*6777b538SAndroid Build Coastguard Worker
DelayedTrim()215*6777b538SAndroid Build Coastguard Worker void Eviction::DelayedTrim() {
216*6777b538SAndroid Build Coastguard Worker delay_trim_ = false;
217*6777b538SAndroid Build Coastguard Worker if (trim_delays_ < kMaxDelayedTrims && backend_->IsLoaded())
218*6777b538SAndroid Build Coastguard Worker return PostDelayedTrim();
219*6777b538SAndroid Build Coastguard Worker
220*6777b538SAndroid Build Coastguard Worker TrimCache(false);
221*6777b538SAndroid Build Coastguard Worker }
222*6777b538SAndroid Build Coastguard Worker
ShouldTrim()223*6777b538SAndroid Build Coastguard Worker bool Eviction::ShouldTrim() {
224*6777b538SAndroid Build Coastguard Worker if (!FallingBehind(header_->num_bytes, max_size_) &&
225*6777b538SAndroid Build Coastguard Worker trim_delays_ < kMaxDelayedTrims && backend_->IsLoaded()) {
226*6777b538SAndroid Build Coastguard Worker return false;
227*6777b538SAndroid Build Coastguard Worker }
228*6777b538SAndroid Build Coastguard Worker
229*6777b538SAndroid Build Coastguard Worker trim_delays_ = 0;
230*6777b538SAndroid Build Coastguard Worker return true;
231*6777b538SAndroid Build Coastguard Worker }
232*6777b538SAndroid Build Coastguard Worker
ShouldTrimDeleted()233*6777b538SAndroid Build Coastguard Worker bool Eviction::ShouldTrimDeleted() {
234*6777b538SAndroid Build Coastguard Worker int index_load = header_->num_entries * 100 / index_size_;
235*6777b538SAndroid Build Coastguard Worker
236*6777b538SAndroid Build Coastguard Worker // If the index is not loaded, the deleted list will tend to double the size
237*6777b538SAndroid Build Coastguard Worker // of the other lists 3 lists (40% of the total). Otherwise, all lists will be
238*6777b538SAndroid Build Coastguard Worker // about the same size.
239*6777b538SAndroid Build Coastguard Worker int max_length = (index_load < 25) ? header_->num_entries * 2 / 5 :
240*6777b538SAndroid Build Coastguard Worker header_->num_entries / 4;
241*6777b538SAndroid Build Coastguard Worker return (!test_mode_ && header_->lru.sizes[Rankings::DELETED] > max_length);
242*6777b538SAndroid Build Coastguard Worker }
243*6777b538SAndroid Build Coastguard Worker
ReportTrimTimes(EntryImpl * entry)244*6777b538SAndroid Build Coastguard Worker void Eviction::ReportTrimTimes(EntryImpl* entry) {
245*6777b538SAndroid Build Coastguard Worker if (first_trim_) {
246*6777b538SAndroid Build Coastguard Worker first_trim_ = false;
247*6777b538SAndroid Build Coastguard Worker
248*6777b538SAndroid Build Coastguard Worker if (header_->lru.filled)
249*6777b538SAndroid Build Coastguard Worker return;
250*6777b538SAndroid Build Coastguard Worker
251*6777b538SAndroid Build Coastguard Worker header_->lru.filled = 1;
252*6777b538SAndroid Build Coastguard Worker
253*6777b538SAndroid Build Coastguard Worker if (header_->create_time) {
254*6777b538SAndroid Build Coastguard Worker // This is the first entry that we have to evict, generate some noise.
255*6777b538SAndroid Build Coastguard Worker backend_->FirstEviction();
256*6777b538SAndroid Build Coastguard Worker } else {
257*6777b538SAndroid Build Coastguard Worker // This is an old file, but we may want more reports from this user so
258*6777b538SAndroid Build Coastguard Worker // lets save some create_time. Conversion cannot fail here.
259*6777b538SAndroid Build Coastguard Worker const base::Time time_2009_3_1 =
260*6777b538SAndroid Build Coastguard Worker base::Time::FromInternalValue(12985574400000000);
261*6777b538SAndroid Build Coastguard Worker header_->create_time = time_2009_3_1.ToInternalValue();
262*6777b538SAndroid Build Coastguard Worker }
263*6777b538SAndroid Build Coastguard Worker }
264*6777b538SAndroid Build Coastguard Worker }
265*6777b538SAndroid Build Coastguard Worker
GetListForEntry(EntryImpl * entry)266*6777b538SAndroid Build Coastguard Worker Rankings::List Eviction::GetListForEntry(EntryImpl* entry) {
267*6777b538SAndroid Build Coastguard Worker return Rankings::NO_USE;
268*6777b538SAndroid Build Coastguard Worker }
269*6777b538SAndroid Build Coastguard Worker
EvictEntry(CacheRankingsBlock * node,bool empty,Rankings::List list)270*6777b538SAndroid Build Coastguard Worker bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty,
271*6777b538SAndroid Build Coastguard Worker Rankings::List list) {
272*6777b538SAndroid Build Coastguard Worker scoped_refptr<EntryImpl> entry = backend_->GetEnumeratedEntry(node, list);
273*6777b538SAndroid Build Coastguard Worker if (!entry)
274*6777b538SAndroid Build Coastguard Worker return false;
275*6777b538SAndroid Build Coastguard Worker
276*6777b538SAndroid Build Coastguard Worker ReportTrimTimes(entry.get());
277*6777b538SAndroid Build Coastguard Worker if (empty || !new_eviction_) {
278*6777b538SAndroid Build Coastguard Worker entry->DoomImpl();
279*6777b538SAndroid Build Coastguard Worker } else {
280*6777b538SAndroid Build Coastguard Worker entry->DeleteEntryData(false);
281*6777b538SAndroid Build Coastguard Worker EntryStore* info = entry->entry()->Data();
282*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(ENTRY_NORMAL, info->state);
283*6777b538SAndroid Build Coastguard Worker
284*6777b538SAndroid Build Coastguard Worker rankings_->Remove(entry->rankings(), GetListForEntryV2(entry.get()), true);
285*6777b538SAndroid Build Coastguard Worker info->state = ENTRY_EVICTED;
286*6777b538SAndroid Build Coastguard Worker entry->entry()->Store();
287*6777b538SAndroid Build Coastguard Worker rankings_->Insert(entry->rankings(), true, Rankings::DELETED);
288*6777b538SAndroid Build Coastguard Worker }
289*6777b538SAndroid Build Coastguard Worker if (!empty)
290*6777b538SAndroid Build Coastguard Worker backend_->OnEvent(Stats::TRIM_ENTRY);
291*6777b538SAndroid Build Coastguard Worker
292*6777b538SAndroid Build Coastguard Worker return true;
293*6777b538SAndroid Build Coastguard Worker }
294*6777b538SAndroid Build Coastguard Worker
295*6777b538SAndroid Build Coastguard Worker // -----------------------------------------------------------------------
296*6777b538SAndroid Build Coastguard Worker
TrimCacheV2(bool empty)297*6777b538SAndroid Build Coastguard Worker void Eviction::TrimCacheV2(bool empty) {
298*6777b538SAndroid Build Coastguard Worker TRACE_EVENT0("disk_cache", "Eviction::TrimCacheV2");
299*6777b538SAndroid Build Coastguard Worker
300*6777b538SAndroid Build Coastguard Worker trimming_ = true;
301*6777b538SAndroid Build Coastguard Worker TimeTicks start = TimeTicks::Now();
302*6777b538SAndroid Build Coastguard Worker
303*6777b538SAndroid Build Coastguard Worker const int kListsToSearch = 3;
304*6777b538SAndroid Build Coastguard Worker Rankings::ScopedRankingsBlock next[kListsToSearch];
305*6777b538SAndroid Build Coastguard Worker int list = Rankings::LAST_ELEMENT;
306*6777b538SAndroid Build Coastguard Worker
307*6777b538SAndroid Build Coastguard Worker // Get a node from each list.
308*6777b538SAndroid Build Coastguard Worker bool done = false;
309*6777b538SAndroid Build Coastguard Worker for (int i = 0; i < kListsToSearch; i++) {
310*6777b538SAndroid Build Coastguard Worker next[i].set_rankings(rankings_);
311*6777b538SAndroid Build Coastguard Worker if (done)
312*6777b538SAndroid Build Coastguard Worker continue;
313*6777b538SAndroid Build Coastguard Worker next[i].reset(rankings_->GetPrev(nullptr, static_cast<Rankings::List>(i)));
314*6777b538SAndroid Build Coastguard Worker if (!empty && NodeIsOldEnough(next[i].get(), i)) {
315*6777b538SAndroid Build Coastguard Worker list = static_cast<Rankings::List>(i);
316*6777b538SAndroid Build Coastguard Worker done = true;
317*6777b538SAndroid Build Coastguard Worker }
318*6777b538SAndroid Build Coastguard Worker }
319*6777b538SAndroid Build Coastguard Worker
320*6777b538SAndroid Build Coastguard Worker // If we are not meeting the time targets lets move on to list length.
321*6777b538SAndroid Build Coastguard Worker if (!empty && Rankings::LAST_ELEMENT == list)
322*6777b538SAndroid Build Coastguard Worker list = SelectListByLength(next);
323*6777b538SAndroid Build Coastguard Worker
324*6777b538SAndroid Build Coastguard Worker if (empty)
325*6777b538SAndroid Build Coastguard Worker list = 0;
326*6777b538SAndroid Build Coastguard Worker
327*6777b538SAndroid Build Coastguard Worker Rankings::ScopedRankingsBlock node(rankings_);
328*6777b538SAndroid Build Coastguard Worker int deleted_entries = 0;
329*6777b538SAndroid Build Coastguard Worker int target_size = empty ? 0 : max_size_;
330*6777b538SAndroid Build Coastguard Worker
331*6777b538SAndroid Build Coastguard Worker for (; list < kListsToSearch; list++) {
332*6777b538SAndroid Build Coastguard Worker while ((header_->num_bytes > target_size || test_mode_) &&
333*6777b538SAndroid Build Coastguard Worker next[list].get()) {
334*6777b538SAndroid Build Coastguard Worker // The iterator could be invalidated within EvictEntry().
335*6777b538SAndroid Build Coastguard Worker if (!next[list]->HasData())
336*6777b538SAndroid Build Coastguard Worker break;
337*6777b538SAndroid Build Coastguard Worker node.reset(next[list].release());
338*6777b538SAndroid Build Coastguard Worker next[list].reset(rankings_->GetPrev(node.get(),
339*6777b538SAndroid Build Coastguard Worker static_cast<Rankings::List>(list)));
340*6777b538SAndroid Build Coastguard Worker if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) {
341*6777b538SAndroid Build Coastguard Worker // This entry is not being used by anybody.
342*6777b538SAndroid Build Coastguard Worker // Do NOT use node as an iterator after this point.
343*6777b538SAndroid Build Coastguard Worker rankings_->TrackRankingsBlock(node.get(), false);
344*6777b538SAndroid Build Coastguard Worker if (EvictEntry(node.get(), empty, static_cast<Rankings::List>(list)))
345*6777b538SAndroid Build Coastguard Worker deleted_entries++;
346*6777b538SAndroid Build Coastguard Worker
347*6777b538SAndroid Build Coastguard Worker if (!empty && test_mode_)
348*6777b538SAndroid Build Coastguard Worker break;
349*6777b538SAndroid Build Coastguard Worker }
350*6777b538SAndroid Build Coastguard Worker if (!empty && (deleted_entries > 20 ||
351*6777b538SAndroid Build Coastguard Worker (TimeTicks::Now() - start).InMilliseconds() > 20)) {
352*6777b538SAndroid Build Coastguard Worker base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
353*6777b538SAndroid Build Coastguard Worker FROM_HERE, base::BindOnce(&Eviction::TrimCache,
354*6777b538SAndroid Build Coastguard Worker ptr_factory_.GetWeakPtr(), false));
355*6777b538SAndroid Build Coastguard Worker break;
356*6777b538SAndroid Build Coastguard Worker }
357*6777b538SAndroid Build Coastguard Worker }
358*6777b538SAndroid Build Coastguard Worker if (!empty)
359*6777b538SAndroid Build Coastguard Worker list = kListsToSearch;
360*6777b538SAndroid Build Coastguard Worker }
361*6777b538SAndroid Build Coastguard Worker
362*6777b538SAndroid Build Coastguard Worker if (empty) {
363*6777b538SAndroid Build Coastguard Worker TrimDeleted(true);
364*6777b538SAndroid Build Coastguard Worker } else if (ShouldTrimDeleted()) {
365*6777b538SAndroid Build Coastguard Worker base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
366*6777b538SAndroid Build Coastguard Worker FROM_HERE, base::BindOnce(&Eviction::TrimDeleted,
367*6777b538SAndroid Build Coastguard Worker ptr_factory_.GetWeakPtr(), empty));
368*6777b538SAndroid Build Coastguard Worker }
369*6777b538SAndroid Build Coastguard Worker
370*6777b538SAndroid Build Coastguard Worker trimming_ = false;
371*6777b538SAndroid Build Coastguard Worker return;
372*6777b538SAndroid Build Coastguard Worker }
373*6777b538SAndroid Build Coastguard Worker
UpdateRankV2(EntryImpl * entry,bool modified)374*6777b538SAndroid Build Coastguard Worker void Eviction::UpdateRankV2(EntryImpl* entry, bool modified) {
375*6777b538SAndroid Build Coastguard Worker rankings_->UpdateRank(entry->rankings(), modified, GetListForEntryV2(entry));
376*6777b538SAndroid Build Coastguard Worker }
377*6777b538SAndroid Build Coastguard Worker
OnOpenEntryV2(EntryImpl * entry)378*6777b538SAndroid Build Coastguard Worker void Eviction::OnOpenEntryV2(EntryImpl* entry) {
379*6777b538SAndroid Build Coastguard Worker EntryStore* info = entry->entry()->Data();
380*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(ENTRY_NORMAL, info->state);
381*6777b538SAndroid Build Coastguard Worker
382*6777b538SAndroid Build Coastguard Worker if (info->reuse_count < std::numeric_limits<int32_t>::max()) {
383*6777b538SAndroid Build Coastguard Worker info->reuse_count++;
384*6777b538SAndroid Build Coastguard Worker entry->entry()->set_modified();
385*6777b538SAndroid Build Coastguard Worker
386*6777b538SAndroid Build Coastguard Worker // We may need to move this to a new list.
387*6777b538SAndroid Build Coastguard Worker if (1 == info->reuse_count) {
388*6777b538SAndroid Build Coastguard Worker rankings_->Remove(entry->rankings(), Rankings::NO_USE, true);
389*6777b538SAndroid Build Coastguard Worker rankings_->Insert(entry->rankings(), false, Rankings::LOW_USE);
390*6777b538SAndroid Build Coastguard Worker entry->entry()->Store();
391*6777b538SAndroid Build Coastguard Worker } else if (kHighUse == info->reuse_count) {
392*6777b538SAndroid Build Coastguard Worker rankings_->Remove(entry->rankings(), Rankings::LOW_USE, true);
393*6777b538SAndroid Build Coastguard Worker rankings_->Insert(entry->rankings(), false, Rankings::HIGH_USE);
394*6777b538SAndroid Build Coastguard Worker entry->entry()->Store();
395*6777b538SAndroid Build Coastguard Worker }
396*6777b538SAndroid Build Coastguard Worker }
397*6777b538SAndroid Build Coastguard Worker }
398*6777b538SAndroid Build Coastguard Worker
OnCreateEntryV2(EntryImpl * entry)399*6777b538SAndroid Build Coastguard Worker void Eviction::OnCreateEntryV2(EntryImpl* entry) {
400*6777b538SAndroid Build Coastguard Worker EntryStore* info = entry->entry()->Data();
401*6777b538SAndroid Build Coastguard Worker switch (info->state) {
402*6777b538SAndroid Build Coastguard Worker case ENTRY_NORMAL: {
403*6777b538SAndroid Build Coastguard Worker DCHECK(!info->reuse_count);
404*6777b538SAndroid Build Coastguard Worker DCHECK(!info->refetch_count);
405*6777b538SAndroid Build Coastguard Worker break;
406*6777b538SAndroid Build Coastguard Worker };
407*6777b538SAndroid Build Coastguard Worker case ENTRY_EVICTED: {
408*6777b538SAndroid Build Coastguard Worker if (info->refetch_count < std::numeric_limits<int32_t>::max())
409*6777b538SAndroid Build Coastguard Worker info->refetch_count++;
410*6777b538SAndroid Build Coastguard Worker
411*6777b538SAndroid Build Coastguard Worker if (info->refetch_count > kHighUse && info->reuse_count < kHighUse) {
412*6777b538SAndroid Build Coastguard Worker info->reuse_count = kHighUse;
413*6777b538SAndroid Build Coastguard Worker } else {
414*6777b538SAndroid Build Coastguard Worker info->reuse_count++;
415*6777b538SAndroid Build Coastguard Worker }
416*6777b538SAndroid Build Coastguard Worker info->state = ENTRY_NORMAL;
417*6777b538SAndroid Build Coastguard Worker entry->entry()->Store();
418*6777b538SAndroid Build Coastguard Worker rankings_->Remove(entry->rankings(), Rankings::DELETED, true);
419*6777b538SAndroid Build Coastguard Worker break;
420*6777b538SAndroid Build Coastguard Worker };
421*6777b538SAndroid Build Coastguard Worker default:
422*6777b538SAndroid Build Coastguard Worker NOTREACHED();
423*6777b538SAndroid Build Coastguard Worker }
424*6777b538SAndroid Build Coastguard Worker
425*6777b538SAndroid Build Coastguard Worker rankings_->Insert(entry->rankings(), true, GetListForEntryV2(entry));
426*6777b538SAndroid Build Coastguard Worker }
427*6777b538SAndroid Build Coastguard Worker
OnDoomEntryV2(EntryImpl * entry)428*6777b538SAndroid Build Coastguard Worker void Eviction::OnDoomEntryV2(EntryImpl* entry) {
429*6777b538SAndroid Build Coastguard Worker EntryStore* info = entry->entry()->Data();
430*6777b538SAndroid Build Coastguard Worker if (ENTRY_NORMAL != info->state)
431*6777b538SAndroid Build Coastguard Worker return;
432*6777b538SAndroid Build Coastguard Worker
433*6777b538SAndroid Build Coastguard Worker if (entry->LeaveRankingsBehind()) {
434*6777b538SAndroid Build Coastguard Worker info->state = ENTRY_DOOMED;
435*6777b538SAndroid Build Coastguard Worker entry->entry()->Store();
436*6777b538SAndroid Build Coastguard Worker return;
437*6777b538SAndroid Build Coastguard Worker }
438*6777b538SAndroid Build Coastguard Worker
439*6777b538SAndroid Build Coastguard Worker rankings_->Remove(entry->rankings(), GetListForEntryV2(entry), true);
440*6777b538SAndroid Build Coastguard Worker
441*6777b538SAndroid Build Coastguard Worker info->state = ENTRY_DOOMED;
442*6777b538SAndroid Build Coastguard Worker entry->entry()->Store();
443*6777b538SAndroid Build Coastguard Worker rankings_->Insert(entry->rankings(), true, Rankings::DELETED);
444*6777b538SAndroid Build Coastguard Worker }
445*6777b538SAndroid Build Coastguard Worker
OnDestroyEntryV2(EntryImpl * entry)446*6777b538SAndroid Build Coastguard Worker void Eviction::OnDestroyEntryV2(EntryImpl* entry) {
447*6777b538SAndroid Build Coastguard Worker if (entry->LeaveRankingsBehind())
448*6777b538SAndroid Build Coastguard Worker return;
449*6777b538SAndroid Build Coastguard Worker
450*6777b538SAndroid Build Coastguard Worker rankings_->Remove(entry->rankings(), Rankings::DELETED, true);
451*6777b538SAndroid Build Coastguard Worker }
452*6777b538SAndroid Build Coastguard Worker
GetListForEntryV2(EntryImpl * entry)453*6777b538SAndroid Build Coastguard Worker Rankings::List Eviction::GetListForEntryV2(EntryImpl* entry) {
454*6777b538SAndroid Build Coastguard Worker EntryStore* info = entry->entry()->Data();
455*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(ENTRY_NORMAL, info->state);
456*6777b538SAndroid Build Coastguard Worker
457*6777b538SAndroid Build Coastguard Worker if (!info->reuse_count)
458*6777b538SAndroid Build Coastguard Worker return Rankings::NO_USE;
459*6777b538SAndroid Build Coastguard Worker
460*6777b538SAndroid Build Coastguard Worker if (info->reuse_count < kHighUse)
461*6777b538SAndroid Build Coastguard Worker return Rankings::LOW_USE;
462*6777b538SAndroid Build Coastguard Worker
463*6777b538SAndroid Build Coastguard Worker return Rankings::HIGH_USE;
464*6777b538SAndroid Build Coastguard Worker }
465*6777b538SAndroid Build Coastguard Worker
466*6777b538SAndroid Build Coastguard Worker // This is a minimal implementation that just discards the oldest nodes.
467*6777b538SAndroid Build Coastguard Worker // TODO(rvargas): Do something better here.
TrimDeleted(bool empty)468*6777b538SAndroid Build Coastguard Worker void Eviction::TrimDeleted(bool empty) {
469*6777b538SAndroid Build Coastguard Worker TRACE_EVENT0("disk_cache", "Eviction::TrimDeleted");
470*6777b538SAndroid Build Coastguard Worker
471*6777b538SAndroid Build Coastguard Worker if (backend_->disabled_)
472*6777b538SAndroid Build Coastguard Worker return;
473*6777b538SAndroid Build Coastguard Worker
474*6777b538SAndroid Build Coastguard Worker TimeTicks start = TimeTicks::Now();
475*6777b538SAndroid Build Coastguard Worker Rankings::ScopedRankingsBlock node(rankings_);
476*6777b538SAndroid Build Coastguard Worker Rankings::ScopedRankingsBlock next(
477*6777b538SAndroid Build Coastguard Worker rankings_, rankings_->GetPrev(node.get(), Rankings::DELETED));
478*6777b538SAndroid Build Coastguard Worker int deleted_entries = 0;
479*6777b538SAndroid Build Coastguard Worker while (next.get() &&
480*6777b538SAndroid Build Coastguard Worker (empty || (deleted_entries < 20 &&
481*6777b538SAndroid Build Coastguard Worker (TimeTicks::Now() - start).InMilliseconds() < 20))) {
482*6777b538SAndroid Build Coastguard Worker node.reset(next.release());
483*6777b538SAndroid Build Coastguard Worker next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED));
484*6777b538SAndroid Build Coastguard Worker if (RemoveDeletedNode(node.get()))
485*6777b538SAndroid Build Coastguard Worker deleted_entries++;
486*6777b538SAndroid Build Coastguard Worker if (test_mode_)
487*6777b538SAndroid Build Coastguard Worker break;
488*6777b538SAndroid Build Coastguard Worker }
489*6777b538SAndroid Build Coastguard Worker
490*6777b538SAndroid Build Coastguard Worker if (deleted_entries && !empty && ShouldTrimDeleted()) {
491*6777b538SAndroid Build Coastguard Worker base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
492*6777b538SAndroid Build Coastguard Worker FROM_HERE, base::BindOnce(&Eviction::TrimDeleted,
493*6777b538SAndroid Build Coastguard Worker ptr_factory_.GetWeakPtr(), false));
494*6777b538SAndroid Build Coastguard Worker }
495*6777b538SAndroid Build Coastguard Worker
496*6777b538SAndroid Build Coastguard Worker return;
497*6777b538SAndroid Build Coastguard Worker }
498*6777b538SAndroid Build Coastguard Worker
RemoveDeletedNode(CacheRankingsBlock * node)499*6777b538SAndroid Build Coastguard Worker bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) {
500*6777b538SAndroid Build Coastguard Worker scoped_refptr<EntryImpl> entry =
501*6777b538SAndroid Build Coastguard Worker backend_->GetEnumeratedEntry(node, Rankings::DELETED);
502*6777b538SAndroid Build Coastguard Worker if (!entry)
503*6777b538SAndroid Build Coastguard Worker return false;
504*6777b538SAndroid Build Coastguard Worker
505*6777b538SAndroid Build Coastguard Worker bool doomed = (entry->entry()->Data()->state == ENTRY_DOOMED);
506*6777b538SAndroid Build Coastguard Worker entry->entry()->Data()->state = ENTRY_DOOMED;
507*6777b538SAndroid Build Coastguard Worker entry->DoomImpl();
508*6777b538SAndroid Build Coastguard Worker return !doomed;
509*6777b538SAndroid Build Coastguard Worker }
510*6777b538SAndroid Build Coastguard Worker
NodeIsOldEnough(CacheRankingsBlock * node,int list)511*6777b538SAndroid Build Coastguard Worker bool Eviction::NodeIsOldEnough(CacheRankingsBlock* node, int list) {
512*6777b538SAndroid Build Coastguard Worker if (!node)
513*6777b538SAndroid Build Coastguard Worker return false;
514*6777b538SAndroid Build Coastguard Worker
515*6777b538SAndroid Build Coastguard Worker // If possible, we want to keep entries on each list at least kTargetTime
516*6777b538SAndroid Build Coastguard Worker // hours. Each successive list on the enumeration has 2x the target time of
517*6777b538SAndroid Build Coastguard Worker // the previous list.
518*6777b538SAndroid Build Coastguard Worker Time used = Time::FromInternalValue(node->Data()->last_used);
519*6777b538SAndroid Build Coastguard Worker int multiplier = 1 << list;
520*6777b538SAndroid Build Coastguard Worker return (Time::Now() - used).InHours() > kTargetTime * multiplier;
521*6777b538SAndroid Build Coastguard Worker }
522*6777b538SAndroid Build Coastguard Worker
SelectListByLength(Rankings::ScopedRankingsBlock * next)523*6777b538SAndroid Build Coastguard Worker int Eviction::SelectListByLength(Rankings::ScopedRankingsBlock* next) {
524*6777b538SAndroid Build Coastguard Worker int data_entries = header_->num_entries -
525*6777b538SAndroid Build Coastguard Worker header_->lru.sizes[Rankings::DELETED];
526*6777b538SAndroid Build Coastguard Worker // Start by having each list to be roughly the same size.
527*6777b538SAndroid Build Coastguard Worker if (header_->lru.sizes[0] > data_entries / 3)
528*6777b538SAndroid Build Coastguard Worker return 0;
529*6777b538SAndroid Build Coastguard Worker
530*6777b538SAndroid Build Coastguard Worker int list = (header_->lru.sizes[1] > data_entries / 3) ? 1 : 2;
531*6777b538SAndroid Build Coastguard Worker
532*6777b538SAndroid Build Coastguard Worker // Make sure that frequently used items are kept for a minimum time; we know
533*6777b538SAndroid Build Coastguard Worker // that this entry is not older than its current target, but it must be at
534*6777b538SAndroid Build Coastguard Worker // least older than the target for list 0 (kTargetTime), as long as we don't
535*6777b538SAndroid Build Coastguard Worker // exhaust list 0.
536*6777b538SAndroid Build Coastguard Worker if (!NodeIsOldEnough(next[list].get(), 0) &&
537*6777b538SAndroid Build Coastguard Worker header_->lru.sizes[0] > data_entries / 10)
538*6777b538SAndroid Build Coastguard Worker list = 0;
539*6777b538SAndroid Build Coastguard Worker
540*6777b538SAndroid Build Coastguard Worker return list;
541*6777b538SAndroid Build Coastguard Worker }
542*6777b538SAndroid Build Coastguard Worker
543*6777b538SAndroid Build Coastguard Worker } // namespace disk_cache
544