123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 |
- // 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_host.h"
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/debug/leak_annotations.h"
- #include "base/files/file_path.h"
- #include "base/files/file_util.h"
- #include "base/logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/numerics/safe_math.h"
- #include "components/nacl/browser/nacl_browser.h"
- #include "components/nacl/browser/pnacl_translation_cache.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"
- using content::BrowserThread;
- namespace {
- static const base::FilePath::CharType kTranslationCacheDirectoryName[] =
- FILE_PATH_LITERAL("PnaclTranslationCache");
- // Delay to wait for initialization of the cache backend
- static const int kTranslationCacheInitializationDelayMs = 20;
- void CloseBaseFile(base::File file) {
- base::ThreadPool::PostTask(
- FROM_HERE,
- {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
- base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
- base::BindOnce([](base::File) {}, std::move(file)));
- }
- } // namespace
- namespace pnacl {
- class FileProxy {
- public:
- FileProxy(std::unique_ptr<base::File> file, PnaclHost* host);
- int Write(scoped_refptr<net::DrainableIOBuffer> buffer);
- void WriteDone(const PnaclHost::TranslationID& id, int result);
- private:
- std::unique_ptr<base::File> file_;
- raw_ptr<PnaclHost> host_;
- };
- FileProxy::FileProxy(std::unique_ptr<base::File> file, PnaclHost* host)
- : file_(std::move(file)), host_(host) {}
- int FileProxy::Write(scoped_refptr<net::DrainableIOBuffer> buffer) {
- int rv = file_->Write(0, buffer->data(), buffer->size());
- if (rv == -1)
- PLOG(ERROR) << "FileProxy::Write error";
- return rv;
- }
- void FileProxy::WriteDone(const PnaclHost::TranslationID& id, int result) {
- host_->OnBufferCopiedToTempFile(id, std::move(file_), result);
- }
- PnaclHost::PnaclHost() = default;
- PnaclHost* PnaclHost::GetInstance() {
- static PnaclHost* instance = nullptr;
- if (!instance) {
- instance = new PnaclHost;
- ANNOTATE_LEAKING_OBJECT_PTR(instance);
- }
- return instance;
- }
- PnaclHost::PendingTranslation::PendingTranslation()
- : process_handle(base::kNullProcessHandle),
- nexe_fd(nullptr),
- got_nexe_fd(false),
- got_cache_reply(false),
- got_cache_hit(false),
- is_incognito(false),
- callback(NexeFdCallback()),
- cache_info(nacl::PnaclCacheInfo()) {}
- PnaclHost::PendingTranslation::PendingTranslation(
- const PendingTranslation& other) = default;
- PnaclHost::PendingTranslation::~PendingTranslation() {
- if (nexe_fd)
- delete nexe_fd;
- }
- bool PnaclHost::TranslationMayBeCached(
- const PendingTranslationMap::iterator& entry) {
- return !entry->second.is_incognito &&
- !entry->second.cache_info.has_no_store_header;
- }
- /////////////////////////////////////// Initialization
- static base::FilePath GetCachePath() {
- NaClBrowserDelegate* browser_delegate = nacl::NaClBrowser::GetDelegate();
- // Determine where the translation cache resides in the file system. It
- // exists in Chrome's cache directory and is not tied to any specific
- // profile. If we fail, return an empty path.
- // Start by finding the user data directory.
- base::FilePath user_data_dir;
- if (!browser_delegate ||
- !browser_delegate->GetUserDirectory(&user_data_dir)) {
- return base::FilePath();
- }
- // The cache directory may or may not be the user data directory.
- base::FilePath cache_file_path;
- browser_delegate->GetCacheDirectory(&cache_file_path);
- // Append the base file name to the cache directory.
- return cache_file_path.Append(kTranslationCacheDirectoryName);
- }
- void PnaclHost::OnCacheInitialized(int net_error) {
- DCHECK(thread_checker_.CalledOnValidThread());
- // If the cache was cleared before the load completed, ignore.
- if (cache_state_ == CacheReady)
- return;
- if (net_error != net::OK) {
- // This will cause the cache to attempt to re-init on the next call to
- // GetNexeFd.
- cache_state_ = CacheUninitialized;
- } else {
- cache_state_ = CacheReady;
- }
- }
- void PnaclHost::Init() {
- // Extra check that we're on the real UI thread since this version of
- // Init isn't used in unit tests.
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- DCHECK(thread_checker_.CalledOnValidThread());
- base::FilePath cache_path(GetCachePath());
- if (cache_path.empty() || cache_state_ != CacheUninitialized)
- return;
- disk_cache_ = std::make_unique<PnaclTranslationCache>();
- cache_state_ = CacheInitializing;
- int rv = disk_cache_->InitOnDisk(
- cache_path,
- base::BindOnce(&PnaclHost::OnCacheInitialized, base::Unretained(this)));
- if (rv != net::ERR_IO_PENDING)
- OnCacheInitialized(rv);
- }
- // Initialize for testing, optionally using the in-memory backend, and manually
- // setting the temporary file directory instead of using the system directory,
- // and re-initializing file task runner.
- void PnaclHost::InitForTest(base::FilePath temp_dir, bool in_memory) {
- DCHECK(thread_checker_.CalledOnValidThread());
- file_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
- {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
- disk_cache_ = std::make_unique<PnaclTranslationCache>();
- cache_state_ = CacheInitializing;
- temp_dir_ = temp_dir;
- int rv;
- if (in_memory) {
- rv = disk_cache_->InitInMemory(
- base::BindOnce(&PnaclHost::OnCacheInitialized, base::Unretained(this)));
- } else {
- rv = disk_cache_->InitOnDisk(
- temp_dir,
- base::BindOnce(&PnaclHost::OnCacheInitialized, base::Unretained(this)));
- }
- if (rv != net::ERR_IO_PENDING)
- OnCacheInitialized(rv);
- }
- ///////////////////////////////////////// Temp files
- // Create a temporary file on |file_task_runner_|.
- // static
- void PnaclHost::DoCreateTemporaryFile(base::FilePath temp_dir,
- TempFileCallback cb) {
- base::FilePath file_path;
- base::File file;
- bool rv = temp_dir.empty()
- ? base::CreateTemporaryFile(&file_path)
- : base::CreateTemporaryFileInDir(temp_dir, &file_path);
- if (!rv) {
- PLOG(ERROR) << "Temp file creation failed.";
- } else {
- file.Initialize(
- file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
- base::File::FLAG_WRITE | base::File::FLAG_WIN_TEMPORARY |
- base::File::FLAG_DELETE_ON_CLOSE);
- if (!file.IsValid())
- PLOG(ERROR) << "Temp file open failed: " << file.error_details();
- }
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE, base::BindOnce(cb, std::move(file)));
- }
- void PnaclHost::CreateTemporaryFile(TempFileCallback cb) {
- if (!file_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&PnaclHost::DoCreateTemporaryFile, temp_dir_, cb))) {
- DCHECK(thread_checker_.CalledOnValidThread());
- cb.Run(base::File());
- }
- }
- ///////////////////////////////////////// GetNexeFd implementation
- ////////////////////// Common steps
- void PnaclHost::GetNexeFd(int render_process_id,
- int pp_instance,
- bool is_incognito,
- const nacl::PnaclCacheInfo& cache_info,
- const NexeFdCallback& cb) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (cache_state_ == CacheUninitialized) {
- Init();
- }
- if (cache_state_ != CacheReady) {
- // If the backend hasn't yet initialized, try the request again later.
- content::GetUIThreadTaskRunner({})->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&PnaclHost::GetNexeFd, base::Unretained(this),
- render_process_id, pp_instance, is_incognito, cache_info,
- cb),
- base::Milliseconds(kTranslationCacheInitializationDelayMs));
- return;
- }
- TranslationID id(render_process_id, pp_instance);
- auto entry = pending_translations_.find(id);
- if (entry != pending_translations_.end()) {
- // Existing translation must have been abandonded. Clean it up.
- LOG(ERROR) << "GetNexeFd for already-pending translation";
- pending_translations_.erase(entry);
- }
- std::string cache_key(disk_cache_->GetKey(cache_info));
- if (cache_key.empty()) {
- LOG(ERROR) << "GetNexeFd: Invalid cache info";
- cb.Run(base::File(), false);
- return;
- }
- PendingTranslation pt;
- pt.callback = cb;
- pt.cache_info = cache_info;
- pt.cache_key = cache_key;
- pt.is_incognito = is_incognito;
- pending_translations_[id] = pt;
- SendCacheQueryAndTempFileRequest(cache_key, id);
- }
- // Dispatch the cache read request and the temp file creation request
- // simultaneously; currently we need a temp file regardless of whether the
- // request hits.
- void PnaclHost::SendCacheQueryAndTempFileRequest(const std::string& cache_key,
- const TranslationID& id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- pending_backend_operations_++;
- disk_cache_->GetNexe(cache_key, base::BindOnce(&PnaclHost::OnCacheQueryReturn,
- base::Unretained(this), id));
- CreateTemporaryFile(base::BindRepeating(&PnaclHost::OnTempFileReturn,
- base::Unretained(this), id));
- }
- // Callback from the translation cache query. |id| is bound from
- // SendCacheQueryAndTempFileRequest, |net_error| is a net::Error code (which for
- // our purposes means a hit if it's net::OK (i.e. 0). |buffer| is allocated
- // by PnaclTranslationCache and now belongs to PnaclHost.
- // (Bound callbacks must re-lookup the TranslationID because the translation
- // could be cancelled before they get called).
- void PnaclHost::OnCacheQueryReturn(
- const TranslationID& id,
- int net_error,
- scoped_refptr<net::DrainableIOBuffer> buffer) {
- DCHECK(thread_checker_.CalledOnValidThread());
- pending_backend_operations_--;
- auto entry(pending_translations_.find(id));
- if (entry == pending_translations_.end()) {
- LOG(ERROR) << "OnCacheQueryReturn: id not found";
- DeInitIfSafe();
- return;
- }
- PendingTranslation* pt = &entry->second;
- pt->got_cache_reply = true;
- pt->got_cache_hit = (net_error == net::OK);
- if (pt->got_cache_hit)
- pt->nexe_read_buffer = buffer;
- CheckCacheQueryReady(entry);
- }
- // Callback from temp file creation. |id| is bound from
- // SendCacheQueryAndTempFileRequest, and |file| is the created file.
- // If there was an error, file is invalid.
- // (Bound callbacks must re-lookup the TranslationID because the translation
- // could be cancelled before they get called).
- void PnaclHost::OnTempFileReturn(const TranslationID& id,
- base::File file) {
- DCHECK(thread_checker_.CalledOnValidThread());
- auto entry(pending_translations_.find(id));
- if (entry == pending_translations_.end()) {
- // The renderer may have signaled an error or closed while the temp
- // file was being created.
- LOG(ERROR) << "OnTempFileReturn: id not found";
- CloseBaseFile(std::move(file));
- return;
- }
- if (!file.IsValid()) {
- // This translation will fail, but we need to retry any translation
- // waiting for its result.
- LOG(ERROR) << "OnTempFileReturn: temp file creation failed";
- std::string key(entry->second.cache_key);
- entry->second.callback.Run(base::File(), false);
- bool may_be_cached = TranslationMayBeCached(entry);
- pending_translations_.erase(entry);
- // No translations will be waiting for entries that will not be stored.
- if (may_be_cached)
- RequeryMatchingTranslations(key);
- return;
- }
- PendingTranslation* pt = &entry->second;
- pt->got_nexe_fd = true;
- pt->nexe_fd = new base::File(std::move(file));
- CheckCacheQueryReady(entry);
- }
- // Check whether both the cache query and the temp file have returned, and check
- // whether we actually got a hit or not.
- void PnaclHost::CheckCacheQueryReady(
- const PendingTranslationMap::iterator& entry) {
- DCHECK(thread_checker_.CalledOnValidThread());
- PendingTranslation* pt = &entry->second;
- if (!(pt->got_cache_reply && pt->got_nexe_fd))
- return;
- if (!pt->got_cache_hit) {
- // Check if there is already a pending translation for this file. If there
- // is, we will wait for it to come back, to avoid redundant translations.
- for (auto it = pending_translations_.begin();
- it != pending_translations_.end(); ++it) {
- // Another translation matches if it's a request for the same file,
- if (it->second.cache_key == entry->second.cache_key &&
- // and it's not this translation,
- it->first != entry->first &&
- // and it can be stored in the cache,
- TranslationMayBeCached(it) &&
- // and it's already gotten past this check and returned the miss.
- it->second.got_cache_reply &&
- it->second.got_nexe_fd) {
- return;
- }
- }
- ReturnMiss(entry);
- return;
- }
- std::unique_ptr<base::File> file(pt->nexe_fd);
- pt->nexe_fd = nullptr;
- pt->got_nexe_fd = false;
- FileProxy* proxy(new FileProxy(std::move(file), this));
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
- base::BindOnce(&FileProxy::Write, base::Unretained(proxy),
- pt->nexe_read_buffer),
- base::BindOnce(&FileProxy::WriteDone, base::Owned(proxy), entry->first));
- }
- //////////////////// GetNexeFd miss path
- // Return the temp fd to the renderer, reporting a miss.
- void PnaclHost::ReturnMiss(const PendingTranslationMap::iterator& entry) {
- // Return the fd
- PendingTranslation* pt = &entry->second;
- NexeFdCallback cb(pt->callback);
- cb.Run(*pt->nexe_fd, false);
- if (!pt->nexe_fd->IsValid()) {
- // Bad FD is unrecoverable, so clear out the entry.
- pending_translations_.erase(entry);
- }
- }
- // On error, just return a null refptr.
- // static
- scoped_refptr<net::DrainableIOBuffer> PnaclHost::CopyFileToBuffer(
- std::unique_ptr<base::File> file) {
- scoped_refptr<net::DrainableIOBuffer> buffer;
- // TODO(eroman): Maximum size should be changed to size_t once that is
- // what IOBuffer requires. crbug.com/488553. Also I don't think the
- // max size should be inclusive here...
- int64_t file_size = file->GetLength();
- if (file_size < 0 || file_size > std::numeric_limits<int>::max()) {
- PLOG(ERROR) << "Get file length failed " << file_size;
- return buffer;
- }
- buffer = base::MakeRefCounted<net::DrainableIOBuffer>(
- base::MakeRefCounted<net::IOBuffer>(
- base::checked_cast<size_t>(file_size)),
- base::checked_cast<size_t>(file_size));
- if (file->Read(0, buffer->data(), buffer->size()) != file_size) {
- PLOG(ERROR) << "CopyFileToBuffer file read failed";
- buffer = nullptr;
- }
- return buffer;
- }
- // Called by the renderer in the miss path to report a finished translation
- void PnaclHost::TranslationFinished(int render_process_id,
- int pp_instance,
- bool success) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (cache_state_ != CacheReady)
- return;
- TranslationID id(render_process_id, pp_instance);
- auto entry(pending_translations_.find(id));
- if (entry == pending_translations_.end()) {
- LOG(ERROR) << "TranslationFinished: TranslationID " << render_process_id
- << "," << pp_instance << " not found.";
- return;
- }
- bool store_nexe = true;
- // If this is a premature response (i.e. we haven't returned a temp file
- // yet) or if it's an unsuccessful translation, or if we are incognito,
- // don't store in the cache.
- // TODO(dschuff): use a separate in-memory cache for incognito
- // translations.
- if (!entry->second.got_nexe_fd || !entry->second.got_cache_reply ||
- !success || !TranslationMayBeCached(entry)) {
- store_nexe = false;
- } else {
- std::unique_ptr<base::File> file(entry->second.nexe_fd);
- entry->second.nexe_fd = nullptr;
- entry->second.got_nexe_fd = false;
- base::ThreadPool::PostTaskAndReplyWithResult(
- FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
- base::BindOnce(&PnaclHost::CopyFileToBuffer, std::move(file)),
- base::BindOnce(&PnaclHost::StoreTranslatedNexe, base::Unretained(this),
- id));
- }
- if (!store_nexe) {
- // If store_nexe is true, the fd will be closed by CopyFileToBuffer.
- if (entry->second.got_nexe_fd) {
- std::unique_ptr<base::File> file(entry->second.nexe_fd);
- entry->second.nexe_fd = nullptr;
- CloseBaseFile(std::move(*file.get()));
- }
- pending_translations_.erase(entry);
- }
- }
- // Store the translated nexe in the translation cache. Called back with the
- // TranslationID from the host and the result of CopyFileToBuffer.
- // (Bound callbacks must re-lookup the TranslationID because the translation
- // could be cancelled before they get called).
- void PnaclHost::StoreTranslatedNexe(
- TranslationID id,
- scoped_refptr<net::DrainableIOBuffer> buffer) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (cache_state_ != CacheReady)
- return;
- auto it(pending_translations_.find(id));
- if (it == pending_translations_.end()) {
- LOG(ERROR) << "StoreTranslatedNexe: TranslationID " << id.first << ","
- << id.second << " not found.";
- return;
- }
- if (!buffer.get()) {
- LOG(ERROR) << "Error reading translated nexe";
- return;
- }
- pending_backend_operations_++;
- disk_cache_->StoreNexe(it->second.cache_key, buffer.get(),
- base::BindOnce(&PnaclHost::OnTranslatedNexeStored,
- base::Unretained(this), it->first));
- }
- // After we know the nexe has been stored, we can clean up, and unblock any
- // outstanding requests for the same file.
- // (Bound callbacks must re-lookup the TranslationID because the translation
- // could be cancelled before they get called).
- void PnaclHost::OnTranslatedNexeStored(const TranslationID& id, int net_error) {
- auto entry(pending_translations_.find(id));
- pending_backend_operations_--;
- if (entry == pending_translations_.end()) {
- // If the renderer closed while we were storing the nexe, we land here.
- // Make sure we try to de-init.
- DeInitIfSafe();
- return;
- }
- std::string key(entry->second.cache_key);
- pending_translations_.erase(entry);
- RequeryMatchingTranslations(key);
- }
- // Check if any pending translations match |key|. If so, re-issue the cache
- // query. In the overlapped miss case, we expect a hit this time, but a miss
- // is also possible in case of an error.
- void PnaclHost::RequeryMatchingTranslations(const std::string& key) {
- DCHECK(thread_checker_.CalledOnValidThread());
- // Check for outstanding misses to this same file
- for (auto it = pending_translations_.begin();
- it != pending_translations_.end(); ++it) {
- if (it->second.cache_key == key) {
- // Re-send the cache read request. This time we expect a hit, but if
- // something goes wrong, it will just handle it like a miss.
- it->second.got_cache_reply = false;
- pending_backend_operations_++;
- disk_cache_->GetNexe(key,
- base::BindOnce(&PnaclHost::OnCacheQueryReturn,
- base::Unretained(this), it->first));
- }
- }
- }
- //////////////////// GetNexeFd hit path
- void PnaclHost::OnBufferCopiedToTempFile(const TranslationID& id,
- std::unique_ptr<base::File> file,
- int file_error) {
- DCHECK(thread_checker_.CalledOnValidThread());
- auto entry(pending_translations_.find(id));
- if (entry == pending_translations_.end()) {
- CloseBaseFile(std::move(*file.get()));
- return;
- }
- if (file_error == -1) {
- // Write error on the temp file. Request a new file and start over.
- CloseBaseFile(std::move(*file.get()));
- CreateTemporaryFile(base::BindRepeating(
- &PnaclHost::OnTempFileReturn, base::Unretained(this), entry->first));
- return;
- }
- entry->second.callback.Run(*file.get(), true);
- CloseBaseFile(std::move(*file.get()));
- pending_translations_.erase(entry);
- }
- ///////////////////
- void PnaclHost::RendererClosing(int render_process_id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (cache_state_ != CacheReady)
- return;
- for (auto it = pending_translations_.begin();
- it != pending_translations_.end();) {
- auto to_erase(it++);
- if (to_erase->first.first == render_process_id) {
- // Clean up the open files.
- if (to_erase->second.nexe_fd) {
- std::unique_ptr<base::File> file(to_erase->second.nexe_fd);
- to_erase->second.nexe_fd = nullptr;
- CloseBaseFile(std::move(*file.get()));
- }
- std::string key(to_erase->second.cache_key);
- bool may_be_cached = TranslationMayBeCached(to_erase);
- pending_translations_.erase(to_erase);
- // No translations will be waiting for entries that will not be stored.
- if (may_be_cached)
- RequeryMatchingTranslations(key);
- }
- }
- DeInitIfSafe();
- }
- ////////////////// Cache data removal
- void PnaclHost::ClearTranslationCacheEntriesBetween(
- base::Time initial_time,
- base::Time end_time,
- base::OnceClosure callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (cache_state_ == CacheUninitialized) {
- Init();
- }
- if (cache_state_ == CacheInitializing) {
- // If the backend hasn't yet initialized, try the request again later.
- content::GetUIThreadTaskRunner({})->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&PnaclHost::ClearTranslationCacheEntriesBetween,
- base::Unretained(this), initial_time, end_time,
- std::move(callback)),
- base::Milliseconds(kTranslationCacheInitializationDelayMs));
- return;
- }
- pending_backend_operations_++;
- auto split_callback = base::SplitOnceCallback(std::move(callback));
- int rv = disk_cache_->DoomEntriesBetween(
- initial_time, end_time,
- base::BindOnce(&PnaclHost::OnEntriesDoomed, base::Unretained(this),
- std::move(split_callback.first)));
- if (rv != net::ERR_IO_PENDING)
- OnEntriesDoomed(std::move(split_callback.second), rv);
- }
- void PnaclHost::OnEntriesDoomed(base::OnceClosure callback, int net_error) {
- DCHECK(thread_checker_.CalledOnValidThread());
- content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(callback));
- pending_backend_operations_--;
- // When clearing the cache, the UI is blocked on all the cache-clearing
- // operations, and freeing the backend actually blocks the IO thread. So
- // instead of calling DeInitIfSafe directly, post it for later.
- content::GetUIThreadTaskRunner({})->PostTask(
- FROM_HERE,
- base::BindOnce(&PnaclHost::DeInitIfSafe, base::Unretained(this)));
- }
- // Destroying the cache backend causes it to post tasks to the cache thread to
- // flush to disk. PnaclHost is leaked on shutdown because registering it as a
- // Singleton with AtExitManager would result in it not being destroyed until all
- // the browser threads have gone away and it's too late to post anything
- // (attempting to do so hangs shutdown) at that point anyways. So we make sure
- // to destroy it when we no longer have any outstanding operations that need it.
- // These include pending translations, cache clear requests, and requests to
- // read or write translated nexes. We check when renderers close, when cache
- // clear requests finish, and when backend operations complete.
- // It is not safe to delete the backend while it is initializing, nor if it has
- // outstanding entry open requests; it is in theory safe to delete it with
- // outstanding read/write requests, but because that distinction is hidden
- // inside PnaclTranslationCache, we do not delete the backend if there are any
- // backend requests in flight. As a last resort in the destructor, we just leak
- // the backend to avoid hanging shutdown.
- void PnaclHost::DeInitIfSafe() {
- DCHECK(pending_backend_operations_ >= 0);
- if (pending_translations_.empty() &&
- pending_backend_operations_ <= 0 &&
- cache_state_ == CacheReady) {
- cache_state_ = CacheUninitialized;
- disk_cache_.reset();
- }
- }
- } // namespace pnacl
|