diff --git a/engine/src/flutter/shell/common/persistent_cache.cc b/engine/src/flutter/shell/common/persistent_cache.cc index 959fb997456..d33cb863d4c 100644 --- a/engine/src/flutter/shell/common/persistent_cache.cc +++ b/engine/src/flutter/shell/common/persistent_cache.cc @@ -9,6 +9,7 @@ #include "flutter/fml/base32.h" #include "flutter/fml/file.h" +#include "flutter/fml/make_copyable.h" #include "flutter/fml/mapping.h" #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" @@ -40,15 +41,16 @@ PersistentCache* PersistentCache::GetCacheForProcess() { } PersistentCache::PersistentCache() - : cache_directory_(CreateDirectory(fml::paths::GetCachesDirectory(), - { - "flutter_engine", // - GetFlutterEngineVersion(), // - "skia", // - GetSkiaVersion() // - }, - fml::FilePermission::kReadWrite)) { - if (!cache_directory_.is_valid()) { + : cache_directory_(std::make_shared( + CreateDirectory(fml::paths::GetCachesDirectory(), + { + "flutter_engine", // + GetFlutterEngineVersion(), // + "skia", // + GetSkiaVersion() // + }, + fml::FilePermission::kReadWrite))) { + if (!IsValid()) { FML_LOG(ERROR) << "Could not acquire the persistent cache directory. " "Caching of GPU resources on disk is disabled."; } @@ -56,17 +58,21 @@ PersistentCache::PersistentCache() PersistentCache::~PersistentCache() = default; +bool PersistentCache::IsValid() const { + return cache_directory_ && cache_directory_->is_valid(); +} + // |GrContextOptions::PersistentCache| sk_sp PersistentCache::load(const SkData& key) { TRACE_EVENT0("flutter", "PersistentCacheLoad"); - if (!cache_directory_.is_valid()) { + if (!IsValid()) { return nullptr; } auto file_name = SkKeyToFilePath(key); if (file_name.size() == 0) { return nullptr; } - auto file = fml::OpenFile(cache_directory_, file_name.c_str(), false, + auto file = fml::OpenFile(*cache_directory_, file_name.c_str(), false, fml::FilePermission::kRead); if (!file.is_valid()) { return nullptr; @@ -80,23 +86,81 @@ sk_sp PersistentCache::load(const SkData& key) { return SkData::MakeWithCopy(mapping->GetMapping(), mapping->GetSize()); } +static void PersistentCacheStore(fml::RefPtr worker, + std::shared_ptr cache_directory, + std::string key, + std::unique_ptr value) { + auto task = fml::MakeCopyable([cache_directory, // + file_name = std::move(key), // + mapping = std::move(value) // + ]() mutable { + TRACE_EVENT0("flutter", "PersistentCacheStore"); + if (!fml::WriteAtomically(*cache_directory, // + file_name.c_str(), // + *mapping) // + ) { + FML_DLOG(ERROR) << "Could not write cache contents to persistent store."; + } + }); + + if (!worker) { + FML_LOG(ERROR) + << "The persistent cache has no available workers. Performing the task " + "on the current thread. This slow operation is going to occur on a " + "frame workload."; + task(); + } else { + worker->PostTask(std::move(task)); + } +} + // |GrContextOptions::PersistentCache| void PersistentCache::store(const SkData& key, const SkData& data) { - TRACE_EVENT0("flutter", "PersistentCacheStore"); - if (!cache_directory_.is_valid()) { + if (!IsValid()) { return; } auto file_name = SkKeyToFilePath(key); - auto mapping = - std::make_unique(data.bytes(), data.size()); - if (!fml::WriteAtomically(cache_directory_, // - file_name.c_str(), // - *mapping) // - ) { - FML_DLOG(ERROR) << "Could not write cache contents to persistent store."; + if (file_name.size() == 0) { + return; + } + + auto mapping = std::make_unique( + std::vector{data.bytes(), data.bytes() + data.size()}); + + if (mapping == nullptr || mapping->GetSize() == 0) { + return; + } + + PersistentCacheStore(GetWorkerTaskRunner(), cache_directory_, + std::move(file_name), std::move(mapping)); +} + +void PersistentCache::AddWorkerTaskRunner( + fml::RefPtr task_runner) { + std::lock_guard lock(worker_task_runners_mutex_); + worker_task_runners_.insert(task_runner); +} + +void PersistentCache::RemoveWorkerTaskRunner( + fml::RefPtr task_runner) { + std::lock_guard lock(worker_task_runners_mutex_); + auto found = worker_task_runners_.find(task_runner); + if (found != worker_task_runners_.end()) { + worker_task_runners_.erase(found); } } +fml::RefPtr PersistentCache::GetWorkerTaskRunner() const { + fml::RefPtr worker; + + std::lock_guard lock(worker_task_runners_mutex_); + if (!worker_task_runners_.empty()) { + worker = *worker_task_runners_.begin(); + } + + return worker; +} + } // namespace shell diff --git a/engine/src/flutter/shell/common/persistent_cache.h b/engine/src/flutter/shell/common/persistent_cache.h index 2d436cc1733..2bc1280d63c 100644 --- a/engine/src/flutter/shell/common/persistent_cache.h +++ b/engine/src/flutter/shell/common/persistent_cache.h @@ -5,9 +5,13 @@ #ifndef FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ #define FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ +#include #include +#include #include "flutter/fml/macros.h" +#include "flutter/fml/synchronization/thread_annotations.h" +#include "flutter/fml/task_runner.h" #include "flutter/fml/unique_fd.h" #include "third_party/skia/include/gpu/GrContextOptions.h" @@ -19,8 +23,17 @@ class PersistentCache : public GrContextOptions::PersistentCache { ~PersistentCache() override; + void AddWorkerTaskRunner(fml::RefPtr task_runner); + + void RemoveWorkerTaskRunner(fml::RefPtr task_runner); + private: - fml::UniqueFD cache_directory_; + std::shared_ptr cache_directory_; + mutable std::mutex worker_task_runners_mutex_; + std::multiset> worker_task_runners_ + FML_GUARDED_BY(worker_task_runners_mutex_); + + bool IsValid() const; PersistentCache(); @@ -30,6 +43,8 @@ class PersistentCache : public GrContextOptions::PersistentCache { // |GrContextOptions::PersistentCache| void store(const SkData& key, const SkData& data) override; + fml::RefPtr GetWorkerTaskRunner() const; + FML_DISALLOW_COPY_AND_ASSIGN(PersistentCache); }; diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index a8b24fc0266..8f07f500588 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -23,6 +23,7 @@ #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/start_up.h" #include "flutter/shell/common/engine.h" +#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/vsync_waiter.h" @@ -293,6 +294,9 @@ Shell::Shell(blink::TaskRunners task_runners, blink::Settings settings) } Shell::~Shell() { + PersistentCache::GetCacheForProcess()->RemoveWorkerTaskRunner( + task_runners_.GetIOTaskRunner()); + if (auto vm = blink::DartVM::ForProcessIfInitialized()) { vm->GetServiceProtocol().RemoveHandler(this); } @@ -370,6 +374,9 @@ bool Shell::Setup(std::unique_ptr platform_view, vm->GetServiceProtocol().AddHandler(this); } + PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner( + task_runners_.GetIOTaskRunner()); + return true; }