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 #include <functional>
     10 
     11 #if defined(OS_POSIX)
     12 #include <sys/resource.h>
     13 #endif
     14 
     15 #include "base/bind.h"
     16 #include "base/callback.h"
     17 #include "base/files/file_util.h"
     18 #include "base/location.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/thread_task_runner_handle.h"
     26 #include "base/threading/sequenced_worker_pool.h"
     27 #include "base/time/time.h"
     28 #include "net/base/net_errors.h"
     29 #include "net/disk_cache/cache_util.h"
     30 #include "net/disk_cache/simple/simple_entry_format.h"
     31 #include "net/disk_cache/simple/simple_entry_impl.h"
     32 #include "net/disk_cache/simple/simple_histogram_macros.h"
     33 #include "net/disk_cache/simple/simple_index.h"
     34 #include "net/disk_cache/simple/simple_index_file.h"
     35 #include "net/disk_cache/simple/simple_synchronous_entry.h"
     36 #include "net/disk_cache/simple/simple_util.h"
     37 #include "net/disk_cache/simple/simple_version_upgrade.h"
     38 
     39 using base::Callback;
     40 using base::Closure;
     41 using base::FilePath;
     42 using base::SequencedWorkerPool;
     43 using base::Time;
     44 using base::DirectoryExists;
     45 using base::CreateDirectory;
     46 
     47 namespace disk_cache {
     48 
     49 namespace {
     50 
     51 // Maximum number of concurrent worker pool threads, which also is the limit
     52 // on concurrent IO (as we use one thread per IO request).
     53 const int kDefaultMaxWorkerThreads = 50;
     54 
     55 const char kThreadNamePrefix[] = "SimpleCache";
     56 
     57 // Maximum fraction of the cache that one entry can consume.
     58 const int kMaxFileRatio = 8;
     59 
     60 // A global sequenced worker pool to use for launching all tasks.
     61 SequencedWorkerPool* g_sequenced_worker_pool = NULL;
     62 
     63 void MaybeCreateSequencedWorkerPool() {
     64   if (!g_sequenced_worker_pool) {
     65     int max_worker_threads = kDefaultMaxWorkerThreads;
     66 
     67     const std::string thread_count_field_trial =
     68         base::FieldTrialList::FindFullName("SimpleCacheMaxThreads");
     69     if (!thread_count_field_trial.empty()) {
     70       max_worker_threads =
     71           std::max(1, std::atoi(thread_count_field_trial.c_str()));
     72     }
     73 
     74     g_sequenced_worker_pool = new SequencedWorkerPool(max_worker_threads,
     75                                                       kThreadNamePrefix);
     76     g_sequenced_worker_pool->AddRef();  // Leak it.
     77   }
     78 }
     79 
     80 bool g_fd_limit_histogram_has_been_populated = false;
     81 
     82 void MaybeHistogramFdLimit(net::CacheType cache_type) {
     83   if (g_fd_limit_histogram_has_been_populated)
     84     return;
     85 
     86   // Used in histograms; add new entries at end.
     87   enum FdLimitStatus {
     88     FD_LIMIT_STATUS_UNSUPPORTED = 0,
     89     FD_LIMIT_STATUS_FAILED      = 1,
     90     FD_LIMIT_STATUS_SUCCEEDED   = 2,
     91     FD_LIMIT_STATUS_MAX         = 3
     92   };
     93   FdLimitStatus fd_limit_status = FD_LIMIT_STATUS_UNSUPPORTED;
     94   int soft_fd_limit = 0;
     95   int hard_fd_limit = 0;
     96 
     97 #if defined(OS_POSIX)
     98   struct rlimit nofile;
     99   if (!getrlimit(RLIMIT_NOFILE, &nofile)) {
    100     soft_fd_limit = nofile.rlim_cur;
    101     hard_fd_limit = nofile.rlim_max;
    102     fd_limit_status = FD_LIMIT_STATUS_SUCCEEDED;
    103   } else {
    104     fd_limit_status = FD_LIMIT_STATUS_FAILED;
    105   }
    106 #endif
    107 
    108   SIMPLE_CACHE_UMA(ENUMERATION,
    109                    "FileDescriptorLimitStatus", cache_type,
    110                    fd_limit_status, FD_LIMIT_STATUS_MAX);
    111   if (fd_limit_status == FD_LIMIT_STATUS_SUCCEEDED) {
    112     SIMPLE_CACHE_UMA(SPARSE_SLOWLY,
    113                      "FileDescriptorLimitSoft", cache_type, soft_fd_limit);
    114     SIMPLE_CACHE_UMA(SPARSE_SLOWLY,
    115                      "FileDescriptorLimitHard", cache_type, hard_fd_limit);
    116   }
    117 
    118   g_fd_limit_histogram_has_been_populated = true;
    119 }
    120 
    121 // Detects if the files in the cache directory match the current disk cache
    122 // backend type and version. If the directory contains no cache, occupies it
    123 // with the fresh structure.
    124 bool FileStructureConsistent(const base::FilePath& path) {
    125   if (!base::PathExists(path) && !base::CreateDirectory(path)) {
    126     LOG(ERROR) << "Failed to create directory: " << path.LossyDisplayName();
    127     return false;
    128   }
    129   return disk_cache::UpgradeSimpleCacheOnDisk(path);
    130 }
    131 
    132 // A context used by a BarrierCompletionCallback to track state.
    133 struct BarrierContext {
    134   BarrierContext(int expected)
    135       : expected(expected),
    136         count(0),
    137         had_error(false) {}
    138 
    139   const int expected;
    140   int count;
    141   bool had_error;
    142 };
    143 
    144 void BarrierCompletionCallbackImpl(
    145     BarrierContext* context,
    146     const net::CompletionCallback& final_callback,
    147     int result) {
    148   DCHECK_GT(context->expected, context->count);
    149   if (context->had_error)
    150     return;
    151   if (result != net::OK) {
    152     context->had_error = true;
    153     final_callback.Run(result);
    154     return;
    155   }
    156   ++context->count;
    157   if (context->count == context->expected)
    158     final_callback.Run(net::OK);
    159 }
    160 
    161 // A barrier completion callback is a net::CompletionCallback that waits for
    162 // |count| successful results before invoking |final_callback|. In the case of
    163 // an error, the first error is passed to |final_callback| and all others
    164 // are ignored.
    165 net::CompletionCallback MakeBarrierCompletionCallback(
    166     int count,
    167     const net::CompletionCallback& final_callback) {
    168   BarrierContext* context = new BarrierContext(count);
    169   return base::Bind(&BarrierCompletionCallbackImpl,
    170                     base::Owned(context), final_callback);
    171 }
    172 
    173 // A short bindable thunk that ensures a completion callback is always called
    174 // after running an operation asynchronously.
    175 void RunOperationAndCallback(
    176     const Callback<int(const net::CompletionCallback&)>& operation,
    177     const net::CompletionCallback& operation_callback) {
    178   const int operation_result = operation.Run(operation_callback);
    179   if (operation_result != net::ERR_IO_PENDING)
    180     operation_callback.Run(operation_result);
    181 }
    182 
    183 void RecordIndexLoad(net::CacheType cache_type,
    184                      base::TimeTicks constructed_since,
    185                      int result) {
    186   const base::TimeDelta creation_to_index = base::TimeTicks::Now() -
    187                                             constructed_since;
    188   if (result == net::OK) {
    189     SIMPLE_CACHE_UMA(TIMES, "CreationToIndex", cache_type, creation_to_index);
    190   } else {
    191     SIMPLE_CACHE_UMA(TIMES,
    192                      "CreationToIndexFail", cache_type, creation_to_index);
    193   }
    194 }
    195 
    196 }  // namespace
    197 
    198 class SimpleBackendImpl::ActiveEntryProxy
    199     : public SimpleEntryImpl::ActiveEntryProxy {
    200  public:
    201   virtual ~ActiveEntryProxy() {
    202     if (backend_) {
    203       DCHECK_EQ(1U, backend_->active_entries_.count(entry_hash_));
    204       backend_->active_entries_.erase(entry_hash_);
    205     }
    206   }
    207 
    208   static scoped_ptr<SimpleEntryImpl::ActiveEntryProxy> Create(
    209       int64 entry_hash,
    210       SimpleBackendImpl* backend) {
    211     scoped_ptr<SimpleEntryImpl::ActiveEntryProxy>
    212         proxy(new ActiveEntryProxy(entry_hash, backend));
    213     return proxy.Pass();
    214   }
    215 
    216  private:
    217   ActiveEntryProxy(uint64 entry_hash,
    218                    SimpleBackendImpl* backend)
    219       : entry_hash_(entry_hash),
    220         backend_(backend->AsWeakPtr()) {}
    221 
    222   uint64 entry_hash_;
    223   base::WeakPtr<SimpleBackendImpl> backend_;
    224 };
    225 
    226 SimpleBackendImpl::SimpleBackendImpl(
    227     const FilePath& path,
    228     int max_bytes,
    229     net::CacheType cache_type,
    230     const scoped_refptr<base::SingleThreadTaskRunner>& cache_thread,
    231     net::NetLog* net_log)
    232     : path_(path),
    233       cache_type_(cache_type),
    234       cache_thread_(cache_thread),
    235       orig_max_size_(max_bytes),
    236       entry_operations_mode_(cache_type == net::DISK_CACHE ?
    237                                  SimpleEntryImpl::OPTIMISTIC_OPERATIONS :
    238                                  SimpleEntryImpl::NON_OPTIMISTIC_OPERATIONS),
    239       net_log_(net_log) {
    240   MaybeHistogramFdLimit(cache_type_);
    241 }
    242 
    243 SimpleBackendImpl::~SimpleBackendImpl() {
    244   index_->WriteToDisk();
    245 }
    246 
    247 int SimpleBackendImpl::Init(const CompletionCallback& completion_callback) {
    248   MaybeCreateSequencedWorkerPool();
    249 
    250   worker_pool_ = g_sequenced_worker_pool->GetTaskRunnerWithShutdownBehavior(
    251       SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
    252 
    253   index_.reset(new SimpleIndex(
    254       base::ThreadTaskRunnerHandle::Get(),
    255       this,
    256       cache_type_,
    257       make_scoped_ptr(new SimpleIndexFile(
    258           cache_thread_, worker_pool_.get(), cache_type_, path_))));
    259   index_->ExecuteWhenReady(
    260       base::Bind(&RecordIndexLoad, cache_type_, base::TimeTicks::Now()));
    261 
    262   PostTaskAndReplyWithResult(
    263       cache_thread_.get(),
    264       FROM_HERE,
    265       base::Bind(
    266           &SimpleBackendImpl::InitCacheStructureOnDisk, path_, orig_max_size_),
    267       base::Bind(&SimpleBackendImpl::InitializeIndex,
    268                  AsWeakPtr(),
    269                  completion_callback));
    270   return net::ERR_IO_PENDING;
    271 }
    272 
    273 bool SimpleBackendImpl::SetMaxSize(int max_bytes) {
    274   orig_max_size_ = max_bytes;
    275   return index_->SetMaxSize(max_bytes);
    276 }
    277 
    278 int SimpleBackendImpl::GetMaxFileSize() const {
    279   return index_->max_size() / kMaxFileRatio;
    280 }
    281 
    282 void SimpleBackendImpl::OnDoomStart(uint64 entry_hash) {
    283   DCHECK_EQ(0u, entries_pending_doom_.count(entry_hash));
    284   entries_pending_doom_.insert(
    285       std::make_pair(entry_hash, std::vector<Closure>()));
    286 }
    287 
    288 void SimpleBackendImpl::OnDoomComplete(uint64 entry_hash) {
    289   DCHECK_EQ(1u, entries_pending_doom_.count(entry_hash));
    290   base::hash_map<uint64, std::vector<Closure> >::iterator it =
    291       entries_pending_doom_.find(entry_hash);
    292   std::vector<Closure> to_run_closures;
    293   to_run_closures.swap(it->second);
    294   entries_pending_doom_.erase(it);
    295 
    296   std::for_each(to_run_closures.begin(), to_run_closures.end(),
    297                 std::mem_fun_ref(&Closure::Run));
    298 }
    299 
    300 void SimpleBackendImpl::DoomEntries(std::vector<uint64>* entry_hashes,
    301                                     const net::CompletionCallback& callback) {
    302   scoped_ptr<std::vector<uint64> >
    303       mass_doom_entry_hashes(new std::vector<uint64>());
    304   mass_doom_entry_hashes->swap(*entry_hashes);
    305 
    306   std::vector<uint64> to_doom_individually_hashes;
    307 
    308   // For each of the entry hashes, there are two cases:
    309   // 1. The entry is either open or pending doom, and so it should be doomed
    310   //    individually to avoid flakes.
    311   // 2. The entry is not in use at all, so we can call
    312   //    SimpleSynchronousEntry::DoomEntrySet and delete the files en masse.
    313   for (int i = mass_doom_entry_hashes->size() - 1; i >= 0; --i) {
    314     const uint64 entry_hash = (*mass_doom_entry_hashes)[i];
    315     DCHECK(active_entries_.count(entry_hash) == 0 ||
    316            entries_pending_doom_.count(entry_hash) == 0);
    317     if (!active_entries_.count(entry_hash) &&
    318         !entries_pending_doom_.count(entry_hash)) {
    319       continue;
    320     }
    321 
    322     to_doom_individually_hashes.push_back(entry_hash);
    323 
    324     (*mass_doom_entry_hashes)[i] = mass_doom_entry_hashes->back();
    325     mass_doom_entry_hashes->resize(mass_doom_entry_hashes->size() - 1);
    326   }
    327 
    328   net::CompletionCallback barrier_callback =
    329       MakeBarrierCompletionCallback(to_doom_individually_hashes.size() + 1,
    330                                     callback);
    331   for (std::vector<uint64>::const_iterator
    332            it = to_doom_individually_hashes.begin(),
    333            end = to_doom_individually_hashes.end(); it != end; ++it) {
    334     const int doom_result = DoomEntryFromHash(*it, barrier_callback);
    335     DCHECK_EQ(net::ERR_IO_PENDING, doom_result);
    336     index_->Remove(*it);
    337   }
    338 
    339   for (std::vector<uint64>::const_iterator it = mass_doom_entry_hashes->begin(),
    340                                            end = mass_doom_entry_hashes->end();
    341        it != end; ++it) {
    342     index_->Remove(*it);
    343     OnDoomStart(*it);
    344   }
    345 
    346   // Taking this pointer here avoids undefined behaviour from calling
    347   // base::Passed before mass_doom_entry_hashes.get().
    348   std::vector<uint64>* mass_doom_entry_hashes_ptr =
    349       mass_doom_entry_hashes.get();
    350   PostTaskAndReplyWithResult(worker_pool_.get(),
    351                              FROM_HERE,
    352                              base::Bind(&SimpleSynchronousEntry::DoomEntrySet,
    353                                         mass_doom_entry_hashes_ptr,
    354                                         path_),
    355                              base::Bind(&SimpleBackendImpl::DoomEntriesComplete,
    356                                         AsWeakPtr(),
    357                                         base::Passed(&mass_doom_entry_hashes),
    358                                         barrier_callback));
    359 }
    360 
    361 net::CacheType SimpleBackendImpl::GetCacheType() const {
    362   return net::DISK_CACHE;
    363 }
    364 
    365 int32 SimpleBackendImpl::GetEntryCount() const {
    366   // TODO(pasko): Use directory file count when index is not ready.
    367   return index_->GetEntryCount();
    368 }
    369 
    370 int SimpleBackendImpl::OpenEntry(const std::string& key,
    371                                  Entry** entry,
    372                                  const CompletionCallback& callback) {
    373   const uint64 entry_hash = simple_util::GetEntryHashKey(key);
    374 
    375   // TODO(gavinp): Factor out this (not quite completely) repetitive code
    376   // block from OpenEntry/CreateEntry/DoomEntry.
    377   base::hash_map<uint64, std::vector<Closure> >::iterator it =
    378       entries_pending_doom_.find(entry_hash);
    379   if (it != entries_pending_doom_.end()) {
    380     Callback<int(const net::CompletionCallback&)> operation =
    381         base::Bind(&SimpleBackendImpl::OpenEntry,
    382                    base::Unretained(this), key, entry);
    383     it->second.push_back(base::Bind(&RunOperationAndCallback,
    384                                     operation, callback));
    385     return net::ERR_IO_PENDING;
    386   }
    387   scoped_refptr<SimpleEntryImpl> simple_entry =
    388       CreateOrFindActiveEntry(entry_hash, key);
    389   CompletionCallback backend_callback =
    390       base::Bind(&SimpleBackendImpl::OnEntryOpenedFromKey,
    391                  AsWeakPtr(),
    392                  key,
    393                  entry,
    394                  simple_entry,
    395                  callback);
    396   return simple_entry->OpenEntry(entry, backend_callback);
    397 }
    398 
    399 int SimpleBackendImpl::CreateEntry(const std::string& key,
    400                                    Entry** entry,
    401                                    const CompletionCallback& callback) {
    402   DCHECK_LT(0u, key.size());
    403   const uint64 entry_hash = simple_util::GetEntryHashKey(key);
    404 
    405   base::hash_map<uint64, std::vector<Closure> >::iterator it =
    406       entries_pending_doom_.find(entry_hash);
    407   if (it != entries_pending_doom_.end()) {
    408     Callback<int(const net::CompletionCallback&)> operation =
    409         base::Bind(&SimpleBackendImpl::CreateEntry,
    410                    base::Unretained(this), key, entry);
    411     it->second.push_back(base::Bind(&RunOperationAndCallback,
    412                                     operation, callback));
    413     return net::ERR_IO_PENDING;
    414   }
    415   scoped_refptr<SimpleEntryImpl> simple_entry =
    416       CreateOrFindActiveEntry(entry_hash, key);
    417   return simple_entry->CreateEntry(entry, callback);
    418 }
    419 
    420 int SimpleBackendImpl::DoomEntry(const std::string& key,
    421                                  const net::CompletionCallback& callback) {
    422   const uint64 entry_hash = simple_util::GetEntryHashKey(key);
    423 
    424   base::hash_map<uint64, std::vector<Closure> >::iterator it =
    425       entries_pending_doom_.find(entry_hash);
    426   if (it != entries_pending_doom_.end()) {
    427     Callback<int(const net::CompletionCallback&)> operation =
    428         base::Bind(&SimpleBackendImpl::DoomEntry, base::Unretained(this), key);
    429     it->second.push_back(base::Bind(&RunOperationAndCallback,
    430                                     operation, callback));
    431     return net::ERR_IO_PENDING;
    432   }
    433   scoped_refptr<SimpleEntryImpl> simple_entry =
    434       CreateOrFindActiveEntry(entry_hash, key);
    435   return simple_entry->DoomEntry(callback);
    436 }
    437 
    438 int SimpleBackendImpl::DoomAllEntries(const CompletionCallback& callback) {
    439   return DoomEntriesBetween(Time(), Time(), callback);
    440 }
    441 
    442 void SimpleBackendImpl::IndexReadyForDoom(Time initial_time,
    443                                           Time end_time,
    444                                           const CompletionCallback& callback,
    445                                           int result) {
    446   if (result != net::OK) {
    447     callback.Run(result);
    448     return;
    449   }
    450   scoped_ptr<std::vector<uint64> > removed_key_hashes(
    451       index_->GetEntriesBetween(initial_time, end_time).release());
    452   DoomEntries(removed_key_hashes.get(), callback);
    453 }
    454 
    455 int SimpleBackendImpl::DoomEntriesBetween(
    456     const Time initial_time,
    457     const Time end_time,
    458     const CompletionCallback& callback) {
    459   return index_->ExecuteWhenReady(
    460       base::Bind(&SimpleBackendImpl::IndexReadyForDoom, AsWeakPtr(),
    461                  initial_time, end_time, callback));
    462 }
    463 
    464 int SimpleBackendImpl::DoomEntriesSince(
    465     const Time initial_time,
    466     const CompletionCallback& callback) {
    467   return DoomEntriesBetween(initial_time, Time(), callback);
    468 }
    469 
    470 class SimpleBackendImpl::SimpleIterator FINAL : public Iterator {
    471  public:
    472   explicit SimpleIterator(base::WeakPtr<SimpleBackendImpl> backend)
    473       : backend_(backend),
    474         weak_factory_(this) {
    475   }
    476 
    477   // From Backend::Iterator:
    478   virtual int OpenNextEntry(Entry** next_entry,
    479                             const CompletionCallback& callback) OVERRIDE {
    480     CompletionCallback open_next_entry_impl =
    481         base::Bind(&SimpleIterator::OpenNextEntryImpl,
    482                    weak_factory_.GetWeakPtr(), next_entry, callback);
    483     return backend_->index_->ExecuteWhenReady(open_next_entry_impl);
    484   }
    485 
    486   void OpenNextEntryImpl(Entry** next_entry,
    487                          const CompletionCallback& callback,
    488                          int index_initialization_error_code) {
    489     if (!backend_) {
    490       callback.Run(net::ERR_FAILED);
    491       return;
    492     }
    493     if (index_initialization_error_code != net::OK) {
    494       callback.Run(index_initialization_error_code);
    495       return;
    496     }
    497     if (!hashes_to_enumerate_)
    498       hashes_to_enumerate_ = backend_->index()->GetAllHashes().Pass();
    499 
    500     while (!hashes_to_enumerate_->empty()) {
    501       uint64 entry_hash = hashes_to_enumerate_->back();
    502       hashes_to_enumerate_->pop_back();
    503       if (backend_->index()->Has(entry_hash)) {
    504         *next_entry = NULL;
    505         CompletionCallback continue_iteration = base::Bind(
    506             &SimpleIterator::CheckIterationReturnValue,
    507             weak_factory_.GetWeakPtr(),
    508             next_entry,
    509             callback);
    510         int error_code_open = backend_->OpenEntryFromHash(entry_hash,
    511                                                           next_entry,
    512                                                           continue_iteration);
    513         if (error_code_open == net::ERR_IO_PENDING)
    514           return;
    515         if (error_code_open != net::ERR_FAILED) {
    516           callback.Run(error_code_open);
    517           return;
    518         }
    519       }
    520     }
    521     callback.Run(net::ERR_FAILED);
    522   }
    523 
    524   void CheckIterationReturnValue(Entry** entry,
    525                                  const CompletionCallback& callback,
    526                                  int error_code) {
    527     if (error_code == net::ERR_FAILED) {
    528       OpenNextEntry(entry, callback);
    529       return;
    530     }
    531     callback.Run(error_code);
    532   }
    533 
    534  private:
    535   base::WeakPtr<SimpleBackendImpl> backend_;
    536   scoped_ptr<std::vector<uint64> > hashes_to_enumerate_;
    537   base::WeakPtrFactory<SimpleIterator> weak_factory_;
    538 };
    539 
    540 scoped_ptr<Backend::Iterator> SimpleBackendImpl::CreateIterator() {
    541   return scoped_ptr<Iterator>(new SimpleIterator(AsWeakPtr()));
    542 }
    543 
    544 void SimpleBackendImpl::GetStats(
    545     std::vector<std::pair<std::string, std::string> >* stats) {
    546   std::pair<std::string, std::string> item;
    547   item.first = "Cache type";
    548   item.second = "Simple Cache";
    549   stats->push_back(item);
    550 }
    551 
    552 void SimpleBackendImpl::OnExternalCacheHit(const std::string& key) {
    553   index_->UseIfExists(simple_util::GetEntryHashKey(key));
    554 }
    555 
    556 void SimpleBackendImpl::InitializeIndex(const CompletionCallback& callback,
    557                                         const DiskStatResult& result) {
    558   if (result.net_error == net::OK) {
    559     index_->SetMaxSize(result.max_size);
    560     index_->Initialize(result.cache_dir_mtime);
    561   }
    562   callback.Run(result.net_error);
    563 }
    564 
    565 SimpleBackendImpl::DiskStatResult SimpleBackendImpl::InitCacheStructureOnDisk(
    566     const base::FilePath& path,
    567     uint64 suggested_max_size) {
    568   DiskStatResult result;
    569   result.max_size = suggested_max_size;
    570   result.net_error = net::OK;
    571   if (!FileStructureConsistent(path)) {
    572     LOG(ERROR) << "Simple Cache Backend: wrong file structure on disk: "
    573                << path.LossyDisplayName();
    574     result.net_error = net::ERR_FAILED;
    575   } else {
    576     bool mtime_result =
    577         disk_cache::simple_util::GetMTime(path, &result.cache_dir_mtime);
    578     DCHECK(mtime_result);
    579     if (!result.max_size) {
    580       int64 available = base::SysInfo::AmountOfFreeDiskSpace(path);
    581       result.max_size = disk_cache::PreferredCacheSize(available);
    582     }
    583     DCHECK(result.max_size);
    584   }
    585   return result;
    586 }
    587 
    588 scoped_refptr<SimpleEntryImpl> SimpleBackendImpl::CreateOrFindActiveEntry(
    589     const uint64 entry_hash,
    590     const std::string& key) {
    591   DCHECK_EQ(entry_hash, simple_util::GetEntryHashKey(key));
    592   std::pair<EntryMap::iterator, bool> insert_result =
    593       active_entries_.insert(EntryMap::value_type(entry_hash, NULL));
    594   EntryMap::iterator& it = insert_result.first;
    595   const bool did_insert = insert_result.second;
    596   if (did_insert) {
    597     SimpleEntryImpl* entry = it->second =
    598         new SimpleEntryImpl(cache_type_, path_, entry_hash,
    599                             entry_operations_mode_,this, net_log_);
    600     entry->SetKey(key);
    601     entry->SetActiveEntryProxy(ActiveEntryProxy::Create(entry_hash, this));
    602   }
    603   DCHECK(it->second);
    604   // It's possible, but unlikely, that we have an entry hash collision with a
    605   // currently active entry.
    606   if (key != it->second->key()) {
    607     it->second->Doom();
    608     DCHECK_EQ(0U, active_entries_.count(entry_hash));
    609     return CreateOrFindActiveEntry(entry_hash, key);
    610   }
    611   return make_scoped_refptr(it->second);
    612 }
    613 
    614 int SimpleBackendImpl::OpenEntryFromHash(uint64 entry_hash,
    615                                          Entry** entry,
    616                                          const CompletionCallback& callback) {
    617   base::hash_map<uint64, std::vector<Closure> >::iterator it =
    618       entries_pending_doom_.find(entry_hash);
    619   if (it != entries_pending_doom_.end()) {
    620     Callback<int(const net::CompletionCallback&)> operation =
    621         base::Bind(&SimpleBackendImpl::OpenEntryFromHash,
    622                    base::Unretained(this), entry_hash, entry);
    623     it->second.push_back(base::Bind(&RunOperationAndCallback,
    624                                     operation, callback));
    625     return net::ERR_IO_PENDING;
    626   }
    627 
    628   EntryMap::iterator has_active = active_entries_.find(entry_hash);
    629   if (has_active != active_entries_.end()) {
    630     return OpenEntry(has_active->second->key(), entry, callback);
    631   }
    632 
    633   scoped_refptr<SimpleEntryImpl> simple_entry = new SimpleEntryImpl(
    634       cache_type_, path_, entry_hash, entry_operations_mode_, this, net_log_);
    635   CompletionCallback backend_callback =
    636       base::Bind(&SimpleBackendImpl::OnEntryOpenedFromHash,
    637                  AsWeakPtr(), entry_hash, entry, simple_entry, callback);
    638   return simple_entry->OpenEntry(entry, backend_callback);
    639 }
    640 
    641 int SimpleBackendImpl::DoomEntryFromHash(uint64 entry_hash,
    642                                          const CompletionCallback& callback) {
    643   Entry** entry = new Entry*();
    644   scoped_ptr<Entry*> scoped_entry(entry);
    645 
    646   base::hash_map<uint64, std::vector<Closure> >::iterator pending_it =
    647       entries_pending_doom_.find(entry_hash);
    648   if (pending_it != entries_pending_doom_.end()) {
    649     Callback<int(const net::CompletionCallback&)> operation =
    650         base::Bind(&SimpleBackendImpl::DoomEntryFromHash,
    651                    base::Unretained(this), entry_hash);
    652     pending_it->second.push_back(base::Bind(&RunOperationAndCallback,
    653                                     operation, callback));
    654     return net::ERR_IO_PENDING;
    655   }
    656 
    657   EntryMap::iterator active_it = active_entries_.find(entry_hash);
    658   if (active_it != active_entries_.end())
    659     return active_it->second->DoomEntry(callback);
    660 
    661   // There's no pending dooms, nor any open entry. We can make a trivial
    662   // call to DoomEntries() to delete this entry.
    663   std::vector<uint64> entry_hash_vector;
    664   entry_hash_vector.push_back(entry_hash);
    665   DoomEntries(&entry_hash_vector, callback);
    666   return net::ERR_IO_PENDING;
    667 }
    668 
    669 void SimpleBackendImpl::OnEntryOpenedFromHash(
    670     uint64 hash,
    671     Entry** entry,
    672     const scoped_refptr<SimpleEntryImpl>& simple_entry,
    673     const CompletionCallback& callback,
    674     int error_code) {
    675   if (error_code != net::OK) {
    676     callback.Run(error_code);
    677     return;
    678   }
    679   DCHECK(*entry);
    680   std::pair<EntryMap::iterator, bool> insert_result =
    681       active_entries_.insert(EntryMap::value_type(hash, simple_entry.get()));
    682   EntryMap::iterator& it = insert_result.first;
    683   const bool did_insert = insert_result.second;
    684   if (did_insert) {
    685     // There was no active entry corresponding to this hash. We've already put
    686     // the entry opened from hash in the |active_entries_|. We now provide the
    687     // proxy object to the entry.
    688     it->second->SetActiveEntryProxy(ActiveEntryProxy::Create(hash, this));
    689     callback.Run(net::OK);
    690   } else {
    691     // The entry was made active while we waiting for the open from hash to
    692     // finish. The entry created from hash needs to be closed, and the one
    693     // in |active_entries_| can be returned to the caller.
    694     simple_entry->Close();
    695     it->second->OpenEntry(entry, callback);
    696   }
    697 }
    698 
    699 void SimpleBackendImpl::OnEntryOpenedFromKey(
    700     const std::string key,
    701     Entry** entry,
    702     const scoped_refptr<SimpleEntryImpl>& simple_entry,
    703     const CompletionCallback& callback,
    704     int error_code) {
    705   int final_code = error_code;
    706   if (final_code == net::OK) {
    707     bool key_matches = key.compare(simple_entry->key()) == 0;
    708     if (!key_matches) {
    709       // TODO(clamy): Add a unit test to check this code path.
    710       DLOG(WARNING) << "Key mismatch on open.";
    711       simple_entry->Doom();
    712       simple_entry->Close();
    713       final_code = net::ERR_FAILED;
    714     } else {
    715       DCHECK_EQ(simple_entry->entry_hash(), simple_util::GetEntryHashKey(key));
    716     }
    717     SIMPLE_CACHE_UMA(BOOLEAN, "KeyMatchedOnOpen", cache_type_, key_matches);
    718   }
    719   callback.Run(final_code);
    720 }
    721 
    722 void SimpleBackendImpl::DoomEntriesComplete(
    723     scoped_ptr<std::vector<uint64> > entry_hashes,
    724     const net::CompletionCallback& callback,
    725     int result) {
    726   std::for_each(
    727       entry_hashes->begin(), entry_hashes->end(),
    728       std::bind1st(std::mem_fun(&SimpleBackendImpl::OnDoomComplete),
    729                    this));
    730   callback.Run(result);
    731 }
    732 
    733 void SimpleBackendImpl::FlushWorkerPoolForTesting() {
    734   if (g_sequenced_worker_pool)
    735     g_sequenced_worker_pool->FlushForTesting();
    736 }
    737 
    738 }  // namespace disk_cache
    739