| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 109 if (new_eviction_) | 109 if (new_eviction_) |
| 110 return TrimCacheV2(empty); | 110 return TrimCacheV2(empty); |
| 111 | 111 |
| 112 Trace("*** Trim Cache ***"); | 112 Trace("*** Trim Cache ***"); |
| 113 trimming_ = true; | 113 trimming_ = true; |
| 114 TimeTicks start = TimeTicks::Now(); | 114 TimeTicks start = TimeTicks::Now(); |
| 115 Rankings::ScopedRankingsBlock node(rankings_); | 115 Rankings::ScopedRankingsBlock node(rankings_); |
| 116 Rankings::ScopedRankingsBlock next(rankings_, | 116 Rankings::ScopedRankingsBlock next(rankings_, |
| 117 rankings_->GetPrev(node.get(), Rankings::NO_USE)); | 117 rankings_->GetPrev(node.get(), Rankings::NO_USE)); |
| 118 int target_size = empty ? 0 : max_size_; | 118 int target_size = empty ? 0 : max_size_; |
| 119 while ((header_->num_bytes > target_size && next.get()) || test_mode_) { | 119 while ((header_->num_bytes > target_size || test_mode_) && next.get()) { |
| 120 // The iterator could be invalidated within EvictEntry(). | 120 // The iterator could be invalidated within EvictEntry(). |
| 121 if (!next->HasData()) | 121 if (!next->HasData()) |
| 122 break; | 122 break; |
| 123 node.reset(next.release()); | 123 node.reset(next.release()); |
| 124 next.reset(rankings_->GetPrev(node.get(), Rankings::NO_USE)); | 124 next.reset(rankings_->GetPrev(node.get(), Rankings::NO_USE)); |
| 125 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { | 125 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { |
| 126 // This entry is not being used by anybody. | 126 // This entry is not being used by anybody. |
| 127 // Do NOT use node as an iterator after this point. | 127 // Do NOT use node as an iterator after this point. |
| 128 rankings_->TrackRankingsBlock(node.get(), false); | 128 rankings_->TrackRankingsBlock(node.get(), false); |
| 129 if (!EvictEntry(node.get(), empty) && !test_mode_) | 129 if (!EvictEntry(node.get(), empty, Rankings::NO_USE) && !test_mode_) |
| 130 continue; | 130 continue; |
| 131 | 131 |
| 132 if (!empty) { | 132 if (!empty) { |
| 133 backend_->OnEvent(Stats::TRIM_ENTRY); | 133 backend_->OnEvent(Stats::TRIM_ENTRY); |
| 134 if (test_mode_) | 134 if (test_mode_) |
| 135 break; | 135 break; |
| 136 | 136 |
| 137 if ((TimeTicks::Now() - start).InMilliseconds() > 20) { | 137 if ((TimeTicks::Now() - start).InMilliseconds() > 20) { |
| 138 MessageLoop::current()->PostTask(FROM_HERE, | 138 MessageLoop::current()->PostTask(FROM_HERE, |
| 139 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); | 139 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 170 if (new_eviction_) | 170 if (new_eviction_) |
| 171 return OnCreateEntryV2(entry); | 171 return OnCreateEntryV2(entry); |
| 172 | 172 |
| 173 rankings_->Insert(entry->rankings(), true, GetListForEntry(entry)); | 173 rankings_->Insert(entry->rankings(), true, GetListForEntry(entry)); |
| 174 } | 174 } |
| 175 | 175 |
| 176 void Eviction::OnDoomEntry(EntryImpl* entry) { | 176 void Eviction::OnDoomEntry(EntryImpl* entry) { |
| 177 if (new_eviction_) | 177 if (new_eviction_) |
| 178 return OnDoomEntryV2(entry); | 178 return OnDoomEntryV2(entry); |
| 179 | 179 |
| 180 rankings_->Remove(entry->rankings(), GetListForEntry(entry)); | 180 if (entry->LeaveRankingsBehind()) |
| 181 return; |
| 182 |
| 183 rankings_->Remove(entry->rankings(), GetListForEntry(entry), true); |
| 181 } | 184 } |
| 182 | 185 |
| 183 void Eviction::OnDestroyEntry(EntryImpl* entry) { | 186 void Eviction::OnDestroyEntry(EntryImpl* entry) { |
| 184 if (new_eviction_) | 187 if (new_eviction_) |
| 185 return OnDestroyEntryV2(entry); | 188 return OnDestroyEntryV2(entry); |
| 186 } | 189 } |
| 187 | 190 |
| 188 void Eviction::SetTestMode() { | 191 void Eviction::SetTestMode() { |
| 189 test_mode_ = true; | 192 test_mode_ = true; |
| 190 } | 193 } |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 247 old.day_of_month = 1; | 250 old.day_of_month = 1; |
| 248 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); | 251 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); |
| 249 } | 252 } |
| 250 } | 253 } |
| 251 } | 254 } |
| 252 | 255 |
| 253 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { | 256 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { |
| 254 return Rankings::NO_USE; | 257 return Rankings::NO_USE; |
| 255 } | 258 } |
| 256 | 259 |
| 257 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty) { | 260 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty, |
| 258 EntryImpl* entry = backend_->GetEnumeratedEntry(node); | 261 Rankings::List list) { |
| 262 EntryImpl* entry = backend_->GetEnumeratedEntry(node, list); |
| 259 if (!entry) { | 263 if (!entry) { |
| 260 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 264 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
| 261 return false; | 265 return false; |
| 262 } | 266 } |
| 263 | 267 |
| 264 ReportTrimTimes(entry); | 268 ReportTrimTimes(entry); |
| 265 if (empty || !new_eviction_) { | 269 if (empty || !new_eviction_) { |
| 266 entry->DoomImpl(); | 270 entry->DoomImpl(); |
| 267 } else { | 271 } else { |
| 268 entry->DeleteEntryData(false); | 272 entry->DeleteEntryData(false); |
| 269 EntryStore* info = entry->entry()->Data(); | 273 EntryStore* info = entry->entry()->Data(); |
| 270 DCHECK_EQ(ENTRY_NORMAL, info->state); | 274 DCHECK_EQ(ENTRY_NORMAL, info->state); |
| 271 | 275 |
| 272 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry)); | 276 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry), true); |
| 273 info->state = ENTRY_EVICTED; | 277 info->state = ENTRY_EVICTED; |
| 274 entry->entry()->Store(); | 278 entry->entry()->Store(); |
| 275 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); | 279 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); |
| 276 backend_->OnEvent(Stats::TRIM_ENTRY); | 280 backend_->OnEvent(Stats::TRIM_ENTRY); |
| 277 } | 281 } |
| 278 entry->Release(); | 282 entry->Release(); |
| 279 | 283 |
| 280 return true; | 284 return true; |
| 281 } | 285 } |
| 282 | 286 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 308 if (!empty && Rankings::LAST_ELEMENT == list) | 312 if (!empty && Rankings::LAST_ELEMENT == list) |
| 309 list = SelectListByLength(next); | 313 list = SelectListByLength(next); |
| 310 | 314 |
| 311 if (empty) | 315 if (empty) |
| 312 list = 0; | 316 list = 0; |
| 313 | 317 |
| 314 Rankings::ScopedRankingsBlock node(rankings_); | 318 Rankings::ScopedRankingsBlock node(rankings_); |
| 315 | 319 |
| 316 int target_size = empty ? 0 : max_size_; | 320 int target_size = empty ? 0 : max_size_; |
| 317 for (; list < kListsToSearch; list++) { | 321 for (; list < kListsToSearch; list++) { |
| 318 while ((header_->num_bytes > target_size && next[list].get()) || | 322 while ((header_->num_bytes > target_size || test_mode_) && |
| 319 test_mode_) { | 323 next[list].get()) { |
| 320 // The iterator could be invalidated within EvictEntry(). | 324 // The iterator could be invalidated within EvictEntry(). |
| 321 if (!next[list]->HasData()) | 325 if (!next[list]->HasData()) |
| 322 break; | 326 break; |
| 323 node.reset(next[list].release()); | 327 node.reset(next[list].release()); |
| 324 next[list].reset(rankings_->GetPrev(node.get(), | 328 next[list].reset(rankings_->GetPrev(node.get(), |
| 325 static_cast<Rankings::List>(list))); | 329 static_cast<Rankings::List>(list))); |
| 326 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { | 330 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { |
| 327 // This entry is not being used by anybody. | 331 // This entry is not being used by anybody. |
| 328 // Do NOT use node as an iterator after this point. | 332 // Do NOT use node as an iterator after this point. |
| 329 rankings_->TrackRankingsBlock(node.get(), false); | 333 rankings_->TrackRankingsBlock(node.get(), false); |
| 330 if (!EvictEntry(node.get(), empty) && !test_mode_) | 334 if (!EvictEntry(node.get(), empty, static_cast<Rankings::List>(list)) && |
| 335 !test_mode_) |
| 331 continue; | 336 continue; |
| 332 | 337 |
| 333 if (!empty && test_mode_) | 338 if (!empty && test_mode_) |
| 334 break; | 339 break; |
| 335 | 340 |
| 336 if (!empty && (TimeTicks::Now() - start).InMilliseconds() > 20) { | 341 if (!empty && (TimeTicks::Now() - start).InMilliseconds() > 20) { |
| 337 MessageLoop::current()->PostTask(FROM_HERE, | 342 MessageLoop::current()->PostTask(FROM_HERE, |
| 338 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); | 343 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); |
| 339 break; | 344 break; |
| 340 } | 345 } |
| (...skipping 29 matching lines...) Expand all Loading... |
| 370 void Eviction::OnOpenEntryV2(EntryImpl* entry) { | 375 void Eviction::OnOpenEntryV2(EntryImpl* entry) { |
| 371 EntryStore* info = entry->entry()->Data(); | 376 EntryStore* info = entry->entry()->Data(); |
| 372 DCHECK_EQ(ENTRY_NORMAL, info->state); | 377 DCHECK_EQ(ENTRY_NORMAL, info->state); |
| 373 | 378 |
| 374 if (info->reuse_count < kint32max) { | 379 if (info->reuse_count < kint32max) { |
| 375 info->reuse_count++; | 380 info->reuse_count++; |
| 376 entry->entry()->set_modified(); | 381 entry->entry()->set_modified(); |
| 377 | 382 |
| 378 // We may need to move this to a new list. | 383 // We may need to move this to a new list. |
| 379 if (1 == info->reuse_count) { | 384 if (1 == info->reuse_count) { |
| 380 rankings_->Remove(entry->rankings(), Rankings::NO_USE); | 385 rankings_->Remove(entry->rankings(), Rankings::NO_USE, true); |
| 381 rankings_->Insert(entry->rankings(), false, Rankings::LOW_USE); | 386 rankings_->Insert(entry->rankings(), false, Rankings::LOW_USE); |
| 382 entry->entry()->Store(); | 387 entry->entry()->Store(); |
| 383 } else if (kHighUse == info->reuse_count) { | 388 } else if (kHighUse == info->reuse_count) { |
| 384 rankings_->Remove(entry->rankings(), Rankings::LOW_USE); | 389 rankings_->Remove(entry->rankings(), Rankings::LOW_USE, true); |
| 385 rankings_->Insert(entry->rankings(), false, Rankings::HIGH_USE); | 390 rankings_->Insert(entry->rankings(), false, Rankings::HIGH_USE); |
| 386 entry->entry()->Store(); | 391 entry->entry()->Store(); |
| 387 } | 392 } |
| 388 } | 393 } |
| 389 } | 394 } |
| 390 | 395 |
| 391 void Eviction::OnCreateEntryV2(EntryImpl* entry) { | 396 void Eviction::OnCreateEntryV2(EntryImpl* entry) { |
| 392 EntryStore* info = entry->entry()->Data(); | 397 EntryStore* info = entry->entry()->Data(); |
| 393 switch (info->state) { | 398 switch (info->state) { |
| 394 case ENTRY_NORMAL: { | 399 case ENTRY_NORMAL: { |
| 395 DCHECK(!info->reuse_count); | 400 DCHECK(!info->reuse_count); |
| 396 DCHECK(!info->refetch_count); | 401 DCHECK(!info->refetch_count); |
| 397 break; | 402 break; |
| 398 }; | 403 }; |
| 399 case ENTRY_EVICTED: { | 404 case ENTRY_EVICTED: { |
| 400 if (info->refetch_count < kint32max) | 405 if (info->refetch_count < kint32max) |
| 401 info->refetch_count++; | 406 info->refetch_count++; |
| 402 | 407 |
| 403 if (info->refetch_count > kHighUse && info->reuse_count < kHighUse) { | 408 if (info->refetch_count > kHighUse && info->reuse_count < kHighUse) { |
| 404 info->reuse_count = kHighUse; | 409 info->reuse_count = kHighUse; |
| 405 } else { | 410 } else { |
| 406 info->reuse_count++; | 411 info->reuse_count++; |
| 407 } | 412 } |
| 408 info->state = ENTRY_NORMAL; | 413 info->state = ENTRY_NORMAL; |
| 409 entry->entry()->Store(); | 414 entry->entry()->Store(); |
| 410 rankings_->Remove(entry->rankings(), Rankings::DELETED); | 415 rankings_->Remove(entry->rankings(), Rankings::DELETED, true); |
| 411 break; | 416 break; |
| 412 }; | 417 }; |
| 413 default: | 418 default: |
| 414 NOTREACHED(); | 419 NOTREACHED(); |
| 415 } | 420 } |
| 416 | 421 |
| 417 rankings_->Insert(entry->rankings(), true, GetListForEntryV2(entry)); | 422 rankings_->Insert(entry->rankings(), true, GetListForEntryV2(entry)); |
| 418 } | 423 } |
| 419 | 424 |
| 420 void Eviction::OnDoomEntryV2(EntryImpl* entry) { | 425 void Eviction::OnDoomEntryV2(EntryImpl* entry) { |
| 421 EntryStore* info = entry->entry()->Data(); | 426 EntryStore* info = entry->entry()->Data(); |
| 422 if (ENTRY_NORMAL != info->state) | 427 if (ENTRY_NORMAL != info->state) |
| 423 return; | 428 return; |
| 424 | 429 |
| 425 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry)); | 430 if (entry->LeaveRankingsBehind()) { |
| 431 info->state = ENTRY_DOOMED; |
| 432 entry->entry()->Store(); |
| 433 return; |
| 434 } |
| 435 |
| 436 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry), true); |
| 426 | 437 |
| 427 info->state = ENTRY_DOOMED; | 438 info->state = ENTRY_DOOMED; |
| 428 entry->entry()->Store(); | 439 entry->entry()->Store(); |
| 429 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); | 440 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); |
| 430 } | 441 } |
| 431 | 442 |
| 432 void Eviction::OnDestroyEntryV2(EntryImpl* entry) { | 443 void Eviction::OnDestroyEntryV2(EntryImpl* entry) { |
| 433 rankings_->Remove(entry->rankings(), Rankings::DELETED); | 444 if (entry->LeaveRankingsBehind()) |
| 445 return; |
| 446 |
| 447 rankings_->Remove(entry->rankings(), Rankings::DELETED, true); |
| 434 } | 448 } |
| 435 | 449 |
| 436 Rankings::List Eviction::GetListForEntryV2(EntryImpl* entry) { | 450 Rankings::List Eviction::GetListForEntryV2(EntryImpl* entry) { |
| 437 EntryStore* info = entry->entry()->Data(); | 451 EntryStore* info = entry->entry()->Data(); |
| 438 DCHECK_EQ(ENTRY_NORMAL, info->state); | 452 DCHECK_EQ(ENTRY_NORMAL, info->state); |
| 439 | 453 |
| 440 if (!info->reuse_count) | 454 if (!info->reuse_count) |
| 441 return Rankings::NO_USE; | 455 return Rankings::NO_USE; |
| 442 | 456 |
| 443 if (info->reuse_count < kHighUse) | 457 if (info->reuse_count < kHighUse) |
| 444 return Rankings::LOW_USE; | 458 return Rankings::LOW_USE; |
| 445 | 459 |
| 446 return Rankings::HIGH_USE; | 460 return Rankings::HIGH_USE; |
| 447 } | 461 } |
| 448 | 462 |
| 449 // This is a minimal implementation that just discards the oldest nodes. | 463 // This is a minimal implementation that just discards the oldest nodes. |
| 450 // TODO(rvargas): Do something better here. | 464 // TODO(rvargas): Do something better here. |
| 451 void Eviction::TrimDeleted(bool empty) { | 465 void Eviction::TrimDeleted(bool empty) { |
| 452 Trace("*** Trim Deleted ***"); | 466 Trace("*** Trim Deleted ***"); |
| 453 if (backend_->disabled_) | 467 if (backend_->disabled_) |
| 454 return; | 468 return; |
| 455 | 469 |
| 456 TimeTicks start = TimeTicks::Now(); | 470 TimeTicks start = TimeTicks::Now(); |
| 457 Rankings::ScopedRankingsBlock node(rankings_); | 471 Rankings::ScopedRankingsBlock node(rankings_); |
| 458 Rankings::ScopedRankingsBlock next(rankings_, | 472 Rankings::ScopedRankingsBlock next(rankings_, |
| 459 rankings_->GetPrev(node.get(), Rankings::DELETED)); | 473 rankings_->GetPrev(node.get(), Rankings::DELETED)); |
| 460 bool deleted = false; | 474 bool deleted = false; |
| 461 for (int i = 0; (i < 4 || empty) && next.get(); i++) { | 475 while (next.get() && |
| 476 (empty || (TimeTicks::Now() - start).InMilliseconds() < 20)) { |
| 462 node.reset(next.release()); | 477 node.reset(next.release()); |
| 463 next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED)); | 478 next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED)); |
| 464 deleted |= RemoveDeletedNode(node.get()); | 479 deleted |= RemoveDeletedNode(node.get()); |
| 465 if (test_mode_) | 480 if (test_mode_) |
| 466 break; | 481 break; |
| 467 } | 482 } |
| 468 | 483 |
| 469 // Normally we use 25% for each list. The experiment doubles the number of | 484 // Normally we use 25% for each list. The experiment doubles the number of |
| 470 // deleted entries, so the total number of entries increases by 25%. Using | 485 // deleted entries, so the total number of entries increases by 25%. Using |
| 471 // 40% of that value for deleted entries leaves the size of the other three | 486 // 40% of that value for deleted entries leaves the size of the other three |
| 472 // lists intact. | 487 // lists intact. |
| 473 int max_length = in_experiment_ ? header_->num_entries * 2 / 5 : | 488 int max_length = in_experiment_ ? header_->num_entries * 2 / 5 : |
| 474 header_->num_entries / 4; | 489 header_->num_entries / 4; |
| 475 if (deleted && !empty && !test_mode_ && | 490 if (deleted && !empty && !test_mode_ && |
| 476 header_->lru.sizes[Rankings::DELETED] > max_length) { | 491 header_->lru.sizes[Rankings::DELETED] > max_length) { |
| 477 MessageLoop::current()->PostTask(FROM_HERE, | 492 MessageLoop::current()->PostTask(FROM_HERE, |
| 478 factory_.NewRunnableMethod(&Eviction::TrimDeleted, false)); | 493 factory_.NewRunnableMethod(&Eviction::TrimDeleted, false)); |
| 479 } | 494 } |
| 480 | 495 |
| 481 CACHE_UMA(AGE_MS, "TotalTrimDeletedTime", 0, start); | 496 CACHE_UMA(AGE_MS, "TotalTrimDeletedTime", 0, start); |
| 482 Trace("*** Trim Deleted end ***"); | 497 Trace("*** Trim Deleted end ***"); |
| 483 return; | 498 return; |
| 484 } | 499 } |
| 485 | 500 |
| 486 bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) { | 501 bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) { |
| 487 EntryImpl* entry = backend_->GetEnumeratedEntry(node); | 502 EntryImpl* entry = backend_->GetEnumeratedEntry(node, Rankings::DELETED); |
| 488 if (!entry) { | 503 if (!entry) { |
| 489 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 504 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
| 490 return false; | 505 return false; |
| 491 } | 506 } |
| 492 | 507 |
| 493 bool doomed = (entry->entry()->Data()->state == ENTRY_DOOMED); | 508 bool doomed = (entry->entry()->Data()->state == ENTRY_DOOMED); |
| 494 entry->entry()->Data()->state = ENTRY_DOOMED; | 509 entry->entry()->Data()->state = ENTRY_DOOMED; |
| 495 entry->DoomImpl(); | 510 entry->DoomImpl(); |
| 496 entry->Release(); | 511 entry->Release(); |
| 497 return !doomed; | 512 return !doomed; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 550 Time::FromInternalValue(last2.get()->Data()->last_used)); | 565 Time::FromInternalValue(last2.get()->Data()->last_used)); |
| 551 if (last3.get()) | 566 if (last3.get()) |
| 552 CACHE_UMA(AGE, "HighUseAge", 0, | 567 CACHE_UMA(AGE, "HighUseAge", 0, |
| 553 Time::FromInternalValue(last3.get()->Data()->last_used)); | 568 Time::FromInternalValue(last3.get()->Data()->last_used)); |
| 554 if (last4.get()) | 569 if (last4.get()) |
| 555 CACHE_UMA(AGE, "DeletedAge", 0, | 570 CACHE_UMA(AGE, "DeletedAge", 0, |
| 556 Time::FromInternalValue(last4.get()->Data()->last_used)); | 571 Time::FromInternalValue(last4.get()->Data()->last_used)); |
| 557 } | 572 } |
| 558 | 573 |
| 559 } // namespace disk_cache | 574 } // namespace disk_cache |
| OLD | NEW |