| Index: content/browser/gpu/shader_disk_cache.cc
|
| diff --git a/content/browser/gpu/shader_disk_cache.cc b/content/browser/gpu/shader_disk_cache.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a609b85a2376980dd82f9bbcc62db663bd0ee4ec
|
| --- /dev/null
|
| +++ b/content/browser/gpu/shader_disk_cache.cc
|
| @@ -0,0 +1,607 @@
|
| +// Copyright (c) 2013 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 "content/browser/gpu/shader_disk_cache.h"
|
| +
|
| +#include "base/macros.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/single_thread_task_runner.h"
|
| +#include "base/threading/thread_checker.h"
|
| +#include "gpu/command_buffer/common/constants.h"
|
| +#include "net/base/cache_type.h"
|
| +#include "net/base/io_buffer.h"
|
| +#include "net/base/net_errors.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +static const base::FilePath::CharType kGpuCachePath[] =
|
| + FILE_PATH_LITERAL("GPUCache");
|
| +
|
| +static ShaderCacheFactory* factory_instance = nullptr;
|
| +
|
| +} // namespace
|
| +
|
| +// ShaderDiskCacheEntry handles the work of caching/updating the cached
|
| +// shaders.
|
| +class ShaderDiskCacheEntry : public base::ThreadChecker {
|
| + public:
|
| + ShaderDiskCacheEntry(ShaderDiskCache* cache,
|
| + const std::string& key,
|
| + const std::string& shader);
|
| + ~ShaderDiskCacheEntry();
|
| +
|
| + void Cache();
|
| +
|
| + private:
|
| + enum OpType {
|
| + OPEN_ENTRY,
|
| + WRITE_DATA,
|
| + CREATE_ENTRY,
|
| + };
|
| +
|
| + void OnOpComplete(int rv);
|
| +
|
| + int OpenCallback(int rv);
|
| + int WriteCallback(int rv);
|
| + int IOComplete(int rv);
|
| +
|
| + ShaderDiskCache* cache_;
|
| + OpType op_type_;
|
| + std::string key_;
|
| + std::string shader_;
|
| + disk_cache::Entry* entry_;
|
| + base::WeakPtr<ShaderDiskCacheEntry> weak_ptr_;
|
| + base::WeakPtrFactory<ShaderDiskCacheEntry> weak_ptr_factory_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ShaderDiskCacheEntry);
|
| +};
|
| +
|
| +// ShaderDiskReadHelper is used to load all of the cached shaders from the
|
| +// disk cache and send to the memory cache.
|
| +class ShaderDiskReadHelper : public base::ThreadChecker {
|
| + public:
|
| + using ShaderLoadedCallback = ShaderDiskCache::ShaderLoadedCallback;
|
| + ShaderDiskReadHelper(ShaderDiskCache* cache,
|
| + const ShaderLoadedCallback& callback);
|
| + ~ShaderDiskReadHelper();
|
| +
|
| + void LoadCache();
|
| +
|
| + private:
|
| + enum OpType {
|
| + TERMINATE,
|
| + OPEN_NEXT,
|
| + OPEN_NEXT_COMPLETE,
|
| + READ_COMPLETE,
|
| + ITERATION_FINISHED
|
| + };
|
| +
|
| +
|
| + void OnOpComplete(int rv);
|
| +
|
| + int OpenNextEntry();
|
| + int OpenNextEntryComplete(int rv);
|
| + int ReadComplete(int rv);
|
| + int IterationComplete(int rv);
|
| +
|
| + ShaderDiskCache* cache_;
|
| + ShaderLoadedCallback shader_loaded_callback_;
|
| + OpType op_type_;
|
| + std::unique_ptr<disk_cache::Backend::Iterator> iter_;
|
| + scoped_refptr<net::IOBufferWithSize> buf_;
|
| + disk_cache::Entry* entry_;
|
| + base::WeakPtrFactory<ShaderDiskReadHelper> weak_ptr_factory_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ShaderDiskReadHelper);
|
| +};
|
| +
|
| +class ShaderClearHelper : public base::ThreadChecker {
|
| + public:
|
| + ShaderClearHelper(scoped_refptr<ShaderDiskCache> cache,
|
| + const base::FilePath& path,
|
| + const base::Time& delete_begin,
|
| + const base::Time& delete_end,
|
| + const base::Closure& callback);
|
| + ~ShaderClearHelper();
|
| +
|
| + void Clear();
|
| +
|
| + private:
|
| + enum OpType {
|
| + TERMINATE,
|
| + VERIFY_CACHE_SETUP,
|
| + DELETE_CACHE
|
| + };
|
| +
|
| + void DoClearShaderCache(int rv);
|
| +
|
| + scoped_refptr<ShaderDiskCache> cache_;
|
| + OpType op_type_;
|
| + base::FilePath path_;
|
| + base::Time delete_begin_;
|
| + base::Time delete_end_;
|
| + base::Closure callback_;
|
| + base::WeakPtrFactory<ShaderClearHelper> weak_ptr_factory_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ShaderClearHelper);
|
| +};
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ShaderDiskCacheEntry
|
| +
|
| +ShaderDiskCacheEntry::ShaderDiskCacheEntry(ShaderDiskCache* cache,
|
| + const std::string& key,
|
| + const std::string& shader)
|
| + : cache_(cache),
|
| + op_type_(OPEN_ENTRY),
|
| + key_(key),
|
| + shader_(shader),
|
| + entry_(nullptr),
|
| + weak_ptr_factory_(this) {
|
| + weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
|
| +}
|
| +
|
| +ShaderDiskCacheEntry::~ShaderDiskCacheEntry() {
|
| + DCHECK(CalledOnValidThread());
|
| + if (entry_)
|
| + entry_->Close();
|
| +}
|
| +
|
| +void ShaderDiskCacheEntry::Cache() {
|
| + DCHECK(CalledOnValidThread());
|
| + int rv = cache_->backend()->OpenEntry(
|
| + key_, &entry_, base::Bind(&ShaderDiskCacheEntry::OnOpComplete,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| + if (rv != net::ERR_IO_PENDING)
|
| + OnOpComplete(rv);
|
| +}
|
| +
|
| +void ShaderDiskCacheEntry::OnOpComplete(int rv) {
|
| + DCHECK(CalledOnValidThread());
|
| + // The function calls inside the switch block below can end up destroying
|
| + // |this|. So hold on to a WeakPtr<>, and terminate the while loop if |this|
|
| + // has been destroyed.
|
| + auto weak_ptr = std::move(weak_ptr_);
|
| + do {
|
| + switch (op_type_) {
|
| + case OPEN_ENTRY:
|
| + rv = OpenCallback(rv);
|
| + break;
|
| + case CREATE_ENTRY:
|
| + rv = WriteCallback(rv);
|
| + break;
|
| + case WRITE_DATA:
|
| + rv = IOComplete(rv);
|
| + break;
|
| + }
|
| + } while (rv != net::ERR_IO_PENDING && weak_ptr);
|
| + if (weak_ptr)
|
| + weak_ptr_ = std::move(weak_ptr);
|
| +}
|
| +
|
| +int ShaderDiskCacheEntry::OpenCallback(int rv) {
|
| + DCHECK(CalledOnValidThread());
|
| + if (rv == net::OK) {
|
| + cache_->backend()->OnExternalCacheHit(key_);
|
| + cache_->EntryComplete(this);
|
| + return rv;
|
| + }
|
| +
|
| + op_type_ = CREATE_ENTRY;
|
| + return cache_->backend()->CreateEntry(
|
| + key_, &entry_, base::Bind(&ShaderDiskCacheEntry::OnOpComplete,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +int ShaderDiskCacheEntry::WriteCallback(int rv) {
|
| + DCHECK(CalledOnValidThread());
|
| + if (rv != net::OK) {
|
| + LOG(ERROR) << "Failed to create shader cache entry: " << rv;
|
| + cache_->EntryComplete(this);
|
| + return rv;
|
| + }
|
| +
|
| + op_type_ = WRITE_DATA;
|
| + scoped_refptr<net::StringIOBuffer> io_buf = new net::StringIOBuffer(shader_);
|
| + return entry_->WriteData(1, 0, io_buf.get(), shader_.length(),
|
| + base::Bind(&ShaderDiskCacheEntry::OnOpComplete,
|
| + weak_ptr_factory_.GetWeakPtr()),
|
| + false);
|
| +}
|
| +
|
| +int ShaderDiskCacheEntry::IOComplete(int rv) {
|
| + DCHECK(CalledOnValidThread());
|
| + cache_->EntryComplete(this);
|
| + return rv;
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ShaderDiskReadHelper
|
| +
|
| +ShaderDiskReadHelper::ShaderDiskReadHelper(ShaderDiskCache* cache,
|
| + const ShaderLoadedCallback& callback)
|
| + : cache_(cache),
|
| + shader_loaded_callback_(callback),
|
| + op_type_(OPEN_NEXT),
|
| + buf_(NULL),
|
| + entry_(NULL),
|
| + weak_ptr_factory_(this) {}
|
| +
|
| +ShaderDiskReadHelper::~ShaderDiskReadHelper() {
|
| + DCHECK(CalledOnValidThread());
|
| + if (entry_)
|
| + entry_->Close();
|
| + iter_ = nullptr;
|
| +}
|
| +
|
| +void ShaderDiskReadHelper::LoadCache() {
|
| + DCHECK(CalledOnValidThread());
|
| + OnOpComplete(net::OK);
|
| +}
|
| +
|
| +void ShaderDiskReadHelper::OnOpComplete(int rv) {
|
| + DCHECK(CalledOnValidThread());
|
| + do {
|
| + switch (op_type_) {
|
| + case OPEN_NEXT:
|
| + rv = OpenNextEntry();
|
| + break;
|
| + case OPEN_NEXT_COMPLETE:
|
| + rv = OpenNextEntryComplete(rv);
|
| + break;
|
| + case READ_COMPLETE:
|
| + rv = ReadComplete(rv);
|
| + break;
|
| + case ITERATION_FINISHED:
|
| + rv = IterationComplete(rv);
|
| + break;
|
| + case TERMINATE:
|
| + cache_->ReadComplete();
|
| + rv = net::ERR_IO_PENDING; // break the loop
|
| + break;
|
| + }
|
| + } while (rv != net::ERR_IO_PENDING);
|
| +}
|
| +
|
| +int ShaderDiskReadHelper::OpenNextEntry() {
|
| + DCHECK(CalledOnValidThread());
|
| + op_type_ = OPEN_NEXT_COMPLETE;
|
| + if (!iter_)
|
| + iter_ = cache_->backend()->CreateIterator();
|
| + return iter_->OpenNextEntry(&entry_,
|
| + base::Bind(&ShaderDiskReadHelper::OnOpComplete,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +int ShaderDiskReadHelper::OpenNextEntryComplete(int rv) {
|
| + DCHECK(CalledOnValidThread());
|
| + if (rv == net::ERR_FAILED) {
|
| + iter_.reset();
|
| + op_type_ = ITERATION_FINISHED;
|
| + return net::OK;
|
| + }
|
| +
|
| + if (rv < 0)
|
| + return rv;
|
| +
|
| + op_type_ = READ_COMPLETE;
|
| + buf_ = new net::IOBufferWithSize(entry_->GetDataSize(1));
|
| + return entry_->ReadData(1, 0, buf_.get(), buf_->size(),
|
| + base::Bind(&ShaderDiskReadHelper::OnOpComplete,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +int ShaderDiskReadHelper::ReadComplete(int rv) {
|
| + DCHECK(CalledOnValidThread());
|
| + if (rv && rv == buf_->size() && !shader_loaded_callback_.is_null()) {
|
| + shader_loaded_callback_.Run(entry_->GetKey(),
|
| + std::string(buf_->data(), buf_->size()));
|
| + }
|
| +
|
| + buf_ = NULL;
|
| + entry_->Close();
|
| + entry_ = NULL;
|
| +
|
| + op_type_ = OPEN_NEXT;
|
| + return net::OK;
|
| +}
|
| +
|
| +int ShaderDiskReadHelper::IterationComplete(int rv) {
|
| + DCHECK(CalledOnValidThread());
|
| + iter_.reset();
|
| + op_type_ = TERMINATE;
|
| + return net::OK;
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ShaderClearHelper
|
| +
|
| +ShaderClearHelper::ShaderClearHelper(scoped_refptr<ShaderDiskCache> cache,
|
| + const base::FilePath& path,
|
| + const base::Time& delete_begin,
|
| + const base::Time& delete_end,
|
| + const base::Closure& callback)
|
| + : cache_(std::move(cache)),
|
| + op_type_(VERIFY_CACHE_SETUP),
|
| + path_(path),
|
| + delete_begin_(delete_begin),
|
| + delete_end_(delete_end),
|
| + callback_(callback),
|
| + weak_ptr_factory_(this) {}
|
| +
|
| +ShaderClearHelper::~ShaderClearHelper() {
|
| + DCHECK(CalledOnValidThread());
|
| +}
|
| +
|
| +void ShaderClearHelper::Clear() {
|
| + DCHECK(CalledOnValidThread());
|
| + DoClearShaderCache(net::OK);
|
| +}
|
| +
|
| +void ShaderClearHelper::DoClearShaderCache(int rv) {
|
| + DCHECK(CalledOnValidThread());
|
| + while (rv != net::ERR_IO_PENDING) {
|
| + switch (op_type_) {
|
| + case VERIFY_CACHE_SETUP:
|
| + rv = cache_->SetAvailableCallback(
|
| + base::Bind(&ShaderClearHelper::DoClearShaderCache,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| + op_type_ = DELETE_CACHE;
|
| + break;
|
| + case DELETE_CACHE:
|
| + rv = cache_->Clear(delete_begin_, delete_end_,
|
| + base::Bind(&ShaderClearHelper::DoClearShaderCache,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| + op_type_ = TERMINATE;
|
| + break;
|
| + case TERMINATE:
|
| + callback_.Run();
|
| + // Calling CacheCleared() destroys |this|.
|
| + ShaderCacheFactory::GetInstance()->CacheCleared(path_);
|
| + rv = net::ERR_IO_PENDING; // Break the loop.
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ShaderCacheFactory
|
| +
|
| +// static
|
| +void ShaderCacheFactory::InitInstance(
|
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
| + scoped_refptr<base::SingleThreadTaskRunner> cache_task_runner) {
|
| + if (task_runner->BelongsToCurrentThread()) {
|
| + CreateFactoryInstance(std::move(cache_task_runner));
|
| + } else {
|
| + task_runner->PostTask(FROM_HERE,
|
| + base::Bind(&ShaderCacheFactory::CreateFactoryInstance,
|
| + std::move(cache_task_runner)));
|
| + }
|
| +}
|
| +
|
| +// static
|
| +ShaderCacheFactory* ShaderCacheFactory::GetInstance() {
|
| + DCHECK(!factory_instance || factory_instance->CalledOnValidThread());
|
| + return factory_instance;
|
| +}
|
| +
|
| +ShaderCacheFactory::ShaderCacheFactory(
|
| + scoped_refptr<base::SingleThreadTaskRunner> cache_task_runner)
|
| + : cache_task_runner_(std::move(cache_task_runner)) {}
|
| +
|
| +ShaderCacheFactory::~ShaderCacheFactory() {
|
| +}
|
| +
|
| +// static
|
| +void ShaderCacheFactory::CreateFactoryInstance(
|
| + scoped_refptr<base::SingleThreadTaskRunner> cache_task_runner) {
|
| + DCHECK(!factory_instance);
|
| + factory_instance = new ShaderCacheFactory(std::move(cache_task_runner));
|
| +}
|
| +
|
| +void ShaderCacheFactory::SetCacheInfo(int32_t client_id,
|
| + const base::FilePath& path) {
|
| + DCHECK(CalledOnValidThread());
|
| + client_id_to_path_map_[client_id] = path;
|
| +}
|
| +
|
| +void ShaderCacheFactory::RemoveCacheInfo(int32_t client_id) {
|
| + DCHECK(CalledOnValidThread());
|
| + client_id_to_path_map_.erase(client_id);
|
| +}
|
| +
|
| +scoped_refptr<ShaderDiskCache> ShaderCacheFactory::Get(int32_t client_id) {
|
| + DCHECK(CalledOnValidThread());
|
| + ClientIdToPathMap::iterator iter = client_id_to_path_map_.find(client_id);
|
| + if (iter == client_id_to_path_map_.end())
|
| + return NULL;
|
| + return ShaderCacheFactory::GetByPath(iter->second);
|
| +}
|
| +
|
| +scoped_refptr<ShaderDiskCache> ShaderCacheFactory::GetByPath(
|
| + const base::FilePath& path) {
|
| + DCHECK(CalledOnValidThread());
|
| + ShaderCacheMap::iterator iter = shader_cache_map_.find(path);
|
| + if (iter != shader_cache_map_.end())
|
| + return iter->second;
|
| +
|
| + ShaderDiskCache* cache = new ShaderDiskCache(path);
|
| + cache->Init(cache_task_runner_);
|
| + return cache;
|
| +}
|
| +
|
| +void ShaderCacheFactory::AddToCache(const base::FilePath& key,
|
| + ShaderDiskCache* cache) {
|
| + DCHECK(CalledOnValidThread());
|
| + shader_cache_map_[key] = cache;
|
| +}
|
| +
|
| +void ShaderCacheFactory::RemoveFromCache(const base::FilePath& key) {
|
| + DCHECK(CalledOnValidThread());
|
| + shader_cache_map_.erase(key);
|
| +}
|
| +
|
| +void ShaderCacheFactory::ClearByPath(const base::FilePath& path,
|
| + const base::Time& delete_begin,
|
| + const base::Time& delete_end,
|
| + const base::Closure& callback) {
|
| + DCHECK(CalledOnValidThread());
|
| + DCHECK(!callback.is_null());
|
| +
|
| + auto helper = base::MakeUnique<ShaderClearHelper>(
|
| + GetByPath(path), path, delete_begin, delete_end, callback);
|
| +
|
| + // We could receive requests to clear the same path with different
|
| + // begin/end times. So, we keep a list of requests. If we haven't seen this
|
| + // path before we kick off the clear and add it to the list. If we have see it
|
| + // already, then we already have a clear running. We add this clear to the
|
| + // list and wait for any previous clears to finish.
|
| + ShaderClearMap::iterator iter = shader_clear_map_.find(path);
|
| + if (iter != shader_clear_map_.end()) {
|
| + iter->second.push(std::move(helper));
|
| + return;
|
| + }
|
| +
|
| + // Insert the helper in the map before calling Clear(), since it can lead to a
|
| + // call back into CacheCleared().
|
| + ShaderClearHelper* helper_ptr = helper.get();
|
| + shader_clear_map_.insert(
|
| + std::pair<base::FilePath, ShaderClearQueue>(path, ShaderClearQueue()));
|
| + shader_clear_map_[path].push(std::move(helper));
|
| + helper_ptr->Clear();
|
| +}
|
| +
|
| +void ShaderCacheFactory::CacheCleared(const base::FilePath& path) {
|
| + DCHECK(CalledOnValidThread());
|
| +
|
| + ShaderClearMap::iterator iter = shader_clear_map_.find(path);
|
| + if (iter == shader_clear_map_.end()) {
|
| + LOG(ERROR) << "Completed clear but missing clear helper.";
|
| + return;
|
| + }
|
| +
|
| + iter->second.pop();
|
| +
|
| + // If there are remaining items in the list we trigger the Clear on the
|
| + // next one.
|
| + if (!iter->second.empty()) {
|
| + iter->second.front()->Clear();
|
| + return;
|
| + }
|
| +
|
| + shader_clear_map_.erase(iter);
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// ShaderDiskCache
|
| +
|
| +ShaderDiskCache::ShaderDiskCache(const base::FilePath& cache_path)
|
| + : cache_available_(false),
|
| + cache_path_(cache_path),
|
| + is_initialized_(false) {
|
| + ShaderCacheFactory::GetInstance()->AddToCache(cache_path_, this);
|
| +}
|
| +
|
| +ShaderDiskCache::~ShaderDiskCache() {
|
| + ShaderCacheFactory::GetInstance()->RemoveFromCache(cache_path_);
|
| +}
|
| +
|
| +void ShaderDiskCache::Init(
|
| + scoped_refptr<base::SingleThreadTaskRunner> cache_task_runner) {
|
| + if (is_initialized_) {
|
| + NOTREACHED(); // can't initialize disk cache twice.
|
| + return;
|
| + }
|
| + is_initialized_ = true;
|
| +
|
| + int rv = disk_cache::CreateCacheBackend(
|
| + net::SHADER_CACHE, net::CACHE_BACKEND_DEFAULT,
|
| + cache_path_.Append(kGpuCachePath),
|
| + gpu::kDefaultMaxProgramCacheMemoryBytes, true, cache_task_runner, NULL,
|
| + &backend_, base::Bind(&ShaderDiskCache::CacheCreatedCallback, this));
|
| +
|
| + if (rv == net::OK)
|
| + cache_available_ = true;
|
| +}
|
| +
|
| +void ShaderDiskCache::Cache(const std::string& key, const std::string& shader) {
|
| + if (!cache_available_)
|
| + return;
|
| +
|
| + auto shim = base::MakeUnique<ShaderDiskCacheEntry>(this, key, shader);
|
| + shim->Cache();
|
| + auto* raw_ptr = shim.get();
|
| + entries_.insert(std::make_pair(raw_ptr, std::move(shim)));
|
| +}
|
| +
|
| +int ShaderDiskCache::Clear(
|
| + const base::Time begin_time, const base::Time end_time,
|
| + const net::CompletionCallback& completion_callback) {
|
| + int rv;
|
| + if (begin_time.is_null()) {
|
| + rv = backend_->DoomAllEntries(completion_callback);
|
| + } else {
|
| + rv = backend_->DoomEntriesBetween(begin_time, end_time,
|
| + completion_callback);
|
| + }
|
| + return rv;
|
| +}
|
| +
|
| +int32_t ShaderDiskCache::Size() {
|
| + if (!cache_available_)
|
| + return -1;
|
| + return backend_->GetEntryCount();
|
| +}
|
| +
|
| +int ShaderDiskCache::SetAvailableCallback(
|
| + const net::CompletionCallback& callback) {
|
| + if (cache_available_)
|
| + return net::OK;
|
| + available_callback_ = callback;
|
| + return net::ERR_IO_PENDING;
|
| +}
|
| +
|
| +void ShaderDiskCache::CacheCreatedCallback(int rv) {
|
| + if (rv != net::OK) {
|
| + LOG(ERROR) << "Shader Cache Creation failed: " << rv;
|
| + return;
|
| + }
|
| + helper_ =
|
| + base::MakeUnique<ShaderDiskReadHelper>(this, shader_loaded_callback_);
|
| + helper_->LoadCache();
|
| +}
|
| +
|
| +void ShaderDiskCache::EntryComplete(ShaderDiskCacheEntry* entry) {
|
| + entries_.erase(entry);
|
| + if (entries_.empty() && !cache_complete_callback_.is_null())
|
| + cache_complete_callback_.Run(net::OK);
|
| +}
|
| +
|
| +void ShaderDiskCache::ReadComplete() {
|
| + helper_ = nullptr;
|
| +
|
| + // The cache is considered available after we have finished reading any
|
| + // of the old cache values off disk. This prevents a potential race where we
|
| + // are reading from disk and execute a cache clear at the same time.
|
| + cache_available_ = true;
|
| + if (!available_callback_.is_null()) {
|
| + available_callback_.Run(net::OK);
|
| + available_callback_.Reset();
|
| + }
|
| +}
|
| +
|
| +int ShaderDiskCache::SetCacheCompleteCallback(
|
| + const net::CompletionCallback& callback) {
|
| + if (entries_.empty()) {
|
| + return net::OK;
|
| + }
|
| + cache_complete_callback_ = callback;
|
| + return net::ERR_IO_PENDING;
|
| +}
|
| +
|
| +} // namespace content
|
| +
|
|
|