| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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 #include "net/disk_cache/simple/simple_index.h" | 5 #include "net/disk_cache/simple/simple_index.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 | 25 |
| 26 #if defined(OS_POSIX) | 26 #if defined(OS_POSIX) |
| 27 #include <sys/stat.h> | 27 #include <sys/stat.h> |
| 28 #include <sys/time.h> | 28 #include <sys/time.h> |
| 29 #endif | 29 #endif |
| 30 | 30 |
| 31 namespace { | 31 namespace { |
| 32 | 32 |
| 33 // How many seconds we delay writing the index to disk since the last cache | 33 // How many seconds we delay writing the index to disk since the last cache |
| 34 // operation has happened. | 34 // operation has happened. |
| 35 const int kWriteToDiskDelayMSecs = 20000; | 35 const int kWriteToDiskDelaySecs = 20; |
| 36 const int kWriteToDiskOnBackgroundDelayMSecs = 100; | 36 |
| 37 // WriteToDisk at lest every 5 minutes. |
| 38 const int kMaxWriteToDiskDelaySecs = 300; |
| 37 | 39 |
| 38 // Divides the cache space into this amount of parts to evict when only one part | 40 // Divides the cache space into this amount of parts to evict when only one part |
| 39 // is left. | 41 // is left. |
| 40 const uint32 kEvictionMarginDivisor = 20; | 42 const uint32 kEvictionMarginDivisor = 20; |
| 41 | 43 |
| 42 // Utility class used for timestamp comparisons in entry metadata while sorting. | 44 // Utility class used for timestamp comparisons in entry metadata while sorting. |
| 43 class CompareHashesForTimestamp { | 45 class CompareHashesForTimestamp { |
| 44 typedef disk_cache::SimpleIndex SimpleIndex; | 46 typedef disk_cache::SimpleIndex SimpleIndex; |
| 45 typedef disk_cache::SimpleIndex::EntrySet EntrySet; | 47 typedef disk_cache::SimpleIndex::EntrySet EntrySet; |
| 46 public: | 48 public: |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 157 base::SingleThreadTaskRunner* io_thread, | 159 base::SingleThreadTaskRunner* io_thread, |
| 158 const base::FilePath& path) | 160 const base::FilePath& path) |
| 159 : cache_size_(0), | 161 : cache_size_(0), |
| 160 max_size_(0), | 162 max_size_(0), |
| 161 high_watermark_(0), | 163 high_watermark_(0), |
| 162 low_watermark_(0), | 164 low_watermark_(0), |
| 163 eviction_in_progress_(false), | 165 eviction_in_progress_(false), |
| 164 initialized_(false), | 166 initialized_(false), |
| 165 index_filename_(path.AppendASCII("the-real-index")), | 167 index_filename_(path.AppendASCII("the-real-index")), |
| 166 cache_thread_(cache_thread), | 168 cache_thread_(cache_thread), |
| 167 io_thread_(io_thread), | 169 io_thread_(io_thread) { |
| 168 #if defined(OS_ANDROID) | |
| 169 activity_status_notifier_( | |
| 170 base::Bind(&SimpleIndex::ActivityStatusChanged, AsWeakPtr())), | |
| 171 #endif | |
| 172 app_on_background_(false) { | |
| 173 } | 170 } |
| 174 | 171 |
| 175 SimpleIndex::~SimpleIndex() { | 172 SimpleIndex::~SimpleIndex() { |
| 176 DCHECK(io_thread_checker_.CalledOnValidThread()); | 173 DCHECK(io_thread_checker_.CalledOnValidThread()); |
| 177 | 174 |
| 178 // Fail all callbacks waiting for the index to come up. | 175 // Fail all callbacks waiting for the index to come up. |
| 179 for (CallbackList::iterator it = to_run_when_initialized_.begin(), | 176 for (CallbackList::iterator it = to_run_when_initialized_.begin(), |
| 180 end = to_run_when_initialized_.end(); it != end; ++it) { | 177 end = to_run_when_initialized_.end(); it != end; ++it) { |
| 181 it->Run(net::ERR_ABORTED); | 178 it->Run(net::ERR_ABORTED); |
| 182 } | 179 } |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 370 const disk_cache::EntryMetadata& entry_metadata, | 367 const disk_cache::EntryMetadata& entry_metadata, |
| 371 EntrySet* entry_set) { | 368 EntrySet* entry_set) { |
| 372 DCHECK(entry_set); | 369 DCHECK(entry_set); |
| 373 entry_set->insert( | 370 entry_set->insert( |
| 374 std::make_pair(entry_metadata.GetHashKey(), entry_metadata)); | 371 std::make_pair(entry_metadata.GetHashKey(), entry_metadata)); |
| 375 } | 372 } |
| 376 | 373 |
| 377 void SimpleIndex::PostponeWritingToDisk() { | 374 void SimpleIndex::PostponeWritingToDisk() { |
| 378 if (!initialized_) | 375 if (!initialized_) |
| 379 return; | 376 return; |
| 380 int delay = kWriteToDiskDelayMSecs; | 377 const base::TimeDelta file_age = base::Time::Now() - last_write_to_disk_; |
| 381 if (app_on_background_) { | 378 if (file_age > base::TimeDelta::FromSeconds(kMaxWriteToDiskDelaySecs) && |
| 382 // When the app is in the background we can write the index much more | 379 write_to_disk_timer_.IsRunning()) { |
| 383 // frequently. We could even write it to disk on every operation if we | 380 // If the index file is too old and there is a timer programmed to run a |
| 384 // wanted to. | 381 // WriteToDisk soon, we don't postpone it, so we always WriteToDisk |
| 385 delay = kWriteToDiskOnBackgroundDelayMSecs; | 382 // approximately every kMaxWriteToDiskDelaySecs. |
| 383 return; |
| 386 } | 384 } |
| 385 |
| 387 // If the timer is already active, Start() will just Reset it, postponing it. | 386 // If the timer is already active, Start() will just Reset it, postponing it. |
| 388 write_to_disk_timer_.Start( | 387 write_to_disk_timer_.Start( |
| 389 FROM_HERE, | 388 FROM_HERE, |
| 390 base::TimeDelta::FromMilliseconds(delay), | 389 base::TimeDelta::FromSeconds(kWriteToDiskDelaySecs), |
| 391 base::Bind(&SimpleIndex::WriteToDisk, AsWeakPtr())); | 390 base::Bind(&SimpleIndex::WriteToDisk, AsWeakPtr())); |
| 392 } | 391 } |
| 393 | 392 |
| 394 // static | 393 // static |
| 395 bool SimpleIndex::IsIndexFileStale(const base::FilePath& index_filename) { | 394 bool SimpleIndex::IsIndexFileStale(const base::FilePath& index_filename) { |
| 396 base::Time index_mtime; | 395 base::Time index_mtime; |
| 397 base::Time dir_mtime; | 396 base::Time dir_mtime; |
| 398 if (!GetMTime(index_filename.DirName(), &dir_mtime)) | 397 if (!GetMTime(index_filename.DirName(), &dir_mtime)) |
| 399 return true; | 398 return true; |
| 400 if (!GetMTime(index_filename, &index_mtime)) | 399 if (!GetMTime(index_filename, &index_mtime)) |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 446 base::Bind(completion_callback, | 445 base::Bind(completion_callback, |
| 447 base::Passed(&index_file_entries), | 446 base::Passed(&index_file_entries), |
| 448 force_index_flush)); | 447 force_index_flush)); |
| 449 } | 448 } |
| 450 | 449 |
| 451 // static | 450 // static |
| 452 scoped_ptr<SimpleIndex::EntrySet> SimpleIndex::RestoreFromDisk( | 451 scoped_ptr<SimpleIndex::EntrySet> SimpleIndex::RestoreFromDisk( |
| 453 const base::FilePath& index_filename) { | 452 const base::FilePath& index_filename) { |
| 454 using file_util::FileEnumerator; | 453 using file_util::FileEnumerator; |
| 455 LOG(INFO) << "Simple Cache Index is being restored from disk."; | 454 LOG(INFO) << "Simple Cache Index is being restored from disk."; |
| 455 |
| 456 file_util::Delete(index_filename, /* recursive = */ false); |
| 456 scoped_ptr<EntrySet> index_file_entries(new EntrySet()); | 457 scoped_ptr<EntrySet> index_file_entries(new EntrySet()); |
| 457 | 458 |
| 458 // TODO(felipeg,gavinp): Fix this once we have a one-file per entry format. | 459 // TODO(felipeg,gavinp): Fix this once we have a one-file per entry format. |
| 459 COMPILE_ASSERT(kSimpleEntryFileCount == 3, | 460 COMPILE_ASSERT(kSimpleEntryFileCount == 3, |
| 460 file_pattern_must_match_file_count); | 461 file_pattern_must_match_file_count); |
| 461 | 462 |
| 462 const int kFileSuffixLenght = std::string("_0").size(); | 463 const int kFileSuffixLenght = std::string("_0").size(); |
| 463 const base::FilePath::StringType file_pattern = FILE_PATH_LITERAL("*_[0-2]"); | 464 const base::FilePath::StringType file_pattern = FILE_PATH_LITERAL("*_[0-2]"); |
| 464 FileEnumerator enumerator(index_filename.DirName(), | 465 FileEnumerator enumerator(index_filename.DirName(), |
| 465 false /* recursive */, | 466 false /* recursive */, |
| 466 FileEnumerator::FILES, | 467 FileEnumerator::FILES, |
| 467 file_pattern); | 468 file_pattern); |
| 468 for (base::FilePath file_path = enumerator.Next(); !file_path.empty(); | 469 for (base::FilePath file_path = enumerator.Next(); !file_path.empty(); |
| 469 file_path = enumerator.Next()) { | 470 file_path = enumerator.Next()) { |
| 470 const base::FilePath::StringType base_name = file_path.BaseName().value(); | 471 const base::FilePath::StringType base_name = file_path.BaseName().value(); |
| 471 // Converting to std::string is OK since we never use UTF8 wide chars in our | 472 // Converting to std::string is OK since we never use UTF8 wide chars in our |
| 472 // file names. | 473 // file names. |
| 473 const std::string hash_name(base_name.begin(), base_name.end()); | 474 const std::string hash_name(base_name.begin(), base_name.end()); |
| 474 const std::string hash_key_string = | 475 const std::string hash_key_string = |
| 475 hash_name.substr(0, hash_name.size() - kFileSuffixLenght); | 476 hash_name.substr(0, hash_name.size() - kFileSuffixLenght); |
| 476 uint64 hash_key = 0; | 477 uint64 hash_key = 0; |
| 477 if (!simple_util::GetEntryHashKeyFromHexString( | 478 if (!simple_util::GetEntryHashKeyFromHexString( |
| 478 hash_key_string, &hash_key)) { | 479 hash_key_string, &hash_key)) { |
| 479 LOG(WARNING) << "Invalid Entry Hash Key filename while restoring " | 480 LOG(WARNING) << "Invalid Entry Hash Key filename while restoring " |
| 480 << "Simple Index from disk: " << hash_name; | 481 << "Simple Index from disk: " << hash_name; |
| 482 // TODO(felipeg): Should we delete the invalid file here ? |
| 481 continue; | 483 continue; |
| 482 } | 484 } |
| 483 | 485 |
| 484 FileEnumerator::FindInfo find_info = {}; | 486 FileEnumerator::FindInfo find_info = {}; |
| 485 enumerator.GetFindInfo(&find_info); | 487 enumerator.GetFindInfo(&find_info); |
| 486 base::Time last_used_time; | 488 base::Time last_used_time; |
| 487 #if defined(OS_POSIX) | 489 #if defined(OS_POSIX) |
| 488 // For POSIX systems, a last access time is available. However, it's not | 490 // For POSIX systems, a last access time is available. However, it's not |
| 489 // guaranteed to be more accurate than mtime. It is no worse though. | 491 // guaranteed to be more accurate than mtime. It is no worse though. |
| 490 last_used_time = base::Time::FromTimeT(find_info.stat.st_atime); | 492 last_used_time = base::Time::FromTimeT(find_info.stat.st_atime); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 537 if (current_entry != entries_set_.end()) { | 539 if (current_entry != entries_set_.end()) { |
| 538 // When Merging, existing valid data in the |current_entry| will prevail. | 540 // When Merging, existing valid data in the |current_entry| will prevail. |
| 539 cache_size_ -= current_entry->second.GetEntrySize(); | 541 cache_size_ -= current_entry->second.GetEntrySize(); |
| 540 current_entry->second.MergeWith(it->second); | 542 current_entry->second.MergeWith(it->second); |
| 541 cache_size_ += current_entry->second.GetEntrySize(); | 543 cache_size_ += current_entry->second.GetEntrySize(); |
| 542 } else { | 544 } else { |
| 543 InsertInEntrySet(it->second, &entries_set_); | 545 InsertInEntrySet(it->second, &entries_set_); |
| 544 cache_size_ += it->second.GetEntrySize(); | 546 cache_size_ += it->second.GetEntrySize(); |
| 545 } | 547 } |
| 546 } | 548 } |
| 549 last_write_to_disk_ = base::Time::Now(); |
| 547 initialized_ = true; | 550 initialized_ = true; |
| 548 removed_entries_.clear(); | 551 removed_entries_.clear(); |
| 549 | 552 |
| 550 // The actual IO is asynchronous, so calling WriteToDisk() shouldn't slow down | 553 // The actual IO is asynchronous, so calling WriteToDisk() shouldn't slow down |
| 551 // much the merge. | 554 // much the merge. |
| 552 if (force_index_flush) | 555 if (force_index_flush) |
| 553 WriteToDisk(); | 556 WriteToDisk(); |
| 554 | 557 |
| 555 UMA_HISTOGRAM_CUSTOM_COUNTS("SimpleCache.IndexInitializationWaiters", | 558 UMA_HISTOGRAM_CUSTOM_COUNTS("SimpleCache.IndexInitializationWaiters", |
| 556 to_run_when_initialized_.size(), 0, 100, 20); | 559 to_run_when_initialized_.size(), 0, 100, 20); |
| 557 // Run all callbacks waiting for the index to come up. | 560 // Run all callbacks waiting for the index to come up. |
| 558 for (CallbackList::iterator it = to_run_when_initialized_.begin(), | 561 for (CallbackList::iterator it = to_run_when_initialized_.begin(), |
| 559 end = to_run_when_initialized_.end(); it != end; ++it) { | 562 end = to_run_when_initialized_.end(); it != end; ++it) { |
| 560 io_thread_->PostTask(FROM_HERE, base::Bind((*it), net::OK)); | 563 io_thread_->PostTask(FROM_HERE, base::Bind((*it), net::OK)); |
| 561 } | 564 } |
| 562 to_run_when_initialized_.clear(); | 565 to_run_when_initialized_.clear(); |
| 563 } | 566 } |
| 564 | 567 |
| 565 #if defined(OS_ANDROID) | |
| 566 void SimpleIndex::ActivityStatusChanged( | |
| 567 net::SimpleCacheActivityStatusNotifier::ActivityStatus activity_status) { | |
| 568 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
| 569 // For more info about android activities, see: | |
| 570 // developer.android.com/training/basics/activity-lifecycle/pausing.html | |
| 571 // These values are defined in the file ActivityStatus.java | |
| 572 if (activity_status == net::SimpleCacheActivityStatusNotifier::RESUMED) { | |
| 573 app_on_background_ = false; | |
| 574 } else if (activity_status == | |
| 575 net::SimpleCacheActivityStatusNotifier::STOPPED) { | |
| 576 app_on_background_ = true; | |
| 577 WriteToDisk(); | |
| 578 } | |
| 579 } | |
| 580 #endif | |
| 581 | |
| 582 void SimpleIndex::WriteToDisk() { | 568 void SimpleIndex::WriteToDisk() { |
| 583 DCHECK(io_thread_checker_.CalledOnValidThread()); | 569 DCHECK(io_thread_checker_.CalledOnValidThread()); |
| 584 if (!initialized_) | 570 if (!initialized_) |
| 585 return; | 571 return; |
| 586 UMA_HISTOGRAM_CUSTOM_COUNTS("SimpleCache.IndexNumEntriesOnWrite", | 572 UMA_HISTOGRAM_CUSTOM_COUNTS("SimpleCache.IndexNumEntriesOnWrite", |
| 587 entries_set_.size(), 0, 100000, 50); | 573 entries_set_.size(), 0, 100000, 50); |
| 588 const base::TimeTicks start = base::TimeTicks::Now(); | 574 const base::TimeTicks start = base::TimeTicks::Now(); |
| 575 last_write_to_disk_ = base::Time::Now(); |
| 589 SimpleIndexFile::IndexMetadata index_metadata(entries_set_.size(), | 576 SimpleIndexFile::IndexMetadata index_metadata(entries_set_.size(), |
| 590 cache_size_); | 577 cache_size_); |
| 591 scoped_ptr<Pickle> pickle = SimpleIndexFile::Serialize(index_metadata, | 578 scoped_ptr<Pickle> pickle = SimpleIndexFile::Serialize(index_metadata, |
| 592 entries_set_); | 579 entries_set_); |
| 593 cache_thread_->PostTask(FROM_HERE, base::Bind( | 580 cache_thread_->PostTask(FROM_HERE, base::Bind( |
| 594 &SimpleIndex::WriteToDiskInternal, | 581 &SimpleIndex::WriteToDiskInternal, |
| 595 index_filename_, | 582 index_filename_, |
| 596 base::Passed(&pickle), | 583 base::Passed(&pickle), |
| 597 start)); | 584 start)); |
| 598 } | 585 } |
| 599 | 586 |
| 600 } // namespace disk_cache | 587 } // namespace disk_cache |
| OLD | NEW |