123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- // Copyright 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 "components/nacl/browser/pnacl_translation_cache.h"
- #include <string.h>
- #include <string>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/files/file_path.h"
- #include "base/logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/threading/thread_checker.h"
- #include "components/nacl/common/pnacl_types.h"
- #include "content/public/browser/browser_task_traits.h"
- #include "content/public/browser/browser_thread.h"
- #include "net/base/io_buffer.h"
- #include "net/base/net_errors.h"
- #include "net/disk_cache/disk_cache.h"
- using base::NumberToString;
- namespace {
- void CloseDiskCacheEntry(disk_cache::Entry* entry) { entry->Close(); }
- } // namespace
- namespace pnacl {
- // This is in pnacl namespace instead of static so they can be used
- // by the unit test.
- const int kMaxMemCacheSize = 100 * 1024 * 1024;
- //////////////////////////////////////////////////////////////////////
- // Handle Reading/Writing to Cache.
- // PnaclTranslationCacheEntry is a shim that provides storage for the
- // 'key' and 'data' strings as the disk_cache is performing various async
- // operations. It also tracks the open disk_cache::Entry
- // and ensures that the entry is closed.
- class PnaclTranslationCacheEntry
- : public base::RefCountedThreadSafe<PnaclTranslationCacheEntry> {
- public:
- static PnaclTranslationCacheEntry* GetReadEntry(
- base::WeakPtr<PnaclTranslationCache> cache,
- const std::string& key,
- GetNexeCallback callback);
- static PnaclTranslationCacheEntry* GetWriteEntry(
- base::WeakPtr<PnaclTranslationCache> cache,
- const std::string& key,
- net::DrainableIOBuffer* write_nexe,
- CompletionOnceCallback callback);
- PnaclTranslationCacheEntry(const PnaclTranslationCacheEntry&) = delete;
- PnaclTranslationCacheEntry& operator=(const PnaclTranslationCacheEntry&) =
- delete;
- void Start();
- // Writes: ---
- // v |
- // Start -> Open Existing --------------> Write ---> Close
- // \ ^
- // \ /
- // --> Create --
- // Reads:
- // Start -> Open --------Read ----> Close
- // | ^
- // |__|
- enum CacheStep {
- UNINITIALIZED,
- OPEN_ENTRY,
- CREATE_ENTRY,
- TRANSFER_ENTRY,
- CLOSE_ENTRY,
- FINISHED
- };
- private:
- friend class base::RefCountedThreadSafe<PnaclTranslationCacheEntry>;
- PnaclTranslationCacheEntry(base::WeakPtr<PnaclTranslationCache> cache,
- const std::string& key,
- bool is_read);
- ~PnaclTranslationCacheEntry();
- // Try to open an existing entry in the backend
- void OpenEntry();
- // Create a new entry in the backend (for writes)
- void CreateEntry();
- // Write |len| bytes to the backend, starting at |offset|
- void WriteEntry(int offset, int len);
- // Read |len| bytes from the backend, starting at |offset|
- void ReadEntry(int offset, int len);
- // If there was an error, doom the entry. Then post a task to the IO
- // thread to close (and delete) it.
- void CloseEntry(int rv);
- // Call the user callback, and signal to the cache to delete this.
- void Finish(int rv);
- // Used as the callback for all operations to the backend except those that
- // first open/create entries. Handle state transitions, track bytes
- // transferred, and call the other helper methods.
- void DispatchNext(int rv);
- // Like above but for first opening or creating of |entry_|.
- void SaveEntryAndDispatchNext(disk_cache::EntryResult result);
- base::WeakPtr<PnaclTranslationCache> cache_;
- std::string key_;
- raw_ptr<disk_cache::Entry> entry_;
- CacheStep step_;
- bool is_read_;
- GetNexeCallback read_callback_;
- CompletionOnceCallback write_callback_;
- scoped_refptr<net::DrainableIOBuffer> io_buf_;
- base::ThreadChecker thread_checker_;
- };
- // static
- PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetReadEntry(
- base::WeakPtr<PnaclTranslationCache> cache,
- const std::string& key,
- GetNexeCallback callback) {
- PnaclTranslationCacheEntry* entry(
- new PnaclTranslationCacheEntry(cache, key, true));
- entry->read_callback_ = std::move(callback);
- return entry;
- }
- // static
- PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetWriteEntry(
- base::WeakPtr<PnaclTranslationCache> cache,
- const std::string& key,
- net::DrainableIOBuffer* write_nexe,
- CompletionOnceCallback callback) {
- PnaclTranslationCacheEntry* entry(
- new PnaclTranslationCacheEntry(cache, key, false));
- entry->io_buf_ = write_nexe;
- entry->write_callback_ = std::move(callback);
- return entry;
- }
- PnaclTranslationCacheEntry::PnaclTranslationCacheEntry(
- base::WeakPtr<PnaclTranslationCache> cache,
- const std::string& key,
- bool is_read)
- : cache_(cache),
- key_(key),
- entry_(nullptr),
- step_(UNINITIALIZED),
- is_read_(is_read) {}
- PnaclTranslationCacheEntry::~PnaclTranslationCacheEntry() {
- // Ensure we have called the user's callback
- if (step_ != FINISHED) {
- if (!read_callback_.is_null()) {
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE, base::BindOnce(std::move(read_callback_), net::ERR_ABORTED,
- scoped_refptr<net::DrainableIOBuffer>()));
- }
- if (!write_callback_.is_null()) {
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(write_callback_), net::ERR_ABORTED));
- }
- }
- }
- void PnaclTranslationCacheEntry::Start() {
- DCHECK(thread_checker_.CalledOnValidThread());
- step_ = OPEN_ENTRY;
- OpenEntry();
- }
- // OpenEntry, CreateEntry, WriteEntry, ReadEntry and CloseEntry are only called
- // from DispatchNext, so they know that cache_ is still valid.
- void PnaclTranslationCacheEntry::OpenEntry() {
- disk_cache::EntryResult result = cache_->backend()->OpenEntry(
- key_, net::HIGHEST,
- base::BindOnce(&PnaclTranslationCacheEntry::SaveEntryAndDispatchNext,
- this));
- if (result.net_error() != net::ERR_IO_PENDING)
- SaveEntryAndDispatchNext(std::move(result));
- }
- void PnaclTranslationCacheEntry::CreateEntry() {
- disk_cache::EntryResult result = cache_->backend()->CreateEntry(
- key_, net::HIGHEST,
- base::BindOnce(&PnaclTranslationCacheEntry::SaveEntryAndDispatchNext,
- this));
- if (result.net_error() != net::ERR_IO_PENDING)
- SaveEntryAndDispatchNext(std::move(result));
- }
- void PnaclTranslationCacheEntry::WriteEntry(int offset, int len) {
- DCHECK(io_buf_->BytesRemaining() == len);
- int rv = entry_->WriteData(
- 1, offset, io_buf_.get(), len,
- base::BindOnce(&PnaclTranslationCacheEntry::DispatchNext, this), false);
- if (rv != net::ERR_IO_PENDING)
- DispatchNext(rv);
- }
- void PnaclTranslationCacheEntry::ReadEntry(int offset, int len) {
- int rv = entry_->ReadData(
- 1, offset, io_buf_.get(), len,
- base::BindOnce(&PnaclTranslationCacheEntry::DispatchNext, this));
- if (rv != net::ERR_IO_PENDING)
- DispatchNext(rv);
- }
- void PnaclTranslationCacheEntry::CloseEntry(int rv) {
- DCHECK(entry_);
- if (rv < 0) {
- LOG(ERROR) << "Failed to close entry: " << net::ErrorToString(rv);
- entry_->Doom();
- }
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE, base::BindOnce(&CloseDiskCacheEntry, entry_));
- Finish(rv);
- }
- void PnaclTranslationCacheEntry::Finish(int rv) {
- step_ = FINISHED;
- if (is_read_) {
- if (!read_callback_.is_null()) {
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE, base::BindOnce(std::move(read_callback_), rv, io_buf_));
- }
- } else {
- if (!write_callback_.is_null()) {
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE, base::BindOnce(std::move(write_callback_), rv));
- }
- }
- cache_->OpComplete(this);
- }
- void PnaclTranslationCacheEntry::DispatchNext(int rv) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!cache_)
- return;
- switch (step_) {
- case UNINITIALIZED:
- case FINISHED:
- LOG(ERROR) << "DispatchNext called uninitialized";
- break;
- case OPEN_ENTRY:
- if (rv == net::OK) {
- step_ = TRANSFER_ENTRY;
- if (is_read_) {
- int bytes_to_transfer = entry_->GetDataSize(1);
- io_buf_ = base::MakeRefCounted<net::DrainableIOBuffer>(
- base::MakeRefCounted<net::IOBuffer>(bytes_to_transfer),
- bytes_to_transfer);
- ReadEntry(0, bytes_to_transfer);
- } else {
- WriteEntry(0, io_buf_->size());
- }
- } else {
- if (rv != net::ERR_FAILED) {
- // ERROR_FAILED is what we expect if the entry doesn't exist.
- LOG(ERROR) << "OpenEntry failed: " << net::ErrorToString(rv);
- }
- if (is_read_) {
- // Just a cache miss, not necessarily an error.
- entry_ = nullptr;
- Finish(rv);
- } else {
- step_ = CREATE_ENTRY;
- CreateEntry();
- }
- }
- break;
- case CREATE_ENTRY:
- if (rv == net::OK) {
- step_ = TRANSFER_ENTRY;
- WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
- } else {
- LOG(ERROR) << "Failed to Create Entry: " << net::ErrorToString(rv);
- Finish(rv);
- }
- break;
- case TRANSFER_ENTRY:
- if (rv < 0) {
- // We do not call DispatchNext directly if WriteEntry/ReadEntry returns
- // ERR_IO_PENDING, and the callback should not return that value either.
- LOG(ERROR) << "Failed to complete write to entry: "
- << net::ErrorToString(rv);
- step_ = CLOSE_ENTRY;
- CloseEntry(rv);
- break;
- } else if (rv > 0) {
- io_buf_->DidConsume(rv);
- if (io_buf_->BytesRemaining() > 0) {
- is_read_
- ? ReadEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining())
- : WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
- break;
- }
- }
- // rv == 0 or we fell through (i.e. we have transferred all the bytes)
- step_ = CLOSE_ENTRY;
- DCHECK(io_buf_->BytesConsumed() == io_buf_->size());
- if (is_read_)
- io_buf_->SetOffset(0);
- CloseEntry(0);
- break;
- case CLOSE_ENTRY:
- step_ = UNINITIALIZED;
- break;
- }
- }
- void PnaclTranslationCacheEntry::SaveEntryAndDispatchNext(
- disk_cache::EntryResult result) {
- int rv = result.net_error();
- entry_ = result.ReleaseEntry();
- DispatchNext(rv);
- }
- //////////////////////////////////////////////////////////////////////
- void PnaclTranslationCache::OpComplete(PnaclTranslationCacheEntry* entry) {
- open_entries_.erase(entry);
- }
- //////////////////////////////////////////////////////////////////////
- // Construction and cache backend initialization
- PnaclTranslationCache::PnaclTranslationCache() : in_memory_(false) {}
- PnaclTranslationCache::~PnaclTranslationCache() {}
- int PnaclTranslationCache::Init(net::CacheType cache_type,
- const base::FilePath& cache_dir,
- int cache_size,
- CompletionOnceCallback callback) {
- disk_cache::BackendResult result = disk_cache::CreateCacheBackend(
- cache_type, net::CACHE_BACKEND_DEFAULT, /*file_operations=*/nullptr,
- cache_dir, cache_size, disk_cache::ResetHandling::kResetOnError,
- nullptr, /* dummy net log */
- base::BindOnce(&PnaclTranslationCache::OnCreateBackendComplete,
- AsWeakPtr()));
- if (result.net_error == net::OK) {
- disk_cache_ = std::move(result.backend);
- } else if (result.net_error == net::ERR_IO_PENDING) {
- init_callback_ = std::move(callback);
- }
- return result.net_error;
- }
- void PnaclTranslationCache::OnCreateBackendComplete(
- disk_cache::BackendResult result) {
- if (result.net_error < 0) {
- LOG(ERROR) << "Backend init failed:"
- << net::ErrorToString(result.net_error);
- }
- disk_cache_ = std::move(result.backend);
- // Invoke our client's callback function.
- if (!init_callback_.is_null()) {
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE, base::BindOnce(std::move(init_callback_), result.net_error));
- }
- }
- //////////////////////////////////////////////////////////////////////
- // High-level API
- void PnaclTranslationCache::StoreNexe(const std::string& key,
- net::DrainableIOBuffer* nexe_data,
- CompletionOnceCallback callback) {
- PnaclTranslationCacheEntry* entry = PnaclTranslationCacheEntry::GetWriteEntry(
- AsWeakPtr(), key, nexe_data, std::move(callback));
- open_entries_[entry] = entry;
- entry->Start();
- }
- void PnaclTranslationCache::GetNexe(const std::string& key,
- GetNexeCallback callback) {
- PnaclTranslationCacheEntry* entry = PnaclTranslationCacheEntry::GetReadEntry(
- AsWeakPtr(), key, std::move(callback));
- open_entries_[entry] = entry;
- entry->Start();
- }
- int PnaclTranslationCache::InitOnDisk(const base::FilePath& cache_directory,
- CompletionOnceCallback callback) {
- in_memory_ = false;
- return Init(net::PNACL_CACHE, cache_directory, 0 /* auto size */,
- std::move(callback));
- }
- int PnaclTranslationCache::InitInMemory(CompletionOnceCallback callback) {
- in_memory_ = true;
- return Init(net::MEMORY_CACHE, base::FilePath(), kMaxMemCacheSize,
- std::move(callback));
- }
- int PnaclTranslationCache::Size() {
- if (!disk_cache_)
- return -1;
- return disk_cache_->GetEntryCount();
- }
- // Beware that any changes to this function or to PnaclCacheInfo will
- // effectively invalidate existing translation cache entries.
- // static
- std::string PnaclTranslationCache::GetKey(const nacl::PnaclCacheInfo& info) {
- if (!info.pexe_url.is_valid() || info.abi_version < 0 || info.opt_level < 0 ||
- info.extra_flags.size() > 512)
- return std::string();
- std::string retval("ABI:");
- retval += NumberToString(info.abi_version) + ";" +
- "opt:" + NumberToString(info.opt_level) +
- (info.use_subzero ? "subzero;" : ";") + "URL:";
- // Filter the username, password, and ref components from the URL
- GURL::Replacements replacements;
- replacements.ClearUsername();
- replacements.ClearPassword();
- replacements.ClearRef();
- GURL key_url(info.pexe_url.ReplaceComponents(replacements));
- retval += key_url.spec() + ";";
- // You would think that there is already code to format base::Time values
- // somewhere, but I haven't found it yet. In any case, doing it ourselves
- // here means we can keep the format stable.
- base::Time::Exploded exploded;
- info.last_modified.UTCExplode(&exploded);
- if (info.last_modified.is_null() || !exploded.HasValidValues()) {
- memset(&exploded, 0, sizeof(exploded));
- }
- retval += "modified:" + NumberToString(exploded.year) + ":" +
- NumberToString(exploded.month) + ":" +
- NumberToString(exploded.day_of_month) + ":" +
- NumberToString(exploded.hour) + ":" +
- NumberToString(exploded.minute) + ":" +
- NumberToString(exploded.second) + ":" +
- NumberToString(exploded.millisecond) + ":UTC;";
- retval += "etag:" + info.etag + ";";
- retval += "sandbox:" + info.sandbox_isa + ";";
- retval += "extra_flags:" + info.extra_flags + ";";
- return retval;
- }
- int PnaclTranslationCache::DoomEntriesBetween(base::Time initial,
- base::Time end,
- CompletionOnceCallback callback) {
- return disk_cache_->DoomEntriesBetween(initial, end, std::move(callback));
- }
- } // namespace pnacl
|