| 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" |
| 11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/message_loop.h" | 13 #include "base/message_loop.h" |
| 14 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
| 15 #include "base/pickle.h" | 15 #include "base/pickle.h" |
| 16 #include "base/sys_info.h" | 16 #include "base/sys_info.h" |
| 17 #include "base/task_runner.h" | 17 #include "base/task_runner.h" |
| 18 #include "base/threading/worker_pool.h" | 18 #include "base/threading/worker_pool.h" |
| 19 #include "net/base/net_errors.h" | 19 #include "net/base/net_errors.h" |
| 20 #include "net/disk_cache/backend_impl.h" | 20 #include "net/disk_cache/backend_impl.h" |
| 21 #include "net/disk_cache/simple/simple_entry_format.h" | 21 #include "net/disk_cache/simple/simple_entry_format.h" |
| 22 #include "net/disk_cache/simple/simple_index_file.h" | 22 #include "net/disk_cache/simple/simple_index_file.h" |
| 23 #include "net/disk_cache/simple/simple_synchronous_entry.h" | 23 #include "net/disk_cache/simple/simple_synchronous_entry.h" |
| 24 #include "net/disk_cache/simple/simple_util.h" | 24 #include "net/disk_cache/simple/simple_util.h" |
| 25 | 25 |
| 26 namespace { | 26 namespace { |
| 27 | 27 |
| 28 // How many seconds we delay writing the index to disk since the last cache | 28 // How many seconds we delay writing the index to disk since the last cache |
| 29 // operation has happened. | 29 // operation has happened. |
| 30 const int kWriteToDiskDelaySecs = 20; | 30 const int kWriteToDiskDelayMSecs = 20000; |
| 31 | 31 const int kWriteToDiskOnBackgroundDelayMSecs = 100; |
| 32 // WriteToDisk at lest every 5 minutes. | |
| 33 const int kMaxWriteToDiskDelaySecs = 300; | |
| 34 | 32 |
| 35 // Cache size when all other size heuristics failed. | 33 // Cache size when all other size heuristics failed. |
| 36 const uint64 kDefaultCacheSize = 80 * 1024 * 1024; | 34 const uint64 kDefaultCacheSize = 80 * 1024 * 1024; |
| 37 | 35 |
| 38 // Divides the cache space into this amount of parts to evict when only one part | 36 // Divides the cache space into this amount of parts to evict when only one part |
| 39 // is left. | 37 // is left. |
| 40 const uint32 kEvictionMarginDivisor = 20; | 38 const uint32 kEvictionMarginDivisor = 20; |
| 41 | 39 |
| 42 // Utility class used for timestamp comparisons in entry metadata while sorting. | 40 // Utility class used for timestamp comparisons in entry metadata while sorting. |
| 43 class CompareHashesForTimestamp { | 41 class CompareHashesForTimestamp { |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 const base::FilePath& path, | 117 const base::FilePath& path, |
| 120 int max_size) | 118 int max_size) |
| 121 : cache_size_(0), | 119 : cache_size_(0), |
| 122 max_size_(max_size), | 120 max_size_(max_size), |
| 123 high_watermark_(0), | 121 high_watermark_(0), |
| 124 low_watermark_(0), | 122 low_watermark_(0), |
| 125 eviction_in_progress_(false), | 123 eviction_in_progress_(false), |
| 126 initialized_(false), | 124 initialized_(false), |
| 127 index_filename_(path.AppendASCII("simple-index")), | 125 index_filename_(path.AppendASCII("simple-index")), |
| 128 cache_thread_(cache_thread), | 126 cache_thread_(cache_thread), |
| 129 io_thread_(io_thread) { | 127 io_thread_(io_thread), |
| 128 #if defined(OS_ANDROID) |
| 129 activity_status_notifier_( |
| 130 base::Bind(&SimpleIndex::ActivityStatusChanged, AsWeakPtr())), |
| 131 #endif |
| 132 app_on_background_(false) { |
| 130 } | 133 } |
| 131 | 134 |
| 132 SimpleIndex::~SimpleIndex() { | 135 SimpleIndex::~SimpleIndex() { |
| 133 DCHECK(io_thread_checker_.CalledOnValidThread()); | 136 DCHECK(io_thread_checker_.CalledOnValidThread()); |
| 134 | 137 |
| 135 // Fail all callbacks waiting for the index to come up. | 138 // Fail all callbacks waiting for the index to come up. |
| 136 for (CallbackList::iterator it = to_run_when_initialized_.begin(), | 139 for (CallbackList::iterator it = to_run_when_initialized_.begin(), |
| 137 end = to_run_when_initialized_.end(); it != end; ++it) { | 140 end = to_run_when_initialized_.end(); it != end; ++it) { |
| 138 it->Run(net::ERR_ABORTED); | 141 it->Run(net::ERR_ABORTED); |
| 139 } | 142 } |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 328 const disk_cache::EntryMetadata& entry_metadata, | 331 const disk_cache::EntryMetadata& entry_metadata, |
| 329 EntrySet* entry_set) { | 332 EntrySet* entry_set) { |
| 330 DCHECK(entry_set); | 333 DCHECK(entry_set); |
| 331 entry_set->insert( | 334 entry_set->insert( |
| 332 std::make_pair(entry_metadata.GetHashKey(), entry_metadata)); | 335 std::make_pair(entry_metadata.GetHashKey(), entry_metadata)); |
| 333 } | 336 } |
| 334 | 337 |
| 335 void SimpleIndex::PostponeWritingToDisk() { | 338 void SimpleIndex::PostponeWritingToDisk() { |
| 336 if (!initialized_) | 339 if (!initialized_) |
| 337 return; | 340 return; |
| 338 const base::TimeDelta file_age = base::Time::Now() - last_write_to_disk_; | 341 int delay = kWriteToDiskDelayMSecs; |
| 339 if (file_age > base::TimeDelta::FromSeconds(kMaxWriteToDiskDelaySecs) && | 342 if (app_on_background_) { |
| 340 write_to_disk_timer_.IsRunning()) { | 343 // When the app is in the background we can write the index much more |
| 341 // If the index file is too old and there is a timer programmed to run a | 344 // frequently. We could even write it to disk on every operation if we |
| 342 // WriteToDisk soon, we don't postpone it, so we always WriteToDisk | 345 // wanted to. |
| 343 // approximately every kMaxWriteToDiskDelaySecs. | 346 delay = kWriteToDiskOnBackgroundDelayMSecs; |
| 344 return; | |
| 345 } | 347 } |
| 346 | |
| 347 // If the timer is already active, Start() will just Reset it, postponing it. | 348 // If the timer is already active, Start() will just Reset it, postponing it. |
| 348 write_to_disk_timer_.Start( | 349 write_to_disk_timer_.Start( |
| 349 FROM_HERE, | 350 FROM_HERE, |
| 350 base::TimeDelta::FromSeconds(kWriteToDiskDelaySecs), | 351 base::TimeDelta::FromMilliseconds(delay), |
| 351 base::Bind(&SimpleIndex::WriteToDisk, AsWeakPtr())); | 352 base::Bind(&SimpleIndex::WriteToDisk, AsWeakPtr())); |
| 352 } | 353 } |
| 353 | 354 |
| 354 // static | 355 // static |
| 355 bool SimpleIndex::IsIndexFileStale(const base::FilePath& index_filename) { | 356 bool SimpleIndex::IsIndexFileStale(const base::FilePath& index_filename) { |
| 356 base::PlatformFileInfo dir_info; | 357 base::PlatformFileInfo dir_info; |
| 357 base::PlatformFileInfo index_info; | 358 base::PlatformFileInfo index_info; |
| 358 if (!file_util::GetFileInfo(index_filename.DirName(), &dir_info)) | 359 if (!file_util::GetFileInfo(index_filename.DirName(), &dir_info)) |
| 359 return false; | 360 return false; |
| 360 DCHECK(dir_info.is_directory); | 361 DCHECK(dir_info.is_directory); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 391 base::Bind(completion_callback, | 392 base::Bind(completion_callback, |
| 392 base::Passed(&index_file_entries), | 393 base::Passed(&index_file_entries), |
| 393 force_index_flush)); | 394 force_index_flush)); |
| 394 } | 395 } |
| 395 | 396 |
| 396 // static | 397 // static |
| 397 scoped_ptr<SimpleIndex::EntrySet> SimpleIndex::RestoreFromDisk( | 398 scoped_ptr<SimpleIndex::EntrySet> SimpleIndex::RestoreFromDisk( |
| 398 const base::FilePath& index_filename) { | 399 const base::FilePath& index_filename) { |
| 399 using file_util::FileEnumerator; | 400 using file_util::FileEnumerator; |
| 400 LOG(INFO) << "Simple Cache Index is being restored from disk."; | 401 LOG(INFO) << "Simple Cache Index is being restored from disk."; |
| 401 | |
| 402 file_util::Delete(index_filename, /* recursive = */ false); | |
| 403 scoped_ptr<EntrySet> index_file_entries(new EntrySet()); | 402 scoped_ptr<EntrySet> index_file_entries(new EntrySet()); |
| 404 | 403 |
| 405 // TODO(felipeg,gavinp): Fix this once we have a one-file per entry format. | 404 // TODO(felipeg,gavinp): Fix this once we have a one-file per entry format. |
| 406 COMPILE_ASSERT(kSimpleEntryFileCount == 3, | 405 COMPILE_ASSERT(kSimpleEntryFileCount == 3, |
| 407 file_pattern_must_match_file_count); | 406 file_pattern_must_match_file_count); |
| 408 | 407 |
| 409 const int kFileSuffixLenght = std::string("_0").size(); | 408 const int kFileSuffixLenght = std::string("_0").size(); |
| 410 const base::FilePath::StringType file_pattern = FILE_PATH_LITERAL("*_[0-2]"); | 409 const base::FilePath::StringType file_pattern = FILE_PATH_LITERAL("*_[0-2]"); |
| 411 FileEnumerator enumerator(index_filename.DirName(), | 410 FileEnumerator enumerator(index_filename.DirName(), |
| 412 false /* recursive */, | 411 false /* recursive */, |
| 413 FileEnumerator::FILES, | 412 FileEnumerator::FILES, |
| 414 file_pattern); | 413 file_pattern); |
| 415 for (base::FilePath file_path = enumerator.Next(); !file_path.empty(); | 414 for (base::FilePath file_path = enumerator.Next(); !file_path.empty(); |
| 416 file_path = enumerator.Next()) { | 415 file_path = enumerator.Next()) { |
| 417 const base::FilePath::StringType base_name = file_path.BaseName().value(); | 416 const base::FilePath::StringType base_name = file_path.BaseName().value(); |
| 418 // Converting to std::string is OK since we never use UTF8 wide chars in our | 417 // Converting to std::string is OK since we never use UTF8 wide chars in our |
| 419 // file names. | 418 // file names. |
| 420 const std::string hash_name(base_name.begin(), base_name.end()); | 419 const std::string hash_name(base_name.begin(), base_name.end()); |
| 421 const std::string hash_key_string = | 420 const std::string hash_key_string = |
| 422 hash_name.substr(0, hash_name.size() - kFileSuffixLenght); | 421 hash_name.substr(0, hash_name.size() - kFileSuffixLenght); |
| 423 uint64 hash_key = 0; | 422 uint64 hash_key = 0; |
| 424 if (!simple_util::GetEntryHashKeyFromHexString( | 423 if (!simple_util::GetEntryHashKeyFromHexString( |
| 425 hash_key_string, &hash_key)) { | 424 hash_key_string, &hash_key)) { |
| 426 LOG(WARNING) << "Invalid Entry Hash Key filename while restoring " | 425 LOG(WARNING) << "Invalid Entry Hash Key filename while restoring " |
| 427 << "Simple Index from disk: " << hash_name; | 426 << "Simple Index from disk: " << hash_name; |
| 428 // TODO(felipeg): Should we delete the invalid file here ? | |
| 429 continue; | 427 continue; |
| 430 } | 428 } |
| 431 | 429 |
| 432 FileEnumerator::FindInfo find_info = {}; | 430 FileEnumerator::FindInfo find_info = {}; |
| 433 enumerator.GetFindInfo(&find_info); | 431 enumerator.GetFindInfo(&find_info); |
| 434 base::Time last_used_time; | 432 base::Time last_used_time; |
| 435 #if defined(OS_POSIX) | 433 #if defined(OS_POSIX) |
| 436 // For POSIX systems, a last access time is available. However, it's not | 434 // For POSIX systems, a last access time is available. However, it's not |
| 437 // guaranteed to be more accurate than mtime. It is no worse though. | 435 // guaranteed to be more accurate than mtime. It is no worse though. |
| 438 last_used_time = base::Time::FromTimeT(find_info.stat.st_atime); | 436 last_used_time = base::Time::FromTimeT(find_info.stat.st_atime); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 481 EntrySet::iterator current_entry = entries_set_.find(it->first); | 479 EntrySet::iterator current_entry = entries_set_.find(it->first); |
| 482 if (current_entry != entries_set_.end()) { | 480 if (current_entry != entries_set_.end()) { |
| 483 // When Merging, existing valid data in the |current_entry| will prevail. | 481 // When Merging, existing valid data in the |current_entry| will prevail. |
| 484 current_entry->second.MergeWith(it->second); | 482 current_entry->second.MergeWith(it->second); |
| 485 cache_size_ += current_entry->second.GetEntrySize(); | 483 cache_size_ += current_entry->second.GetEntrySize(); |
| 486 } else { | 484 } else { |
| 487 InsertInEntrySet(it->second, &entries_set_); | 485 InsertInEntrySet(it->second, &entries_set_); |
| 488 cache_size_ += it->second.GetEntrySize(); | 486 cache_size_ += it->second.GetEntrySize(); |
| 489 } | 487 } |
| 490 } | 488 } |
| 491 last_write_to_disk_ = base::Time::Now(); | |
| 492 initialized_ = true; | 489 initialized_ = true; |
| 493 removed_entries_.clear(); | 490 removed_entries_.clear(); |
| 494 | 491 |
| 495 // The actual IO is asynchronous, so calling WriteToDisk() shouldn't slow down | 492 // The actual IO is asynchronous, so calling WriteToDisk() shouldn't slow down |
| 496 // much the merge. | 493 // much the merge. |
| 497 if (force_index_flush) | 494 if (force_index_flush) |
| 498 WriteToDisk(); | 495 WriteToDisk(); |
| 499 | 496 |
| 500 // Run all callbacks waiting for the index to come up. | 497 // Run all callbacks waiting for the index to come up. |
| 501 for (CallbackList::iterator it = to_run_when_initialized_.begin(), | 498 for (CallbackList::iterator it = to_run_when_initialized_.begin(), |
| 502 end = to_run_when_initialized_.end(); it != end; ++it) { | 499 end = to_run_when_initialized_.end(); it != end; ++it) { |
| 503 io_thread_->PostTask(FROM_HERE, base::Bind((*it), net::OK)); | 500 io_thread_->PostTask(FROM_HERE, base::Bind((*it), net::OK)); |
| 504 } | 501 } |
| 505 to_run_when_initialized_.clear(); | 502 to_run_when_initialized_.clear(); |
| 506 } | 503 } |
| 507 | 504 |
| 505 #if defined(OS_ANDROID) |
| 506 void SimpleIndex::ActivityStatusChanged( |
| 507 net::SimpleCacheActivityStatusNotifier::ActivityStatus activity_status) { |
| 508 DCHECK(io_thread_checker_.CalledOnValidThread()); |
| 509 // For more info about android activities, see: |
| 510 // developer.android.com/training/basics/activity-lifecycle/pausing.html |
| 511 // These values are defined in the file ActivityStatus.java |
| 512 if (activity_status == net::SimpleCacheActivityStatusNotifier::RESUMED) { |
| 513 app_on_background_ = false; |
| 514 } else if (activity_status == |
| 515 net::SimpleCacheActivityStatusNotifier::STOPPED) { |
| 516 app_on_background_ = true; |
| 517 WriteToDisk(); |
| 518 } |
| 519 } |
| 520 #endif |
| 521 |
| 508 void SimpleIndex::WriteToDisk() { | 522 void SimpleIndex::WriteToDisk() { |
| 509 DCHECK(io_thread_checker_.CalledOnValidThread()); | 523 DCHECK(io_thread_checker_.CalledOnValidThread()); |
| 510 if (!initialized_) | 524 if (!initialized_) |
| 511 return; | 525 return; |
| 512 last_write_to_disk_ = base::Time::Now(); | |
| 513 SimpleIndexFile::IndexMetadata index_metadata(entries_set_.size(), | 526 SimpleIndexFile::IndexMetadata index_metadata(entries_set_.size(), |
| 514 cache_size_); | 527 cache_size_); |
| 515 scoped_ptr<Pickle> pickle = SimpleIndexFile::Serialize(index_metadata, | 528 scoped_ptr<Pickle> pickle = SimpleIndexFile::Serialize(index_metadata, |
| 516 entries_set_); | 529 entries_set_); |
| 517 cache_thread_->PostTask(FROM_HERE, base::Bind( | 530 cache_thread_->PostTask(FROM_HERE, base::Bind( |
| 518 &SimpleIndex::WriteToDiskInternal, | 531 &SimpleIndex::WriteToDiskInternal, |
| 519 index_filename_, | 532 index_filename_, |
| 520 base::Passed(&pickle))); | 533 base::Passed(&pickle))); |
| 521 } | 534 } |
| 522 | 535 |
| 523 } // namespace disk_cache | 536 } // namespace disk_cache |
| OLD | NEW |