// Copyright (c) 2012 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 #include "base/files/file_path.h" #include "base/macros.h" #include "base/metrics/field_trial.h" #include "base/single_thread_task_runner.h" #include "base/task/task_scheduler/task_scheduler.h" #include "net/base/cache_type.h" #include "net/base/net_errors.h" #include "net/disk_cache/backend_cleanup_tracker.h" #include "net/disk_cache/blockfile/backend_impl.h" #include "net/disk_cache/cache_util.h" #include "net/disk_cache/disk_cache.h" #include "net/disk_cache/memory/mem_backend_impl.h" #include "net/disk_cache/simple/simple_backend_impl.h" namespace { // Builds an instance of the backend depending on platform, type, experiments // etc. Takes care of the retry state. This object will self-destroy when // finished. class CacheCreator { public: CacheCreator(const base::FilePath& path, bool force, int max_bytes, net::CacheType type, net::BackendType backend_type, uint32_t flags, net::NetLog* net_log, std::unique_ptr* backend, base::OnceClosure post_cleanup_callback, net::CompletionOnceCallback callback); int TryCreateCleanupTrackerAndRun(); // Creates the backend, the cleanup context for it having been already // established... or purposefully left as null. int Run(); private: ~CacheCreator(); void DoCallback(int result); void OnIOComplete(int result); const base::FilePath path_; bool force_; bool retry_; int max_bytes_; net::CacheType type_; net::BackendType backend_type_; #if !defined(OS_ANDROID) uint32_t flags_; #endif std::unique_ptr* backend_; base::OnceClosure post_cleanup_callback_; net::CompletionOnceCallback callback_; std::unique_ptr created_cache_; net::NetLog* net_log_; scoped_refptr cleanup_tracker_; DISALLOW_COPY_AND_ASSIGN(CacheCreator); }; CacheCreator::CacheCreator(const base::FilePath& path, bool force, int max_bytes, net::CacheType type, net::BackendType backend_type, uint32_t flags, net::NetLog* net_log, std::unique_ptr* backend, base::OnceClosure post_cleanup_callback, net::CompletionOnceCallback callback) : path_(path), force_(force), retry_(false), max_bytes_(max_bytes), type_(type), backend_type_(backend_type), #if !defined(OS_ANDROID) flags_(flags), #endif backend_(backend), post_cleanup_callback_(std::move(post_cleanup_callback)), callback_(std::move(callback)), net_log_(net_log) { } CacheCreator::~CacheCreator() = default; int CacheCreator::Run() { #if defined(OS_ANDROID) static const bool kSimpleBackendIsDefault = true; #else static const bool kSimpleBackendIsDefault = false; #endif if (backend_type_ == net::CACHE_BACKEND_SIMPLE || (backend_type_ == net::CACHE_BACKEND_DEFAULT && kSimpleBackendIsDefault)) { disk_cache::SimpleBackendImpl* simple_cache = new disk_cache::SimpleBackendImpl(path_, cleanup_tracker_.get(), /* file_tracker = */ nullptr, max_bytes_, type_, net_log_); created_cache_.reset(simple_cache); return simple_cache->Init( base::Bind(&CacheCreator::OnIOComplete, base::Unretained(this))); } // Avoid references to blockfile functions on Android to reduce binary size. #if defined(OS_ANDROID) return net::ERR_FAILED; #else disk_cache::BackendImpl* new_cache = new disk_cache::BackendImpl( path_, cleanup_tracker_.get(), /*cache_thread = */ nullptr, net_log_); created_cache_.reset(new_cache); new_cache->SetMaxSize(max_bytes_); new_cache->SetType(type_); new_cache->SetFlags(flags_); int rv = new_cache->Init( base::Bind(&CacheCreator::OnIOComplete, base::Unretained(this))); DCHECK_EQ(net::ERR_IO_PENDING, rv); return rv; #endif } int CacheCreator::TryCreateCleanupTrackerAndRun() { // Before creating a cache Backend, a BackendCleanupTracker object is needed // so there is a place to keep track of outstanding I/O even after the backend // object itself is destroyed, so that further use of the directory // doesn't race with those outstanding disk I/O ops. // This method's purpose it to grab exlusive ownership of a fresh // BackendCleanupTracker for the cache path, and then move on to Run(), // which will take care of creating the actual cache backend. It's possible // that something else is currently making use of the directory, in which // case BackendCleanupTracker::TryCreate will fail, but will just have // TryCreateCleanupTrackerAndRun run again at an opportune time to make // another attempt. // The resulting BackendCleanupTracker is stored into a scoped_refptr member // so that it's kept alive while |this| CacheCreator exists , so that in the // case Run() needs to retry Backend creation the same BackendCleanupTracker // is used for both attempts, and |post_cleanup_callback_| gets called after // the second try, not the first one. cleanup_tracker_ = disk_cache::BackendCleanupTracker::TryCreate( path_, base::BindOnce(base::IgnoreResult( &CacheCreator::TryCreateCleanupTrackerAndRun), base::Unretained(this))); if (!cleanup_tracker_) return net::ERR_IO_PENDING; if (!post_cleanup_callback_.is_null()) cleanup_tracker_->AddPostCleanupCallback(std::move(post_cleanup_callback_)); return Run(); } void CacheCreator::DoCallback(int result) { DCHECK_NE(net::ERR_IO_PENDING, result); if (result == net::OK) { *backend_ = std::move(created_cache_); } else { LOG(ERROR) << "Unable to create cache"; created_cache_.reset(); } std::move(callback_).Run(result); delete this; } // If the initialization of the cache fails, and |force| is true, we will // discard the whole cache and create a new one. 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; created_cache_.reset(); if (!disk_cache::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); } } // namespace namespace disk_cache { int CreateCacheBackendImpl(net::CacheType type, net::BackendType backend_type, const base::FilePath& path, int max_bytes, bool force, net::NetLog* net_log, std::unique_ptr* backend, base::OnceClosure post_cleanup_callback, net::CompletionOnceCallback callback) { DCHECK(!callback.is_null()); if (type == net::MEMORY_CACHE) { std::unique_ptr mem_backend_impl = disk_cache::MemBackendImpl::CreateBackend(max_bytes, net_log); if (mem_backend_impl) { mem_backend_impl->SetPostCleanupCallback( std::move(post_cleanup_callback)); *backend = std::move(mem_backend_impl); return net::OK; } else { if (!post_cleanup_callback.is_null()) base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(post_cleanup_callback)); return net::ERR_FAILED; } } bool had_post_cleanup_callback = !post_cleanup_callback.is_null(); CacheCreator* creator = new CacheCreator( path, force, max_bytes, type, backend_type, kNone, net_log, backend, std::move(post_cleanup_callback), std::move(callback)); if (type == net::DISK_CACHE || type == net::MEDIA_CACHE) { DCHECK(!had_post_cleanup_callback); return creator->Run(); } return creator->TryCreateCleanupTrackerAndRun(); } int CreateCacheBackend(net::CacheType type, net::BackendType backend_type, const base::FilePath& path, int max_bytes, bool force, net::NetLog* net_log, std::unique_ptr* backend, net::CompletionOnceCallback callback) { return CreateCacheBackendImpl(type, backend_type, path, max_bytes, force, net_log, backend, base::OnceClosure(), std::move(callback)); } int CreateCacheBackend(net::CacheType type, net::BackendType backend_type, const base::FilePath& path, int max_bytes, bool force, net::NetLog* net_log, std::unique_ptr* backend, base::OnceClosure post_cleanup_callback, net::CompletionOnceCallback callback) { return CreateCacheBackendImpl( type, backend_type, path, max_bytes, force, net_log, backend, std::move(post_cleanup_callback), std::move(callback)); } void FlushCacheThreadForTesting() { // For simple backend. SimpleBackendImpl::FlushWorkerPoolForTesting(); base::TaskScheduler::GetInstance()->FlushForTesting(); // Block backend. BackendImpl::FlushForTesting(); } int Backend::CalculateSizeOfEntriesBetween(base::Time initial_time, base::Time end_time, CompletionOnceCallback callback) { return net::ERR_NOT_IMPLEMENTED; } uint8_t Backend::GetEntryInMemoryData(const std::string& key) { return 0; } void Backend::SetEntryInMemoryData(const std::string& key, uint8_t data) {} } // namespace disk_cache