Chromium Code Reviews| 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 |