<commit_before>// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/disk_cache/backend_impl.h"

#include "base/field_trial.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/histogram.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "base/sys_info.h"
#include "base/timer.h"
#include "base/worker_pool.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/cache_util.h"
#include "net/disk_cache/entry_impl.h"
#include "net/disk_cache/errors.h"
#include "net/disk_cache/hash.h"
#include "net/disk_cache/file.h"
#include "net/disk_cache/mem_backend_impl.h"

// This has to be defined before including histogram_macros.h from this file.
#define NET_DISK_CACHE_BACKEND_IMPL_CC_
#include "net/disk_cache/histogram_macros.h"

using base::Time;
using base::TimeDelta;
using base::TimeTicks;

namespace {

const char* kIndexName = "index";
const int kMaxOldFolders = 100;

// Seems like ~240 MB correspond to less than 50k entries for 99% of the people.
const int k64kEntriesStore = 240 * 1000 * 1000;
const int kBaseTableLen = 64 * 1024;
const int kDefaultCacheSize = 80 * 1024 * 1024;

int DesiredIndexTableLen(int32 storage_size) {
  if (storage_size <= k64kEntriesStore)
    return kBaseTableLen;
  if (storage_size <= k64kEntriesStore * 2)
    return kBaseTableLen * 2;
  if (storage_size <= k64kEntriesStore * 4)
    return kBaseTableLen * 4;
  if (storage_size <= k64kEntriesStore * 8)
    return kBaseTableLen * 8;

  // The biggest storage_size for int32 requires a 4 MB table.
  return kBaseTableLen * 16;
}

int MaxStorageSizeForTable(int table_len) {
  return table_len * (k64kEntriesStore / kBaseTableLen);
}

size_t GetIndexSize(int table_len) {
  size_t table_size = sizeof(disk_cache::CacheAddr) * table_len;
  return sizeof(disk_cache::IndexHeader) + table_size;
}

// ------------------------------------------------------------------------

// Returns a fully qualified name from path and name, using a given name prefix
// and index number. For instance, if the arguments are "/foo", "bar" and 5, it
// will return "/foo/old_bar_005".
FilePath GetPrefixedName(const FilePath& path, const std::string& name,
                         int index) {
  std::string tmp = StringPrintf("%s%s_%03d", "old_", name.c_str(), index);
  return path.AppendASCII(tmp);
}

// This is a simple Task to cleanup old caches.
class CleanupTask : public Task {
 public:
  CleanupTask(const FilePath& path, const std::string& name)
      : path_(path), name_(name) {}

  virtual void Run();

 private:
  FilePath path_;
  std::string name_;
  DISALLOW_COPY_AND_ASSIGN(CleanupTask);
};

void CleanupTask::Run() {
  for (int i = 0; i < kMaxOldFolders; i++) {
    FilePath to_delete = GetPrefixedName(path_, name_, i);
    disk_cache::DeleteCache(to_delete, true);
  }
}

// Returns a full path to rename the current cache, in order to delete it. path
// is the current folder location, and name is the current folder name.
FilePath GetTempCacheName(const FilePath& path, const std::string& name) {
  // We'll attempt to have up to kMaxOldFolders folders for deletion.
  for (int i = 0; i < kMaxOldFolders; i++) {
    FilePath to_delete = GetPrefixedName(path, name, i);
    if (!file_util::PathExists(to_delete))
      return to_delete;
  }
  return FilePath();
}

// Moves the cache files to a new folder and creates a task to delete them.
bool DelayedCacheCleanup(const FilePath& full_path) {
  FilePath current_path = full_path.StripTrailingSeparators();

  FilePath path = current_path.DirName();
  FilePath name = current_path.BaseName();
#if defined(OS_POSIX)
  std::string name_str = name.value();
#elif defined(OS_WIN)
  // We created this file so it should only contain ASCII.
  std::string name_str = WideToASCII(name.value());
#endif

  FilePath to_delete = GetTempCacheName(path, name_str);
  if (to_delete.empty()) {
    LOG(ERROR) << "Unable to get another cache folder";
    return false;
  }

  if (!disk_cache::MoveCache(full_path, to_delete)) {
    LOG(ERROR) << "Unable to rename cache folder";
    return false;
  }

  WorkerPool::PostTask(FROM_HERE, new CleanupTask(path, name_str), true);
  return true;
}

// Sets |current_group| for the current experiment. Returns false if the files
// should be discarded.
bool InitExperiment(int* current_group) {
  if (*current_group == 3 || *current_group == 4) {
    // Discard current cache for groups 3 and 4.
    return false;
  }

  // There is no experiment.
  *current_group = 0;
  return true;
}

// Initializes the field trial structures to allow performance measurements
// for the current cache configuration.
void SetFieldTrialInfo(int size_group) {
  static bool first = true;
  if (!first)
    return;

  // Field trials involve static objects so we have to do this only once.
  first = false;
  scoped_refptr<FieldTrial> trial1 = new FieldTrial("CacheSize", 10);
  std::string group1 = StringPrintf("CacheSizeGroup_%d", size_group);
  trial1->AppendGroup(group1, FieldTrial::kAllRemainingProbability);
}

// ------------------------------------------------------------------------

// This class takes care of building an instance of the backend.
class CacheCreator {
 public:
  CacheCreator(const FilePath& path, bool force, int max_bytes,
               net::CacheType type, uint32 flags,
               base::MessageLoopProxy* thread, disk_cache::Backend** backend,
               net::CompletionCallback* callback)
      : path_(path), force_(force), retry_(false), max_bytes_(max_bytes),
        type_(type), flags_(flags), thread_(thread), backend_(backend),
        callback_(callback), cache_(NULL),
        ALLOW_THIS_IN_INITIALIZER_LIST(
            my_callback_(this, &CacheCreator::OnIOComplete)) {
  }
  ~CacheCreator() {}

