OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // The eviction policy is a very simple pure LRU, so the elements at the end of | 5 // The eviction policy is a very simple pure LRU, so the elements at the end of |
6 // the list are evicted until kCleanUpMargin free space is available. There is | 6 // the list are evicted until kCleanUpMargin free space is available. There is |
7 // only one list in use (Rankings::NO_USE), and elements are sent to the front | 7 // only one list in use (Rankings::NO_USE), and elements are sent to the front |
8 // of the list whenever they are accessed. | 8 // of the list whenever they are accessed. |
9 | 9 |
10 // The new (in-development) eviction policy ads re-use as a factor to evict | 10 // The new (in-development) eviction policy ads re-use as a factor to evict |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
66 new_eviction_ = backend->new_eviction_; | 66 new_eviction_ = backend->new_eviction_; |
67 first_trim_ = true; | 67 first_trim_ = true; |
68 trimming_ = false; | 68 trimming_ = false; |
69 delay_trim_ = false; | 69 delay_trim_ = false; |
70 } | 70 } |
71 | 71 |
72 void Eviction::TrimCache(bool empty) { | 72 void Eviction::TrimCache(bool empty) { |
73 if (new_eviction_) | 73 if (new_eviction_) |
74 return TrimCacheV2(empty); | 74 return TrimCacheV2(empty); |
75 | 75 |
76 Trace("*** Trim Cache ***"); | |
77 if (backend_->disabled_ || trimming_) | 76 if (backend_->disabled_ || trimming_) |
78 return; | 77 return; |
79 | 78 |
80 if (!empty && backend_->IsLoaded()) | 79 if (!empty && backend_->IsLoaded()) |
81 return PostDelayedTrim(); | 80 return PostDelayedTrim(); |
82 | 81 |
| 82 Trace("*** Trim Cache ***"); |
83 trimming_ = true; | 83 trimming_ = true; |
84 Time start = Time::Now(); | 84 Time start = Time::Now(); |
85 Rankings::ScopedRankingsBlock node(rankings_); | 85 Rankings::ScopedRankingsBlock node(rankings_); |
86 Rankings::ScopedRankingsBlock next(rankings_, | 86 Rankings::ScopedRankingsBlock next(rankings_, |
87 rankings_->GetPrev(node.get(), Rankings::NO_USE)); | 87 rankings_->GetPrev(node.get(), Rankings::NO_USE)); |
88 DCHECK(next.get()); | |
89 int target_size = empty ? 0 : max_size_; | 88 int target_size = empty ? 0 : max_size_; |
90 while (header_->num_bytes > target_size && next.get()) { | 89 while (header_->num_bytes > target_size && next.get()) { |
| 90 // The iterator could be invalidated within EvictEntry(). |
| 91 if (!next->HasData()) |
| 92 break; |
91 node.reset(next.release()); | 93 node.reset(next.release()); |
92 next.reset(rankings_->GetPrev(node.get(), Rankings::NO_USE)); | 94 next.reset(rankings_->GetPrev(node.get(), Rankings::NO_USE)); |
93 if (!node->Data()->pointer || empty) { | 95 if (!node->Data()->pointer || empty) { |
94 // This entry is not being used by anybody. | 96 // This entry is not being used by anybody. |
| 97 // Do NOT use node as an iterator after this point. |
| 98 rankings_->TrackRankingsBlock(node.get(), false); |
95 if (!EvictEntry(node.get(), empty)) | 99 if (!EvictEntry(node.get(), empty)) |
96 continue; | 100 continue; |
97 | 101 |
98 if (!empty) { | 102 if (!empty) { |
99 backend_->OnEvent(Stats::TRIM_ENTRY); | 103 backend_->OnEvent(Stats::TRIM_ENTRY); |
100 | 104 |
101 if ((Time::Now() - start).InMilliseconds() > 20) { | 105 if ((Time::Now() - start).InMilliseconds() > 20) { |
102 MessageLoop::current()->PostTask(FROM_HERE, | 106 MessageLoop::current()->PostTask(FROM_HERE, |
103 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); | 107 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); |
104 break; | 108 break; |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); | 188 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); |
185 } | 189 } |
186 } | 190 } |
187 } | 191 } |
188 | 192 |
189 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { | 193 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { |
190 return Rankings::NO_USE; | 194 return Rankings::NO_USE; |
191 } | 195 } |
192 | 196 |
193 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty) { | 197 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty) { |
194 EntryImpl* entry; | 198 EntryImpl* entry = backend_->GetEnumeratedEntry(node, true); |
195 bool dirty; | 199 if (!entry) { |
196 if (backend_->NewEntry(Addr(node->Data()->contents), &entry, &dirty)) { | |
197 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 200 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
198 return false; | 201 return false; |
199 } | 202 } |
200 | 203 |
201 if (node->Data()->pointer) { | |
202 // We ignore the failure; we're removing the entry anyway. | |
203 entry->Update(); | |
204 } | |
205 ReportTrimTimes(entry); | 204 ReportTrimTimes(entry); |
206 if (empty || !new_eviction_) { | 205 if (empty || !new_eviction_) { |
207 entry->Doom(); | 206 entry->Doom(); |
208 } else { | 207 } else { |
209 entry->DeleteEntryData(false); | 208 entry->DeleteEntryData(false); |
210 EntryStore* info = entry->entry()->Data(); | 209 EntryStore* info = entry->entry()->Data(); |
211 DCHECK(ENTRY_NORMAL == info->state); | 210 DCHECK(ENTRY_NORMAL == info->state); |
212 | 211 |
213 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry)); | 212 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry)); |
214 info->state = ENTRY_EVICTED; | 213 info->state = ENTRY_EVICTED; |
215 entry->entry()->Store(); | 214 entry->entry()->Store(); |
216 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); | 215 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); |
217 backend_->OnEvent(Stats::TRIM_ENTRY); | 216 backend_->OnEvent(Stats::TRIM_ENTRY); |
218 } | 217 } |
219 entry->Release(); | 218 entry->Release(); |
220 | 219 |
221 return true; | 220 return true; |
222 } | 221 } |
223 | 222 |
224 // ----------------------------------------------------------------------- | 223 // ----------------------------------------------------------------------- |
225 | 224 |
226 void Eviction::TrimCacheV2(bool empty) { | 225 void Eviction::TrimCacheV2(bool empty) { |
227 Trace("*** Trim Cache ***"); | |
228 if (backend_->disabled_ || trimming_) | 226 if (backend_->disabled_ || trimming_) |
229 return; | 227 return; |
230 | 228 |
231 if (!empty && backend_->IsLoaded()) | 229 if (!empty && backend_->IsLoaded()) |
232 return PostDelayedTrim(); | 230 return PostDelayedTrim(); |
233 | 231 |
| 232 Trace("*** Trim Cache ***"); |
234 trimming_ = true; | 233 trimming_ = true; |
235 Time start = Time::Now(); | 234 Time start = Time::Now(); |
236 | 235 |
237 const int kListsToSearch = 3; | 236 const int kListsToSearch = 3; |
238 Rankings::ScopedRankingsBlock next[kListsToSearch]; | 237 Rankings::ScopedRankingsBlock next[kListsToSearch]; |
239 int list = Rankings::LAST_ELEMENT; | 238 int list = Rankings::LAST_ELEMENT; |
240 | 239 |
241 // Get a node from each list. | 240 // Get a node from each list. |
242 for (int i = 0; i < kListsToSearch; i++) { | 241 for (int i = 0; i < kListsToSearch; i++) { |
243 bool done = false; | 242 bool done = false; |
(...skipping 19 matching lines...) Expand all Loading... |
263 } | 262 } |
264 | 263 |
265 if (empty) | 264 if (empty) |
266 list = 0; | 265 list = 0; |
267 | 266 |
268 Rankings::ScopedRankingsBlock node(rankings_); | 267 Rankings::ScopedRankingsBlock node(rankings_); |
269 | 268 |
270 int target_size = empty ? 0 : max_size_; | 269 int target_size = empty ? 0 : max_size_; |
271 for (; list < kListsToSearch; list++) { | 270 for (; list < kListsToSearch; list++) { |
272 while (header_->num_bytes > target_size && next[list].get()) { | 271 while (header_->num_bytes > target_size && next[list].get()) { |
| 272 // The iterator could be invalidated within EvictEntry(). |
| 273 if (!next[list]->HasData()) |
| 274 break; |
273 node.reset(next[list].release()); | 275 node.reset(next[list].release()); |
274 next[list].reset(rankings_->GetPrev(node.get(), | 276 next[list].reset(rankings_->GetPrev(node.get(), |
275 static_cast<Rankings::List>(list))); | 277 static_cast<Rankings::List>(list))); |
276 if (!node->Data()->pointer || empty) { | 278 if (!node->Data()->pointer || empty) { |
277 // This entry is not being used by anybody. | 279 // This entry is not being used by anybody. |
| 280 // Do NOT use node as an iterator after this point. |
| 281 rankings_->TrackRankingsBlock(node.get(), false); |
278 if (!EvictEntry(node.get(), empty)) | 282 if (!EvictEntry(node.get(), empty)) |
279 continue; | 283 continue; |
280 | 284 |
281 if (!empty && (Time::Now() - start).InMilliseconds() > 20) { | 285 if (!empty && (Time::Now() - start).InMilliseconds() > 20) { |
282 MessageLoop::current()->PostTask(FROM_HERE, | 286 MessageLoop::current()->PostTask(FROM_HERE, |
283 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); | 287 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); |
284 break; | 288 break; |
285 } | 289 } |
286 } | 290 } |
287 } | 291 } |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 } | 416 } |
413 | 417 |
414 bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) { | 418 bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) { |
415 EntryImpl* entry; | 419 EntryImpl* entry; |
416 bool dirty; | 420 bool dirty; |
417 if (backend_->NewEntry(Addr(node->Data()->contents), &entry, &dirty)) { | 421 if (backend_->NewEntry(Addr(node->Data()->contents), &entry, &dirty)) { |
418 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 422 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
419 return false; | 423 return false; |
420 } | 424 } |
421 | 425 |
| 426 // TODO(rvargas): figure out how to deal with corruption at this point (dirty |
| 427 // entries that live in this list). |
422 if (node->Data()->pointer) { | 428 if (node->Data()->pointer) { |
423 // We ignore the failure; we're removing the entry anyway. | 429 // We ignore the failure; we're removing the entry anyway. |
424 entry->Update(); | 430 entry->Update(); |
425 } | 431 } |
426 entry->entry()->Data()->state = ENTRY_DOOMED; | 432 entry->entry()->Data()->state = ENTRY_DOOMED; |
427 entry->Doom(); | 433 entry->Doom(); |
428 entry->Release(); | 434 entry->Release(); |
429 return true; | 435 return true; |
430 } | 436 } |
431 | 437 |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
473 Time::FromInternalValue(last2.get()->Data()->last_used)); | 479 Time::FromInternalValue(last2.get()->Data()->last_used)); |
474 if (last3.get()) | 480 if (last3.get()) |
475 CACHE_UMA(AGE, "HighUseAge", header_->experiment, | 481 CACHE_UMA(AGE, "HighUseAge", header_->experiment, |
476 Time::FromInternalValue(last3.get()->Data()->last_used)); | 482 Time::FromInternalValue(last3.get()->Data()->last_used)); |
477 if (last4.get()) | 483 if (last4.get()) |
478 CACHE_UMA(AGE, "DeletedAge", header_->experiment, | 484 CACHE_UMA(AGE, "DeletedAge", header_->experiment, |
479 Time::FromInternalValue(last4.get()->Data()->last_used)); | 485 Time::FromInternalValue(last4.get()->Data()->last_used)); |
480 } | 486 } |
481 | 487 |
482 } // namespace disk_cache | 488 } // namespace disk_cache |
OLD | NEW |