Home | History | Annotate | Download | only in simple
      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
      3 // found in the LICENSE file.
      4 
      5 #include "net/disk_cache/simple/simple_backend_impl.h"
      6 
      7 #include <algorithm>
      8 #include <cstdlib>
      9 
     10 #if defined(OS_POSIX)
     11 #include <sys/resource.h>
     12 #endif
     13 
     14 #include "base/bind.h"
     15 #include "base/callback.h"
     16 #include "base/file_util.h"
     17 #include "base/location.h"
     18 #include "base/message_loop/message_loop_proxy.h"
     19 #include "base/metrics/field_trial.h"
     20 #include "base/metrics/histogram.h"
     21 #include "base/metrics/sparse_histogram.h"
     22 #include "base/single_thread_task_runner.h"
     23 #include "base/sys_info.h"
     24 #include "base/task_runner_util.h"
     25 #include "base/threading/sequenced_worker_pool.h"
     26 #include "base/time/time.h"
     27 #include "net/base/net_errors.h"
     28 #include "net/disk_cache/backend_impl.h"
     29 #include "net/disk_cache/simple/simple_entry_format.h"
     30 #include "net/disk_cache/simple/simple_entry_impl.h"
     31 #include "net/disk_cache/simple/simple_index.h"
     32 #include "net/disk_cache/simple/simple_index_file.h"
     33 #include "net/disk_cache/simple/simple_synchronous_entry.h"
     34 #include "net/disk_cache/simple/simple_util.h"
     35 
     36 using base::Closure;
     37 using base::FilePath;
     38 using base::MessageLoopProxy;
     39 using base::SequencedWorkerPool;
     40 using base::SingleThreadTaskRunner;
     41 using base::Time;
     42 using base::DirectoryExists;
     43 using file_util::CreateDirectory;
     44 
     45 namespace {
     46 
     47 // Maximum number of concurrent worker pool threads, which also is the limit
     48 // on concurrent IO (as we use one thread per IO request).
     49 const int kDefaultMaxWorkerThreads = 50;
     50 
     51 const char kThreadNamePrefix[] = "SimpleCache";
     52 
     53 // Cache size when all other size heuristics failed.
     54 const uint64 kDefaultCacheSize = 80 * 1024 * 1024;
     55 
     56 // Maximum fraction of the cache that one entry can consume.
     57 const int kMaxFileRatio = 8;
     58 
     59 // A global sequenced worker pool to use for launching all tasks.
     60 SequencedWorkerPool* g_sequenced_worker_pool = NULL;
     61 
     62 void MaybeCreateSequencedWorkerPool() {
     63   if (!g_sequenced_worker_pool) {
     64     int max_worker_threads = kDefaultMaxWorkerThreads;
     65 
     66     const std::string thread_count_field_trial =
     67         base::FieldTrialList::FindFullName("SimpleCacheMaxThreads");
     68     if (!thread_count_field_trial.empty()) {
     69       max_worker_threads =
     70           std::max(1, std::atoi(thread_count_field_trial.c_str()));
     71     }
     72 
     73     g_sequenced_worker_pool = new SequencedWorkerPool(max_worker_threads,
     74                                                       kThreadNamePrefix);
     75     g_sequenced_worker_pool->AddRef();  // Leak it.
     76   }
     77 }
     78 
     79 bool g_fd_limit_histogram_has_been_populated = false;
     80 
     81 void MaybeHistogramFdLimit() {
     82   if (g_fd_limit_histogram_has_been_populated)
     83     return;
     84 
     85   // Used in histograms; add new entries at end.
     86   enum FdLimitStatus {
     87     FD_LIMIT_STATUS_UNSUPPORTED = 0,
     88     FD_LIMIT_STATUS_FAILED      = 1,
     89     FD_LIMIT_STATUS_SUCCEEDED   = 2,
     90     FD_LIMIT_STATUS_MAX         = 3
     91   };
     92   FdLimitStatus fd_limit_status = FD_LIMIT_STATUS_UNSUPPORTED;
     93   int soft_fd_limit = 0;
     94   int hard_fd_limit = 0;
     95 
     96 #if defined(OS_POSIX)
     97   struct rlimit nofile;
     98   if (!getrlimit(RLIMIT_NOFILE, &nofile)) {
     99     soft_fd_limit = nofile.rlim_cur;
    100     hard_fd_limit = nofile.rlim_max;
    101     fd_limit_status = FD_LIMIT_STATUS_SUCCEEDED;
    102   } else {
    103     fd_limit_status = FD_LIMIT_STATUS_FAILED;
    104   }
    105 #endif
    106 
    107   UMA_HISTOGRAM_ENUMERATION("SimpleCache.FileDescriptorLimitStatus",
    108                             fd_limit_status, FD_LIMIT_STATUS_MAX);
    109   if (fd_limit_status == FD_LIMIT_STATUS_SUCCEEDED) {
    110     UMA_HISTOGRAM_SPARSE_SLOWLY("SimpleCache.FileDescriptorLimitSoft",
    111                                 soft_fd_limit);
    112     UMA_HISTOGRAM_SPARSE_SLOWLY("SimpleCache.FileDescriptorLimitHard",
    113                                 hard_fd_limit);
    114   }
    115 
    116   g_fd_limit_histogram_has_been_populated = true;
    117 }
    118 
    119 // Must run on IO Thread.
    120 void DeleteBackendImpl(disk_cache::Backend** backend,
    121                        const net::CompletionCallback& callback,
    122                        int result) {
    123   DCHECK(*backend);
    124   delete *backend;
    125   *backend = NULL;
    126   callback.Run(result);
    127 }
    128 
    129 // Detects if the files in the cache directory match the current disk cache
    130 // backend type and version. If the directory contains no cache, occupies it
    131 // with the fresh structure.
    132 //
    133 // There is a convention among disk cache backends: looking at the magic in the
    134 // file "index" it should be sufficient to determine if the cache belongs to the
    135 // currently running backend. The Simple Backend stores its index in the file
    136 // "the-real-index" (see simple_index.cc) and the file "index" only signifies
    137 // presence of the implementation's magic and version. There are two reasons for
    138 // that:
    139 // 1. Absence of the index is itself not a fatal error in the Simple Backend
    140 // 2. The Simple Backend has pickled file format for the index making it hacky
    141 //    to have the magic in the right place.
    142 bool FileStructureConsistent(const base::FilePath& path) {
    143   if (!base::PathExists(path) && !file_util::CreateDirectory(path)) {
    144     LOG(ERROR) << "Failed to create directory: " << path.LossyDisplayName();
    145     return false;
    146   }
    147   const base::FilePath fake_index = path.AppendASCII("index");
    148   base::PlatformFileError error;
    149   base::PlatformFile fake_index_file = base::CreatePlatformFile(
    150       fake_index,
    151       base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
    152       NULL,
    153       &error);
    154   if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
    155     base::PlatformFile file = base::CreatePlatformFile(
    156         fake_index,
    157         base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
    158         NULL, &error);
    159     disk_cache::SimpleFileHeader file_contents;
    160     file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber;
    161     file_contents.version = disk_cache::kSimpleVersion;
    162     int bytes_written = base::WritePlatformFile(
    163         file, 0, reinterpret_cast<char*>(&file_contents),
    164         sizeof(file_contents));
    165     if (!base::ClosePlatformFile(file) ||
    166         bytes_written != sizeof(file_contents)) {
    167       LOG(ERROR) << "Failed to write cache structure file: "
    168                  << path.LossyDisplayName();
    169       return false;
    170     }
    171     return true;
    172   } else if (error != base::PLATFORM_FILE_OK) {
    173     LOG(ERROR) << "Could not open cache structure file: "
    174                << path.LossyDisplayName();
    175     return false;
    176   } else {
    177     disk_cache::SimpleFileHeader file_header;
    178     int bytes_read = base::ReadPlatformFile(
    179         fake_index_file, 0, reinterpret_cast<char*>(&file_header),
    180         sizeof(file_header));
    181     if (!base::ClosePlatformFile(fake_index_file) ||
    182         bytes_read != sizeof(file_header) ||
    183         file_header.initial_magic_number !=
    184             disk_cache::kSimpleInitialMagicNumber ||
    185         file_header.version != disk_cache::kSimpleVersion) {
    186       LOG(ERROR) << "File structure does not match the disk cache backend.";
    187       return false;
    188     }
    189     return true;
    190   }
    191 }
    192 
    193 void CallCompletionCallback(const net::CompletionCallback& callback,
    194                             int error_code) {
    195   DCHECK(!callback.is_null());
    196   callback.Run(error_code);
    197 }
    198 
    199 void RecordIndexLoad(base::TimeTicks constructed_since, int result) {
    200   const base::TimeDelta creation_to_index = base::TimeTicks::Now() -
    201                                             constructed_since;
    202   if (result == net::OK)
    203     UMA_HISTOGRAM_TIMES("SimpleCache.CreationToIndex", creation_to_index);
    204   else
    205     UMA_HISTOGRAM_TIMES("SimpleCache.CreationToIndexFail", creation_to_index);
    206 }
    207 
    208 }  // namespace
    209 
    210 namespace disk_cache {
    211 
    212 SimpleBackendImpl::SimpleBackendImpl(const FilePath& path,
    213                                      int max_bytes,
    214                                      net::CacheType type,
    215                                      base::SingleThreadTaskRunner* cache_thread,
    216                                      net::NetLog* net_log)
    217     : path_(path),
    218       cache_thread_(cache_thread),
    219       orig_max_size_(max_bytes),
    220       entry_operations_mode_(
    221           type == net::DISK_CACHE ?
    222               SimpleEntryImpl::OPTIMISTIC_OPERATIONS :
    223               SimpleEntryImpl::NON_OPTIMISTIC_OPERATIONS),
    224       net_log_(net_log) {
    225   MaybeHistogramFdLimit();
    226 }
    227 
    228 SimpleBackendImpl::~SimpleBackendImpl() {
    229   index_->WriteToDisk();
    230 }
    231 
    232 int SimpleBackendImpl::Init(const CompletionCallback& completion_callback) {
    233   MaybeCreateSequencedWorkerPool();
    234 
    235   worker_pool_ = g_sequenced_worker_pool->GetTaskRunnerWithShutdownBehavior(
    236       SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
    237 
    238   index_.reset(
    239       new SimpleIndex(MessageLoopProxy::current().get(),
    240                       path_,
    241                       make_scoped_ptr(new SimpleIndexFile(
    242                           cache_thread_.get(), worker_pool_.get(), path_))));
    243   index_->ExecuteWhenReady(base::Bind(&RecordIndexLoad,
    244                                       base::TimeTicks::Now()));
    245 
    246   PostTaskAndReplyWithResult(
    247       cache_thread_,
    248       FROM_HERE,
    249       base::Bind(&SimpleBackendImpl::InitCacheStructureOnDisk, path_,
    250                  orig_max_size_),
    251       base::Bind(&SimpleBackendImpl::InitializeIndex, AsWeakPtr(),
    252                  completion_callback));
    253   return net::ERR_IO_PENDING;
    254 }
    255 
    256 bool SimpleBackendImpl::SetMaxSize(int max_bytes) {
    257   orig_max_size_ = max_bytes;
    258   return index_->SetMaxSize(max_bytes);
    259 }
    260 
    261 int SimpleBackendImpl::GetMaxFileSize() const {
    262   return index_->max_size() / kMaxFileRatio;
    263 }
    264 
    265 void SimpleBackendImpl::OnDeactivated(const SimpleEntryImpl* entry) {
    266   active_entries_.erase(entry->entry_hash());
    267 }
    268 
    269 net::CacheType SimpleBackendImpl::GetCacheType() const {
    270   return net::DISK_CACHE;
    271 }
    272 
    273 int32 SimpleBackendImpl::GetEntryCount() const {
    274   // TODO(pasko): Use directory file count when index is not ready.
    275   return index_->GetEntryCount();
    276 }
    277 
    278 int SimpleBackendImpl::OpenEntry(const std::string& key,
    279                                  Entry** entry,
    280                                  const CompletionCallback& callback) {
    281   scoped_refptr<SimpleEntryImpl> simple_entry = CreateOrFindActiveEntry(key);
    282   CompletionCallback backend_callback =
    283       base::Bind(&SimpleBackendImpl::OnEntryOpenedFromKey,
    284                  AsWeakPtr(),
    285                  key,
    286                  entry,
    287                  simple_entry,
    288                  callback);
    289   return simple_entry->OpenEntry(entry, backend_callback);
    290 }
    291 
    292 int SimpleBackendImpl::CreateEntry(const std::string& key,
    293                                    Entry** entry,
    294                                    const CompletionCallback& callback) {
    295   DCHECK(key.size() > 0);
    296   scoped_refptr<SimpleEntryImpl> simple_entry = CreateOrFindActiveEntry(key);
    297   return simple_entry->CreateEntry(entry, callback);
    298 }
    299 
    300 int SimpleBackendImpl::DoomEntry(const std::string& key,
    301                                  const net::CompletionCallback& callback) {
    302   scoped_refptr<SimpleEntryImpl> simple_entry = CreateOrFindActiveEntry(key);
    303   return simple_entry->DoomEntry(callback);
    304 }
    305 
    306 int SimpleBackendImpl::DoomAllEntries(const CompletionCallback& callback) {
    307   return DoomEntriesBetween(Time(), Time(), callback);
    308 }
    309 
    310 void SimpleBackendImpl::IndexReadyForDoom(Time initial_time,
    311                                           Time end_time,
    312                                           const CompletionCallback& callback,
    313                                           int result) {
    314   if (result != net::OK) {
    315     callback.Run(result);
    316     return;
    317   }
    318   scoped_ptr<std::vector<uint64> > removed_key_hashes(
    319       index_->RemoveEntriesBetween(initial_time, end_time).release());
    320 
    321   // If any of the entries we are dooming are currently open, we need to remove
    322   // them from |active_entries_|, so that attempts to create new entries will
    323   // succeed and attempts to open them will fail.
    324   for (int i = removed_key_hashes->size() - 1; i >= 0; --i) {
    325     const uint64 entry_hash = (*removed_key_hashes)[i];
    326     EntryMap::iterator it = active_entries_.find(entry_hash);
    327     if (it == active_entries_.end())
    328       continue;
    329     SimpleEntryImpl* entry = it->second.get();
    330     entry->Doom();
    331 
    332     (*removed_key_hashes)[i] = removed_key_hashes->back();
    333     removed_key_hashes->resize(removed_key_hashes->size() - 1);
    334   }
    335 
    336   PostTaskAndReplyWithResult(
    337       worker_pool_, FROM_HERE,
    338       base::Bind(&SimpleSynchronousEntry::DoomEntrySet,
    339                  base::Passed(&removed_key_hashes), path_),
    340       base::Bind(&CallCompletionCallback, callback));
    341 }
    342 
    343 int SimpleBackendImpl::DoomEntriesBetween(
    344     const Time initial_time,
    345     const Time end_time,
    346     const CompletionCallback& callback) {
    347   return index_->ExecuteWhenReady(
    348       base::Bind(&SimpleBackendImpl::IndexReadyForDoom, AsWeakPtr(),
    349                  initial_time, end_time, callback));
    350 }
    351 
    352 int SimpleBackendImpl::DoomEntriesSince(
    353     const Time initial_time,
    354     const CompletionCallback& callback) {
    355   return DoomEntriesBetween(initial_time, Time(), callback);
    356 }
    357 
    358 int SimpleBackendImpl::OpenNextEntry(void** iter,
    359                                      Entry** next_entry,
    360                                      const CompletionCallback& callback) {
    361   CompletionCallback get_next_entry =
    362       base::Bind(&SimpleBackendImpl::GetNextEntryInIterator, AsWeakPtr(), iter,
    363                  next_entry, callback);
    364   return index_->ExecuteWhenReady(get_next_entry);
    365 }
    366 
    367 void SimpleBackendImpl::EndEnumeration(void** iter) {
    368   SimpleIndex::HashList* entry_list =
    369       static_cast<SimpleIndex::HashList*>(*iter);
    370   delete entry_list;
    371   *iter = NULL;
    372 }
    373 
    374 void SimpleBackendImpl::GetStats(
    375     std::vector<std::pair<std::string, std::string> >* stats) {
    376   std::pair<std::string, std::string> item;
    377   item.first = "Cache type";
    378   item.second = "Simple Cache";
    379   stats->push_back(item);
    380 }
    381 
    382 void SimpleBackendImpl::OnExternalCacheHit(const std::string& key) {
    383   index_->UseIfExists(key);
    384 }
    385 
    386 void SimpleBackendImpl::InitializeIndex(const CompletionCallback& callback,
    387                                         const DiskStatResult& result) {
    388   if (result.net_error == net::OK) {
    389     index_->SetMaxSize(result.max_size);
    390     index_->Initialize(result.cache_dir_mtime);
    391   }
    392   callback.Run(result.net_error);
    393 }
    394 
    395 SimpleBackendImpl::DiskStatResult SimpleBackendImpl::InitCacheStructureOnDisk(
    396     const base::FilePath& path,
    397     uint64 suggested_max_size) {
    398   DiskStatResult result;
    399   result.max_size = suggested_max_size;
    400   result.net_error = net::OK;
    401   if (!FileStructureConsistent(path)) {
    402     LOG(ERROR) << "Simple Cache Backend: wrong file structure on disk: "
    403                << path.LossyDisplayName();
    404     result.net_error = net::ERR_FAILED;
    405   } else {
    406     bool mtime_result =
    407         disk_cache::simple_util::GetMTime(path, &result.cache_dir_mtime);
    408     DCHECK(mtime_result);
    409     if (!result.max_size) {
    410       int64 available = base::SysInfo::AmountOfFreeDiskSpace(path);
    411       if (available < 0)
    412         result.max_size = kDefaultCacheSize;
    413       else
    414         // TODO(pasko): Move PreferedCacheSize() to cache_util.h. Also fix the
    415         // spelling.
    416         result.max_size = disk_cache::PreferedCacheSize(available);
    417     }
    418     DCHECK(result.max_size);
    419   }
    420   return result;
    421 }
    422 
    423 scoped_refptr<SimpleEntryImpl> SimpleBackendImpl::CreateOrFindActiveEntry(
    424     const std::string& key) {
    425   const uint64 entry_hash = simple_util::GetEntryHashKey(key);
    426 
    427   std::pair<EntryMap::iterator, bool> insert_result =
    428       active_entries_.insert(std::make_pair(entry_hash,
    429                                             base::WeakPtr<SimpleEntryImpl>()));
    430   EntryMap::iterator& it = insert_result.first;
    431   if (insert_result.second)
    432     DCHECK(!it->second.get());
    433   if (!it->second.get()) {
    434     SimpleEntryImpl* entry = new SimpleEntryImpl(
    435         path_, entry_hash, entry_operations_mode_, this, net_log_);
    436     entry->SetKey(key);
    437     it->second = entry->AsWeakPtr();
    438   }
    439   DCHECK(it->second.get());
    440   // It's possible, but unlikely, that we have an entry hash collision with a
    441   // currently active entry.
    442   if (key != it->second->key()) {
    443     it->second->Doom();
    444     DCHECK_EQ(0U, active_entries_.count(entry_hash));
    445     return CreateOrFindActiveEntry(key);
    446   }
    447   return make_scoped_refptr(it->second.get());
    448 }
    449 
    450 int SimpleBackendImpl::OpenEntryFromHash(uint64 hash,
    451                                          Entry** entry,
    452                                          const CompletionCallback& callback) {
    453   EntryMap::iterator has_active = active_entries_.find(hash);
    454   if (has_active != active_entries_.end())
    455     return OpenEntry(has_active->second->key(), entry, callback);
    456 
    457   scoped_refptr<SimpleEntryImpl> simple_entry =
    458       new SimpleEntryImpl(path_, hash, entry_operations_mode_, this, net_log_);
    459   CompletionCallback backend_callback =
    460       base::Bind(&SimpleBackendImpl::OnEntryOpenedFromHash,
    461                  AsWeakPtr(),
    462                  hash, entry, simple_entry, callback);
    463   return simple_entry->OpenEntry(entry, backend_callback);
    464 }
    465 
    466 void SimpleBackendImpl::GetNextEntryInIterator(
    467     void** iter,
    468     Entry** next_entry,
    469     const CompletionCallback& callback,
    470     int error_code) {
    471   if (error_code != net::OK) {
    472     CallCompletionCallback(callback, error_code);
    473     return;
    474   }
    475   if (*iter == NULL) {
    476     *iter = index()->GetAllHashes().release();
    477   }
    478   SimpleIndex::HashList* entry_list =
    479       static_cast<SimpleIndex::HashList*>(*iter);
    480   while (entry_list->size() > 0) {
    481     uint64 entry_hash = entry_list->back();
    482     entry_list->pop_back();
    483     if (index()->Has(entry_hash)) {
    484       *next_entry = NULL;
    485       CompletionCallback continue_iteration = base::Bind(
    486           &SimpleBackendImpl::CheckIterationReturnValue,
    487           AsWeakPtr(),
    488           iter,
    489           next_entry,
    490           callback);
    491       int error_code_open = OpenEntryFromHash(entry_hash,
    492                                               next_entry,
    493                                               continue_iteration);
    494       if (error_code_open == net::ERR_IO_PENDING)
    495         return;
    496       if (error_code_open != net::ERR_FAILED) {
    497         CallCompletionCallback(callback, error_code_open);
    498         return;
    499       }
    500     }
    501   }
    502   CallCompletionCallback(callback, net::ERR_FAILED);
    503 }
    504 
    505 void SimpleBackendImpl::OnEntryOpenedFromHash(
    506     uint64 hash,
    507     Entry** entry,
    508     scoped_refptr<SimpleEntryImpl> simple_entry,
    509     const CompletionCallback& callback,
    510     int error_code) {
    511   if (error_code != net::OK) {
    512     CallCompletionCallback(callback, error_code);
    513     return;
    514   }
    515   DCHECK(*entry);
    516   std::pair<EntryMap::iterator, bool> insert_result =
    517       active_entries_.insert(std::make_pair(hash,
    518                                             base::WeakPtr<SimpleEntryImpl>()));
    519   EntryMap::iterator& it = insert_result.first;
    520   const bool did_insert = insert_result.second;
    521   if (did_insert) {
    522     // There is no active entry corresponding to this hash. The entry created
    523     // is put in the map of active entries and returned to the caller.
    524     it->second = simple_entry->AsWeakPtr();
    525     CallCompletionCallback(callback, error_code);
    526   } else {
    527     // The entry was made active with the key while the creation from hash
    528     // occurred. The entry created from hash needs to be closed, and the one
    529     // coming from the key returned to the caller.
    530     simple_entry->Close();
    531     it->second->OpenEntry(entry, callback);
    532   }
    533 }
    534 
    535 void SimpleBackendImpl::OnEntryOpenedFromKey(
    536     const std::string key,
    537     Entry** entry,
    538     scoped_refptr<SimpleEntryImpl> simple_entry,
    539     const CompletionCallback& callback,
    540     int error_code) {
    541   int final_code = error_code;
    542   if (final_code == net::OK) {
    543     bool key_matches = key.compare(simple_entry->key()) == 0;
    544     if (!key_matches) {
    545       // TODO(clamy): Add a unit test to check this code path.
    546       DLOG(WARNING) << "Key mismatch on open.";
    547       simple_entry->Doom();
    548       simple_entry->Close();
    549       final_code = net::ERR_FAILED;
    550     } else {
    551       DCHECK_EQ(simple_entry->entry_hash(), simple_util::GetEntryHashKey(key));
    552     }
    553     UMA_HISTOGRAM_BOOLEAN("SimpleCache.KeyMatchedOnOpen", key_matches);
    554   }
    555   CallCompletionCallback(callback, final_code);
    556 }
    557 
    558 void SimpleBackendImpl::CheckIterationReturnValue(
    559     void** iter,
    560     Entry** entry,
    561     const CompletionCallback& callback,
    562     int error_code) {
    563   if (error_code == net::ERR_FAILED) {
    564     OpenNextEntry(iter, entry, callback);
    565     return;
    566   }
    567   CallCompletionCallback(callback, error_code);
    568 }
    569 
    570 }  // namespace disk_cache
    571