  // Creates the backend.
  int Run();

  // Callback implementation.
  void OnIOComplete(int result);

 private:
  void DoCallback(int result);

  const FilePath& path_;
  bool force_;
  bool retry_;
  int max_bytes_;
  net::CacheType type_;
  uint32 flags_;
  scoped_refptr<base::MessageLoopProxy> thread_;
  disk_cache::Backend** backend_;
  net::CompletionCallback* callback_;
  disk_cache::BackendImpl* cache_;
  net::CompletionCallbackImpl<CacheCreator> my_callback_;

  DISALLOW_COPY_AND_ASSIGN(CacheCreator);
};

int CacheCreator::Run() {
  cache_ = new disk_cache::BackendImpl(path_, thread_);
  cache_->SetMaxSize(max_bytes_);
  cache_->SetType(type_);
  cache_->SetFlags(flags_);
  int rv = cache_->Init(&my_callback_);
  DCHECK_EQ(net::ERR_IO_PENDING, rv);
  return rv;
}

void CacheCreator::OnIOComplete(int result) {
  if (result == net::OK || !force_ || retry_)
    return DoCallback(result);

  // This is a failure and we are supposed to try again, so delete the object,
  // delete all the files, and try again.
  retry_ = true;
  delete cache_;
  cache_ = NULL;
  if (!DelayedCacheCleanup(path_))
    return DoCallback(result);

  // The worker thread will start deleting files soon, but the original folder
  // is not there anymore... let's create a new set of files.
  int rv = Run();
  DCHECK_EQ(net::ERR_IO_PENDING, rv);
}

void CacheCreator::DoCallback(int result) {
  DCHECK_NE(net::ERR_IO_PENDING, result);
  if (result == net::OK) {
    *backend_ = cache_;
  } else {
    LOG(ERROR) << "Unable to create cache";
    *backend_ = NULL;
    delete cache_;
  }
  callback_->Run(result);
  delete this;
}

// ------------------------------------------------------------------------

// A task to perform final cleanup on the background thread.
class FinalCleanup : public Task {
 public:
  explicit FinalCleanup(disk_cache::BackendImpl* backend) : backend_(backend) {}
  ~FinalCleanup() {}

  virtual void Run();
 private:
  disk_cache::BackendImpl* backend_;
  DISALLOW_EVIL_CONSTRUCTORS(FinalCleanup);
};

void FinalCleanup::Run() {
  backend_->CleanupCache();
}

}  // namespace

// ------------------------------------------------------------------------

