OLD | NEW |
---|---|
1 // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2010 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 adds re-use as a factor to evict | 10 // The new (in-development) eviction policy adds re-use as a factor to evict |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
75 backend_ = backend; | 75 backend_ = backend; |
76 rankings_ = &backend->rankings_; | 76 rankings_ = &backend->rankings_; |
77 header_ = &backend_->data_->header; | 77 header_ = &backend_->data_->header; |
78 max_size_ = LowWaterAdjust(backend_->max_size_); | 78 max_size_ = LowWaterAdjust(backend_->max_size_); |
79 new_eviction_ = backend->new_eviction_; | 79 new_eviction_ = backend->new_eviction_; |
80 first_trim_ = true; | 80 first_trim_ = true; |
81 trimming_ = false; | 81 trimming_ = false; |
82 delay_trim_ = false; | 82 delay_trim_ = false; |
83 trim_delays_ = 0; | 83 trim_delays_ = 0; |
84 init_ = true; | 84 init_ = true; |
85 test_mode_ = false; | |
85 in_experiment_ = (header_->experiment == EXPERIMENT_DELETED_LIST_IN); | 86 in_experiment_ = (header_->experiment == EXPERIMENT_DELETED_LIST_IN); |
86 } | 87 } |
87 | 88 |
88 void Eviction::Stop() { | 89 void Eviction::Stop() { |
89 // It is possible for the backend initialization to fail, in which case this | 90 // It is possible for the backend initialization to fail, in which case this |
90 // object was never initialized... and there is nothing to do. | 91 // object was never initialized... and there is nothing to do. |
91 if (!init_) | 92 if (!init_) |
92 return; | 93 return; |
93 | 94 |
94 // We want to stop further evictions, so let's pretend that we are busy from | 95 // We want to stop further evictions, so let's pretend that we are busy from |
(...skipping 23 matching lines...) Expand all Loading... | |
118 while (header_->num_bytes > target_size && next.get()) { | 119 while (header_->num_bytes > target_size && next.get()) { |
119 // The iterator could be invalidated within EvictEntry(). | 120 // The iterator could be invalidated within EvictEntry(). |
120 if (!next->HasData()) | 121 if (!next->HasData()) |
121 break; | 122 break; |
122 node.reset(next.release()); | 123 node.reset(next.release()); |
123 next.reset(rankings_->GetPrev(node.get(), Rankings::NO_USE)); | 124 next.reset(rankings_->GetPrev(node.get(), Rankings::NO_USE)); |
124 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { | 125 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { |
125 // This entry is not being used by anybody. | 126 // This entry is not being used by anybody. |
126 // Do NOT use node as an iterator after this point. | 127 // Do NOT use node as an iterator after this point. |
127 rankings_->TrackRankingsBlock(node.get(), false); | 128 rankings_->TrackRankingsBlock(node.get(), false); |
128 if (!EvictEntry(node.get(), empty)) | 129 if (!EvictEntry(node.get(), empty) && !test_mode_) |
129 continue; | 130 continue; |
130 | 131 |
131 if (!empty) { | 132 if (!empty) { |
132 backend_->OnEvent(Stats::TRIM_ENTRY); | 133 backend_->OnEvent(Stats::TRIM_ENTRY); |
134 if (test_mode_) | |
135 break; | |
133 | 136 |
134 if ((TimeTicks::Now() - start).InMilliseconds() > 20) { | 137 if ((TimeTicks::Now() - start).InMilliseconds() > 20) { |
135 MessageLoop::current()->PostTask(FROM_HERE, | 138 MessageLoop::current()->PostTask(FROM_HERE, |
136 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); | 139 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); |
137 break; | 140 break; |
138 } | 141 } |
139 } | 142 } |
140 } | 143 } |
141 } | 144 } |
142 | 145 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
175 return OnDoomEntryV2(entry); | 178 return OnDoomEntryV2(entry); |
176 | 179 |
177 rankings_->Remove(entry->rankings(), GetListForEntry(entry)); | 180 rankings_->Remove(entry->rankings(), GetListForEntry(entry)); |
178 } | 181 } |
179 | 182 |
180 void Eviction::OnDestroyEntry(EntryImpl* entry) { | 183 void Eviction::OnDestroyEntry(EntryImpl* entry) { |
181 if (new_eviction_) | 184 if (new_eviction_) |
182 return OnDestroyEntryV2(entry); | 185 return OnDestroyEntryV2(entry); |
183 } | 186 } |
184 | 187 |
188 void Eviction::SetTestMode() { | |
189 test_mode_ = true; | |
190 } | |
191 | |
192 void Eviction::TrimDeletedList(bool empty) { | |
193 DCHECK(test_mode_ && new_eviction_); | |
194 TrimDeleted(empty); | |
195 } | |
196 | |
185 void Eviction::PostDelayedTrim() { | 197 void Eviction::PostDelayedTrim() { |
186 // Prevent posting multiple tasks. | 198 // Prevent posting multiple tasks. |
187 if (delay_trim_) | 199 if (delay_trim_) |
188 return; | 200 return; |
189 delay_trim_ = true; | 201 delay_trim_ = true; |
190 trim_delays_++; | 202 trim_delays_++; |
191 MessageLoop::current()->PostDelayedTask(FROM_HERE, | 203 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
192 factory_.NewRunnableMethod(&Eviction::DelayedTrim), 1000); | 204 factory_.NewRunnableMethod(&Eviction::DelayedTrim), 1000); |
193 } | 205 } |
194 | 206 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
235 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); | 247 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); |
236 } | 248 } |
237 } | 249 } |
238 } | 250 } |
239 | 251 |
240 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { | 252 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { |
241 return Rankings::NO_USE; | 253 return Rankings::NO_USE; |
242 } | 254 } |
243 | 255 |
244 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty) { | 256 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty) { |
245 EntryImpl* entry = backend_->GetEnumeratedEntry(node, true); | 257 EntryImpl* entry = backend_->GetEnumeratedEntry(node); |
246 if (!entry) { | 258 if (!entry) { |
247 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 259 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
248 return false; | 260 return false; |
249 } | 261 } |
250 | 262 |
251 ReportTrimTimes(entry); | 263 ReportTrimTimes(entry); |
252 if (empty || !new_eviction_) { | 264 if (empty || !new_eviction_) { |
253 entry->DoomImpl(); | 265 entry->DoomImpl(); |
254 } else { | 266 } else { |
255 entry->DeleteEntryData(false); | 267 entry->DeleteEntryData(false); |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
313 // The iterator could be invalidated within EvictEntry(). | 325 // The iterator could be invalidated within EvictEntry(). |
314 if (!next[list]->HasData()) | 326 if (!next[list]->HasData()) |
315 break; | 327 break; |
316 node.reset(next[list].release()); | 328 node.reset(next[list].release()); |
317 next[list].reset(rankings_->GetPrev(node.get(), | 329 next[list].reset(rankings_->GetPrev(node.get(), |
318 static_cast<Rankings::List>(list))); | 330 static_cast<Rankings::List>(list))); |
319 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { | 331 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { |
320 // This entry is not being used by anybody. | 332 // This entry is not being used by anybody. |
321 // Do NOT use node as an iterator after this point. | 333 // Do NOT use node as an iterator after this point. |
322 rankings_->TrackRankingsBlock(node.get(), false); | 334 rankings_->TrackRankingsBlock(node.get(), false); |
323 if (!EvictEntry(node.get(), empty)) | 335 if (!EvictEntry(node.get(), empty) && !test_mode_) |
324 continue; | 336 continue; |
325 | 337 |
338 if (!empty && test_mode_) | |
339 break; | |
340 | |
326 if (!empty && (TimeTicks::Now() - start).InMilliseconds() > 20) { | 341 if (!empty && (TimeTicks::Now() - start).InMilliseconds() > 20) { |
327 MessageLoop::current()->PostTask(FROM_HERE, | 342 MessageLoop::current()->PostTask(FROM_HERE, |
328 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); | 343 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); |
329 break; | 344 break; |
330 } | 345 } |
331 } | 346 } |
332 } | 347 } |
333 if (!empty) | 348 if (!empty) |
334 list = kListsToSearch; | 349 list = kListsToSearch; |
335 } | 350 } |
336 | 351 |
337 if (empty) { | 352 if (empty) { |
338 TrimDeleted(true); | 353 TrimDeleted(true); |
339 } else if (header_->lru.sizes[Rankings::DELETED] > header_->num_entries / 4) { | 354 } else if (header_->lru.sizes[Rankings::DELETED] > header_->num_entries / 4 && |
355 !test_mode_) { | |
340 MessageLoop::current()->PostTask(FROM_HERE, | 356 MessageLoop::current()->PostTask(FROM_HERE, |
341 factory_.NewRunnableMethod(&Eviction::TrimDeleted, empty)); | 357 factory_.NewRunnableMethod(&Eviction::TrimDeleted, empty)); |
342 } | 358 } |
343 | 359 |
344 if (empty) { | 360 if (empty) { |
345 CACHE_UMA(AGE_MS, "TotalClearTimeV2", 0, start); | 361 CACHE_UMA(AGE_MS, "TotalClearTimeV2", 0, start); |
346 } else { | 362 } else { |
347 CACHE_UMA(AGE_MS, "TotalTrimTimeV2", backend_->GetSizeGroup(), start); | 363 CACHE_UMA(AGE_MS, "TotalTrimTimeV2", backend_->GetSizeGroup(), start); |
348 } | 364 } |
349 | 365 |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
444 | 460 |
445 TimeTicks start = TimeTicks::Now(); | 461 TimeTicks start = TimeTicks::Now(); |
446 Rankings::ScopedRankingsBlock node(rankings_); | 462 Rankings::ScopedRankingsBlock node(rankings_); |
447 Rankings::ScopedRankingsBlock next(rankings_, | 463 Rankings::ScopedRankingsBlock next(rankings_, |
448 rankings_->GetPrev(node.get(), Rankings::DELETED)); | 464 rankings_->GetPrev(node.get(), Rankings::DELETED)); |
449 bool deleted = false; | 465 bool deleted = false; |
450 for (int i = 0; (i < 4 || empty) && next.get(); i++) { | 466 for (int i = 0; (i < 4 || empty) && next.get(); i++) { |
451 node.reset(next.release()); | 467 node.reset(next.release()); |
452 next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED)); | 468 next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED)); |
453 deleted |= RemoveDeletedNode(node.get()); | 469 deleted |= RemoveDeletedNode(node.get()); |
470 if (test_mode_) | |
471 break; | |
454 } | 472 } |
455 | 473 |
456 // Normally we use 25% for each list. The experiment doubles the number of | 474 // Normally we use 25% for each list. The experiment doubles the number of |
457 // deleted entries, so the total number of entries increases by 25%. Using | 475 // deleted entries, so the total number of entries increases by 25%. Using |
458 // 40% of that value for deleted entries leaves the size of the other three | 476 // 40% of that value for deleted entries leaves the size of the other three |
459 // lists intact. | 477 // lists intact. |
460 int max_length = in_experiment_ ? header_->num_entries * 2 / 5 : | 478 int max_length = in_experiment_ ? header_->num_entries * 2 / 5 : |
461 header_->num_entries / 4; | 479 header_->num_entries / 4; |
462 if (deleted && !empty && | 480 if (deleted && !empty && !test_mode_ && |
463 header_->lru.sizes[Rankings::DELETED] > max_length) | 481 header_->lru.sizes[Rankings::DELETED] > max_length) { |
464 MessageLoop::current()->PostTask(FROM_HERE, | 482 MessageLoop::current()->PostTask(FROM_HERE, |
465 factory_.NewRunnableMethod(&Eviction::TrimDeleted, false)); | 483 factory_.NewRunnableMethod(&Eviction::TrimDeleted, false)); |
484 } | |
466 | 485 |
467 CACHE_UMA(AGE_MS, "TotalTrimDeletedTime", 0, start); | 486 CACHE_UMA(AGE_MS, "TotalTrimDeletedTime", 0, start); |
468 Trace("*** Trim Deleted end ***"); | 487 Trace("*** Trim Deleted end ***"); |
469 return; | 488 return; |
470 } | 489 } |
471 | 490 |
472 bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) { | 491 bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) { |
473 EntryImpl* entry; | 492 EntryImpl* entry = backend_->GetEnumeratedEntry(node); |
474 bool dirty; | 493 if (!entry) { |
475 if (backend_->NewEntry(Addr(node->Data()->contents), &entry, &dirty)) { | |
476 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 494 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
477 return false; | 495 return false; |
478 } | 496 } |
479 | 497 |
480 // TODO(rvargas): figure out how to deal with corruption at this point (dirty | |
481 // entries that live in this list). | |
gavinp
2011/01/25 20:23:51
Awesome! I'm always happy to see TODOs go.
| |
482 if (node->Data()->dirty) { | |
483 // We ignore the failure; we're removing the entry anyway. | |
484 entry->Update(); | |
485 } | |
486 bool doomed = (entry->entry()->Data()->state == ENTRY_DOOMED); | 498 bool doomed = (entry->entry()->Data()->state == ENTRY_DOOMED); |
487 entry->entry()->Data()->state = ENTRY_DOOMED; | 499 entry->entry()->Data()->state = ENTRY_DOOMED; |
488 entry->DoomImpl(); | 500 entry->DoomImpl(); |
489 entry->Release(); | 501 entry->Release(); |
490 return !doomed; | 502 return !doomed; |
491 } | 503 } |
492 | 504 |
493 bool Eviction::NodeIsOldEnough(CacheRankingsBlock* node, int list) { | 505 bool Eviction::NodeIsOldEnough(CacheRankingsBlock* node, int list) { |
494 if (!node) | 506 if (!node) |
495 return false; | 507 return false; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
534 Time::FromInternalValue(last2.get()->Data()->last_used)); | 546 Time::FromInternalValue(last2.get()->Data()->last_used)); |
535 if (last3.get()) | 547 if (last3.get()) |
536 CACHE_UMA(AGE, "HighUseAge", 0, | 548 CACHE_UMA(AGE, "HighUseAge", 0, |
537 Time::FromInternalValue(last3.get()->Data()->last_used)); | 549 Time::FromInternalValue(last3.get()->Data()->last_used)); |
538 if (last4.get()) | 550 if (last4.get()) |
539 CACHE_UMA(AGE, "DeletedAge", 0, | 551 CACHE_UMA(AGE, "DeletedAge", 0, |
540 Time::FromInternalValue(last4.get()->Data()->last_used)); | 552 Time::FromInternalValue(last4.get()->Data()->last_used)); |
541 } | 553 } |
542 | 554 |
543 } // namespace disk_cache | 555 } // namespace disk_cache |
OLD | NEW |