namespace disk_cache {

int CreateCacheBackend(net::CacheType type, const FilePath& path, int max_bytes,
                       bool force, base::MessageLoopProxy* thread,
                       Backend** backend, CompletionCallback* callback) {
  DCHECK(callback);
  if (type == net::MEMORY_CACHE) {
    *backend = MemBackendImpl::CreateBackend(max_bytes);
    return *backend ? net::OK : net::ERR_FAILED;
  }
  DCHECK(thread);

  return BackendImpl::CreateBackend(path, force, max_bytes, type, kNone, thread,
                                    backend, callback);
}

// Returns the preferred maximum number of bytes for the cache given the
// number of available bytes.
int PreferedCacheSize(int64 available) {
  // Return 80% of the available space if there is not enough space to use
  // kDefaultCacheSize.
  if (available < kDefaultCacheSize * 10 / 8)
    return static_cast<int32>(available * 8 / 10);

  // Return kDefaultCacheSize if it uses 80% to 10% of the available space.
  if (available < kDefaultCacheSize * 10)
    return kDefaultCacheSize;

  // Return 10% of the available space if the target size
  // (2.5 * kDefaultCacheSize) is more than 10%.
  if (available < static_cast<int64>(kDefaultCacheSize) * 25)
    return static_cast<int32>(available / 10);

  // Return the target size (2.5 * kDefaultCacheSize) if it uses 10% to 1%
  // of the available space.
  if (available < static_cast<int64>(kDefaultCacheSize) * 250)
    return kDefaultCacheSize * 5 / 2;

  // Return 1% of the available space if it does not exceed kint32max.
  if (available < static_cast<int64>(kint32max) * 100)
    return static_cast<int32>(available / 100);

  return kint32max;
}

// ------------------------------------------------------------------------

// If the initialization of the cache fails, and force is true, we will discard
// the whole cache and create a new one. In order to process a potentially large
// number of files, we'll rename the cache folder to old_ + original_name +
// number, (located on the same parent folder), and spawn a worker thread to
// delete all the files on all the stale cache folders. The whole process can
// still fail if we are not able to rename the cache folder (for instance due to
// a sharing violation), and in that case a cache for this profile (on the
// desired path) cannot be created.
//
// Static.
int BackendImpl::CreateBackend(const FilePath& full_path, bool force,
                               int max_bytes, net::CacheType type,
                               uint32 flags, base::MessageLoopProxy* thread,
                               Backend** backend,
                               CompletionCallback* callback) {
  DCHECK(callback);
  CacheCreator* creator = new CacheCreator(full_path, force, max_bytes, type,
                                           flags, thread, backend, callback);
  // This object will self-destroy when finished.
  return creator->Run();
}

int BackendImpl::Init(CompletionCallback* callback) {
  background_queue_.Init(callback);
  return net::ERR_IO_PENDING;
}

BackendImpl::~BackendImpl() {
  background_queue_.WaitForPendingIO();

  if (background_queue_.BackgroundIsCurrentThread()) {
    // Unit tests may use the same thread for everything.
    CleanupCache();
  } else {
    background_queue_.background_thread()->PostTask(FROM_HERE,
                                                    new FinalCleanup(this));
    done_.Wait();
  }
}

// ------------------------------------------------------------------------

int32 BackendImpl::GetEntryCount() const {
  if (!index_)
    return 0;
  // num_entries includes entries already evicted.
  int32 not_deleted = data_->header.num_entries -
                      data_->header.lru.sizes[Rankings::DELETED];

  if (not_deleted < 0) {
    NOTREACHED();
    not_deleted = 0;
  }

  return not_deleted;
}

int BackendImpl::OpenEntry(const std::string& key, Entry** entry,
                           CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.OpenEntry(key, entry, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::CreateEntry(const std::string& key, Entry** entry,
                             CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.CreateEntry(key, entry, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::DoomEntry(const std::string& key,
                           CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.DoomEntry(key, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::DoomAllEntries(CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.DoomAllEntries(callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::DoomEntriesBetween(const base::Time initial_time,
                                    const base::Time end_time,
                                    CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.DoomEntriesBetween(initial_time, end_time, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::DoomEntriesSince(const base::Time initial_time,
                                  CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.DoomEntriesSince(initial_time, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::OpenNextEntry(void** iter, Entry** next_entry,
                               CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.OpenNextEntry(iter, next_entry, callback);
  return net::ERR_IO_PENDING;
}

void BackendImpl::EndEnumeration(void** iter) {
  background_queue_.EndEnumeration(*iter);
  *iter = NULL;
}

void BackendImpl::GetStats(StatsItems* stats) {
  if (disabled_)
    return;

  std::pair<std::string, std::string> item;

  item.first = "Entries";
  item.second = StringPrintf("%d", data_->header.num_entries);
  stats->push_back(item);

  item.first = "Pending IO";
  item.second = StringPrintf("%d", num_pending_io_);
  stats->push_back(item);

  item.first = "Max size";
  item.second = StringPrintf("%d", max_size_);
  stats->push_back(item);

  item.first = "Current size";
  item.second = StringPrintf("%d", data_->header.num_bytes);
  stats->push_back(item);

  stats_.GetItems(stats);
}

// ------------------------------------------------------------------------

int BackendImpl::SyncInit() {
  DCHECK(!init_);
  if (init_)
    return net::ERR_FAILED;

  bool create_files = false;
  if (!InitBackingStore(&create_files)) {
    ReportError(ERR_STORAGE_ERROR);
    return net::ERR_FAILED;
  }

  num_refs_ = num_pending_io_ = max_refs_ = 0;
  entry_count_ = byte_count_ = 0;

  if (!restarted_) {
    buffer_bytes_ = 0;
    trace_object_ = TraceObject::GetTraceObject();
    // Create a recurrent timer of 30 secs.
    int timer_delay = unit_test_ ? 1000 : 30000;
    timer_.Start(TimeDelta::FromMilliseconds(timer_delay), this,
                 &BackendImpl::OnStatsTimer);
  }

  init_ = true;

  if (data_->header.experiment != 0 && cache_type_ != net::DISK_CACHE) {
    // No experiment for other caches.
    return net::ERR_FAILED;
  }

  if (!(user_flags_ & disk_cache::kNoRandom)) {
    // The unit test controls directly what to test.
    if (!InitExperiment(&data_->header.experiment))
      return net::ERR_FAILED;

    new_eviction_ = (cache_type_ == net::DISK_CACHE);
  }

  if (!CheckIndex()) {
    ReportError(ERR_INIT_FAILED);
    return net::ERR_FAILED;
  }

  // We don't care if the value overflows. The only thing we care about is that
  // the id cannot be zero, because that value is used as "not dirty".
  // Increasing the value once per second gives us many years before we start
  // having collisions.
  data_->header.this_id++;
  if (!data_->header.this_id)
    data_->header.this_id++;

  if (data_->header.crash) {
    ReportError(ERR_PREVIOUS_CRASH);
  } else {
    ReportError(0);
    data_->header.crash = 1;
  }

  if (!block_files_.Init(create_files))
    return net::ERR_FAILED;

  // stats_ and rankings_ may end up calling back to us so we better be enabled.
  disabled_ = false;
  if (!stats_.Init(this, &data_->header.stats))
    return net::ERR_FAILED;

  disabled_ = !rankings_.Init(this, new_eviction_);
  eviction_.Init(this);

  // Setup load-time data only for the main cache.
  if (cache_type() == net::DISK_CACHE)
    SetFieldTrialInfo(GetSizeGroup());

  return disabled_ ? net::ERR_FAILED : net::OK;
}

void BackendImpl::CleanupCache() {
  Trace("Backend Cleanup");
  eviction_.Stop();
  timer_.Stop();

  if (init_) {
    if (data_)
      data_->header.crash = 0;

    File::WaitForPendingIO(&num_pending_io_);
    DCHECK(!num_refs_);
  }
  factory_.RevokeAll();
  ptr_factory_.InvalidateWeakPtrs();
  done_.Signal();
}

// ------------------------------------------------------------------------

int BackendImpl::OpenPrevEntry(void** iter, Entry** prev_entry,
                               CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.OpenPrevEntry(iter, prev_entry, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::SyncOpenEntry(const std::string& key, Entry** entry) {
  DCHECK(entry);
  *entry = OpenEntryImpl(key);
  return (*entry) ? net::OK : net::ERR_FAILED;
}

int BackendImpl::SyncCreateEntry(const std::string& key, Entry** entry) {<commit_msg>Disk cache: Remove the check for no open entries at destruction if we are not running a net_unittest.<commit_after>// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/disk_cache/backend_impl.h"

#include "base/field_trial.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/histogram.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "base/sys_info.h"
#include "base/timer.h"
#include "base/worker_pool.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/cache_util.h"
#include "net/disk_cache/entry_impl.h"
#include "net/disk_cache/errors.h"
#include "net/disk_cache/hash.h"
#include "net/disk_cache/file.h"
#include "net/disk_cache/mem_backend_impl.h"

// This has to be defined before including histogram_macros.h from this file.
#define NET_DISK_CACHE_BACKEND_IMPL_CC_
#include "net/disk_cache/histogram_macros.h"

using base::Time;
using base::TimeDelta;
using base::TimeTicks;

namespace {

const char* kIndexName = "index";
const int kMaxOldFolders = 100;

// Seems like ~240 MB correspond to less than 50k entries for 99% of the people.
const int k64kEntriesStore = 240 * 1000 * 1000;
const int kBaseTableLen = 64 * 1024;
const int kDefaultCacheSize = 80 * 1024 * 1024;

int DesiredIndexTableLen(int32 storage_size) {
  if (storage_size <= k64kEntriesStore)
    return kBaseTableLen;
  if (storage_size <= k64kEntriesStore * 2)
    return kBaseTableLen * 2;
  if (storage_size <= k64kEntriesStore * 4)
    return kBaseTableLen * 4;
  if (storage_size <= k64kEntriesStore * 8)
    return kBaseTableLen * 8;

  // The biggest storage_size for int32 requires a 4 MB table.
  return kBaseTableLen * 16;
}

int MaxStorageSizeForTable(int table_len) {
  return table_len * (k64kEntriesStore / kBaseTableLen);
}

size_t GetIndexSize(int table_len) {
  size_t table_size = sizeof(disk_cache::CacheAddr) * table_len;
  return sizeof(disk_cache::IndexHeader) + table_size;
}

// ------------------------------------------------------------------------

// Returns a fully qualified name from path and name, using a given name prefix
// and index number. For instance, if the arguments are "/foo", "bar" and 5, it
// will return "/foo/old_bar_005".
FilePath GetPrefixedName(const FilePath& path, const std::string& name,
                         int index) {
  std::string tmp = StringPrintf("%s%s_%03d", "old_", name.c_str(), index);
  return path.AppendASCII(tmp);
}

// This is a simple Task to cleanup old caches.
class CleanupTask : public Task {
 public:
  CleanupTask(const FilePath& path, const std::string& name)
      : path_(path), name_(name) {}

  virtual void Run();

 private:
  FilePath path_;
  std::string name_;
  DISALLOW_COPY_AND_ASSIGN(CleanupTask);
};

void CleanupTask::Run() {
  for (int i = 0; i < kMaxOldFolders; i++) {
    FilePath to_delete = GetPrefixedName(path_, name_, i);
    disk_cache::DeleteCache(to_delete, true);
  }
}

// Returns a full path to rename the current cache, in order to delete it. path
// is the current folder location, and name is the current folder name.
FilePath GetTempCacheName(const FilePath& path, const std::string& name) {
  // We'll attempt to have up to kMaxOldFolders folders for deletion.
  for (int i = 0; i < kMaxOldFolders; i++) {
    FilePath to_delete = GetPrefixedName(path, name, i);
    if (!file_util::PathExists(to_delete))
      return to_delete;
  }
  return FilePath();
}

// Moves the cache files to a new folder and creates a task to delete them.
bool DelayedCacheCleanup(const FilePath& full_path) {
  FilePath current_path = full_path.StripTrailingSeparators();

  FilePath path = current_path.DirName();
  FilePath name = current_path.BaseName();
#if defined(OS_POSIX)
  std::string name_str = name.value();
#elif defined(OS_WIN)
  // We created this file so it should only contain ASCII.
  std::string name_str = WideToASCII(name.value());
#endif

  FilePath to_delete = GetTempCacheName(path, name_str);
  if (to_delete.empty()) {
    LOG(ERROR) << "Unable to get another cache folder";
    return false;
  }

  if (!disk_cache::MoveCache(full_path, to_delete)) {
    LOG(ERROR) << "Unable to rename cache folder";
    return false;
  }

  WorkerPool::PostTask(FROM_HERE, new CleanupTask(path, name_str), true);
  return true;
}

// Sets |current_group| for the current experiment. Returns false if the files
// should be discarded.
bool InitExperiment(int* current_group) {
  if (*current_group == 3 || *current_group == 4) {
    // Discard current cache for groups 3 and 4.
    return false;
  }

  // There is no experiment.
  *current_group = 0;
  return true;
}

// Initializes the field trial structures to allow performance measurements
// for the current cache configuration.
void SetFieldTrialInfo(int size_group) {
  static bool first = true;
  if (!first)
    return;

  // Field trials involve static objects so we have to do this only once.
  first = false;
  scoped_refptr<FieldTrial> trial1 = new FieldTrial("CacheSize", 10);
  std::string group1 = StringPrintf("CacheSizeGroup_%d", size_group);
  trial1->AppendGroup(group1, FieldTrial::kAllRemainingProbability);
}

// ------------------------------------------------------------------------

// This class takes care of building an instance of the backend.
class CacheCreator {
 public:
  CacheCreator(const FilePath& path, bool force, int max_bytes,
               net::CacheType type, uint32 flags,
               base::MessageLoopProxy* thread, disk_cache::Backend** backend,
               net::CompletionCallback* callback)
      : path_(path), force_(force), retry_(false), max_bytes_(max_bytes),
        type_(type), flags_(flags), thread_(thread), backend_(backend),
        callback_(callback), cache_(NULL),
        ALLOW_THIS_IN_INITIALIZER_LIST(
            my_callback_(this, &CacheCreator::OnIOComplete)) {
  }
  ~CacheCreator() {}

  // Creates the backend.
  int Run();

  // Callback implementation.
  void OnIOComplete(int result);

 private:
  void DoCallback(int result);

  const FilePath& path_;
  bool force_;
  bool retry_;
  int max_bytes_;
  net::CacheType type_;
  uint32 flags_;
  scoped_refptr<base::MessageLoopProxy> thread_;
  disk_cache::Backend** backend_;
  net::CompletionCallback* callback_;
  disk_cache::BackendImpl* cache_;
  net::CompletionCallbackImpl<CacheCreator> my_callback_;

  DISALLOW_COPY_AND_ASSIGN(CacheCreator);
};

int CacheCreator::Run() {
  cache_ = new disk_cache::BackendImpl(path_, thread_);
  cache_->SetMaxSize(max_bytes_);
  cache_->SetType(type_);
  cache_->SetFlags(flags_);
  int rv = cache_->Init(&my_callback_);
  DCHECK_EQ(net::ERR_IO_PENDING, rv);
  return rv;
}

void CacheCreator::OnIOComplete(int result) {
  if (result == net::OK || !force_ || retry_)
    return DoCallback(result);

  // This is a failure and we are supposed to try again, so delete the object,
  // delete all the files, and try again.
  retry_ = true;
  delete cache_;
  cache_ = NULL;
  if (!DelayedCacheCleanup(path_))
    return DoCallback(result);

  // The worker thread will start deleting files soon, but the original folder
  // is not there anymore... let's create a new set of files.
  int rv = Run();
  DCHECK_EQ(net::ERR_IO_PENDING, rv);
}

void CacheCreator::DoCallback(int result) {
  DCHECK_NE(net::ERR_IO_PENDING, result);
  if (result == net::OK) {
    *backend_ = cache_;
  } else {
    LOG(ERROR) << "Unable to create cache";
    *backend_ = NULL;
    delete cache_;
  }
  callback_->Run(result);
  delete this;
}

// ------------------------------------------------------------------------

// A task to perform final cleanup on the background thread.
class FinalCleanup : public Task {
 public:
  explicit FinalCleanup(disk_cache::BackendImpl* backend) : backend_(backend) {}
  ~FinalCleanup() {}

  virtual void Run();
 private:
  disk_cache::BackendImpl* backend_;
  DISALLOW_EVIL_CONSTRUCTORS(FinalCleanup);
};

void FinalCleanup::Run() {
  backend_->CleanupCache();
}

}  // namespace

// ------------------------------------------------------------------------

namespace disk_cache {

int CreateCacheBackend(net::CacheType type, const FilePath& path, int max_bytes,
                       bool force, base::MessageLoopProxy* thread,
                       Backend** backend, CompletionCallback* callback) {
  DCHECK(callback);
  if (type == net::MEMORY_CACHE) {
    *backend = MemBackendImpl::CreateBackend(max_bytes);
    return *backend ? net::OK : net::ERR_FAILED;
  }
  DCHECK(thread);

  return BackendImpl::CreateBackend(path, force, max_bytes, type, kNone, thread,
                                    backend, callback);
}

// Returns the preferred maximum number of bytes for the cache given the
// number of available bytes.
int PreferedCacheSize(int64 available) {
  // Return 80% of the available space if there is not enough space to use
  // kDefaultCacheSize.
  if (available < kDefaultCacheSize * 10 / 8)
    return static_cast<int32>(available * 8 / 10);

  // Return kDefaultCacheSize if it uses 80% to 10% of the available space.
  if (available < kDefaultCacheSize * 10)
    return kDefaultCacheSize;

  // Return 10% of the available space if the target size
  // (2.5 * kDefaultCacheSize) is more than 10%.
  if (available < static_cast<int64>(kDefaultCacheSize) * 25)
    return static_cast<int32>(available / 10);

  // Return the target size (2.5 * kDefaultCacheSize) if it uses 10% to 1%
  // of the available space.
  if (available < static_cast<int64>(kDefaultCacheSize) * 250)
    return kDefaultCacheSize * 5 / 2;

  // Return 1% of the available space if it does not exceed kint32max.
  if (available < static_cast<int64>(kint32max) * 100)
    return static_cast<int32>(available / 100);

  return kint32max;
}

// ------------------------------------------------------------------------

// If the initialization of the cache fails, and force is true, we will discard
// the whole cache and create a new one. In order to process a potentially large
// number of files, we'll rename the cache folder to old_ + original_name +
// number, (located on the same parent folder), and spawn a worker thread to
// delete all the files on all the stale cache folders. The whole process can
// still fail if we are not able to rename the cache folder (for instance due to
// a sharing violation), and in that case a cache for this profile (on the
// desired path) cannot be created.
//
// Static.
int BackendImpl::CreateBackend(const FilePath& full_path, bool force,
                               int max_bytes, net::CacheType type,
                               uint32 flags, base::MessageLoopProxy* thread,
                               Backend** backend,
                               CompletionCallback* callback) {
  DCHECK(callback);
  CacheCreator* creator = new CacheCreator(full_path, force, max_bytes, type,
                                           flags, thread, backend, callback);
  // This object will self-destroy when finished.
  return creator->Run();
}

int BackendImpl::Init(CompletionCallback* callback) {
  background_queue_.Init(callback);
  return net::ERR_IO_PENDING;
}

BackendImpl::~BackendImpl() {
  background_queue_.WaitForPendingIO();

  if (background_queue_.BackgroundIsCurrentThread()) {
    // Unit tests may use the same thread for everything.
    CleanupCache();
  } else {
    background_queue_.background_thread()->PostTask(FROM_HERE,
                                                    new FinalCleanup(this));
    done_.Wait();
  }
}

// ------------------------------------------------------------------------

int32 BackendImpl::GetEntryCount() const {
  if (!index_)
    return 0;
  // num_entries includes entries already evicted.
  int32 not_deleted = data_->header.num_entries -
                      data_->header.lru.sizes[Rankings::DELETED];

  if (not_deleted < 0) {
    NOTREACHED();
    not_deleted = 0;
  }

  return not_deleted;
}

int BackendImpl::OpenEntry(const std::string& key, Entry** entry,
                           CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.OpenEntry(key, entry, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::CreateEntry(const std::string& key, Entry** entry,
                             CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.CreateEntry(key, entry, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::DoomEntry(const std::string& key,
                           CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.DoomEntry(key, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::DoomAllEntries(CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.DoomAllEntries(callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::DoomEntriesBetween(const base::Time initial_time,
                                    const base::Time end_time,
                                    CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.DoomEntriesBetween(initial_time, end_time, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::DoomEntriesSince(const base::Time initial_time,
                                  CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.DoomEntriesSince(initial_time, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::OpenNextEntry(void** iter, Entry** next_entry,
                               CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.OpenNextEntry(iter, next_entry, callback);
  return net::ERR_IO_PENDING;
}

void BackendImpl::EndEnumeration(void** iter) {
  background_queue_.EndEnumeration(*iter);
  *iter = NULL;
}

void BackendImpl::GetStats(StatsItems* stats) {
  if (disabled_)
    return;

  std::pair<std::string, std::string> item;

  item.first = "Entries";
  item.second = StringPrintf("%d", data_->header.num_entries);
  stats->push_back(item);

  item.first = "Pending IO";
  item.second = StringPrintf("%d", num_pending_io_);
  stats->push_back(item);

  item.first = "Max size";
  item.second = StringPrintf("%d", max_size_);
  stats->push_back(item);

  item.first = "Current size";
  item.second = StringPrintf("%d", data_->header.num_bytes);
  stats->push_back(item);

  stats_.GetItems(stats);
}

// ------------------------------------------------------------------------

int BackendImpl::SyncInit() {
  DCHECK(!init_);
  if (init_)
    return net::ERR_FAILED;

  bool create_files = false;
  if (!InitBackingStore(&create_files)) {
    ReportError(ERR_STORAGE_ERROR);
    return net::ERR_FAILED;
  }

  num_refs_ = num_pending_io_ = max_refs_ = 0;
  entry_count_ = byte_count_ = 0;

  if (!restarted_) {
    buffer_bytes_ = 0;
    trace_object_ = TraceObject::GetTraceObject();
    // Create a recurrent timer of 30 secs.
    int timer_delay = unit_test_ ? 1000 : 30000;
    timer_.Start(TimeDelta::FromMilliseconds(timer_delay), this,
                 &BackendImpl::OnStatsTimer);
  }

  init_ = true;

  if (data_->header.experiment != 0 && cache_type_ != net::DISK_CACHE) {
    // No experiment for other caches.
    return net::ERR_FAILED;
  }

  if (!(user_flags_ & disk_cache::kNoRandom)) {
    // The unit test controls directly what to test.
    if (!InitExperiment(&data_->header.experiment))
      return net::ERR_FAILED;

    new_eviction_ = (cache_type_ == net::DISK_CACHE);
  }

  if (!CheckIndex()) {
    ReportError(ERR_INIT_FAILED);
    return net::ERR_FAILED;
  }

  // We don't care if the value overflows. The only thing we care about is that
  // the id cannot be zero, because that value is used as "not dirty".
  // Increasing the value once per second gives us many years before we start
  // having collisions.
  data_->header.this_id++;
  if (!data_->header.this_id)
    data_->header.this_id++;

  if (data_->header.crash) {
    ReportError(ERR_PREVIOUS_CRASH);
  } else {
    ReportError(0);
    data_->header.crash = 1;
  }

  if (!block_files_.Init(create_files))
    return net::ERR_FAILED;

  // stats_ and rankings_ may end up calling back to us so we better be enabled.
  disabled_ = false;
  if (!stats_.Init(this, &data_->header.stats))
    return net::ERR_FAILED;

  disabled_ = !rankings_.Init(this, new_eviction_);
  eviction_.Init(this);

  // Setup load-time data only for the main cache.
  if (cache_type() == net::DISK_CACHE)
    SetFieldTrialInfo(GetSizeGroup());

  return disabled_ ? net::ERR_FAILED : net::OK;
}

void BackendImpl::CleanupCache() {
  Trace("Backend Cleanup");
  eviction_.Stop();
  timer_.Stop();

  if (init_) {
    if (data_)
      data_->header.crash = 0;

    File::WaitForPendingIO(&num_pending_io_);
    if (user_flags_ & kNoRandom) {
      // This is a net_unittest, verify that we are not 'leaking' entries.
      DCHECK(!num_refs_);
    }
  }
  factory_.RevokeAll();
  ptr_factory_.InvalidateWeakPtrs();
  done_.Signal();
}

// ------------------------------------------------------------------------

int BackendImpl::OpenPrevEntry(void** iter, Entry** prev_entry,
                               CompletionCallback* callback) {
  DCHECK(callback);
  background_queue_.OpenPrevEntry(iter, prev_entry, callback);
  return net::ERR_IO_PENDING;
}

int BackendImpl::SyncOpenEntry(const std::string& key, Entry** entry) {
  DCHECK(entry);
  *entry = OpenEntryImpl(key);
  return (*entry) ? net::OK : net::ERR_FAILED;
}

int BackendImpl::SyncCreateEntry(const std::string& key, Entry** entry) {
{'fraction_non_alphanumeric': 0.14194417378345847, 'fraction_numerical': 0.010374276379893391, 'mean_word_length': 3.121781242617529, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 75, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4487735', 'n_tokens_mistral': 10857, 'n_tokens_neox': 10474, 'n_words': 2935}