commit 53e770b01f1a855b61cc0a681aed10ee7afc8ae1
parent 9d38131e7dde41cd6d4d432ed45a2bd763485e70
Author: Tooru Fujisawa <arai_a@mac.com>
Date: Tue, 11 Nov 2025 08:26:20 +0000
Bug 1997870 - Part 4: Perform the encoding and the compression off main thread. r=bthrall
Differential Revision: https://phabricator.services.mozilla.com/D271035
Diffstat:
2 files changed, 124 insertions(+), 26 deletions(-)
diff --git a/dom/script/SharedScriptCache.cpp b/dom/script/SharedScriptCache.cpp
@@ -11,6 +11,7 @@
#include "ScriptTrace.h" // TRACE_FOR_TEST
#include "js/experimental/CompileScript.h" // JS::FrontendContext, JS::NewFrontendContext, JS::DestroyFrontendContext
#include "mozilla/Maybe.h" // Maybe, Some, Nothing
+#include "mozilla/TaskController.h" // TaskController, Task
#include "mozilla/dom/ContentParent.h" // dom::ContentParent
#include "nsIMemoryReporter.h" // nsIMemoryReporter, MOZ_DEFINE_MALLOC_SIZE_OF, RegisterWeakMemoryReporter, UnregisterWeakMemoryReporter, MOZ_COLLECT_REPORT, KIND_HEAP, UNITS_BYTES
#include "nsIPrefBranch.h" // nsIPrefBranch, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
@@ -222,13 +223,54 @@ bool SharedScriptCache::MaybeScheduleUpdateDiskCache() {
return true;
}
+class ScriptEncodeAndCompressionTask : public mozilla::Task {
+ public:
+ ScriptEncodeAndCompressionTask()
+ : Task(Kind::OffMainThreadOnly, EventQueuePriority::Idle) {}
+ virtual ~ScriptEncodeAndCompressionTask() = default;
+
+#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
+ bool GetName(nsACString& aName) override {
+ aName.AssignLiteral("ScriptEncodeAndCompressionTask");
+ return true;
+ }
+#endif
+
+ TaskResult Run() override {
+ SharedScriptCache::Get()->EncodeAndCompress();
+ return TaskResult::Complete;
+ }
+};
+
+class ScriptSaveTask : public mozilla::Task {
+ public:
+ ScriptSaveTask() : Task(Kind::MainThreadOnly, EventQueuePriority::Idle) {}
+ virtual ~ScriptSaveTask() = default;
+
+#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
+ bool GetName(nsACString& aName) override {
+ aName.AssignLiteral("ScriptSaveTask");
+ return true;
+ }
+#endif
+
+ TaskResult Run() override {
+ SharedScriptCache::Get()->SaveToDiskCache();
+ return TaskResult::Complete;
+ }
+};
+
void SharedScriptCache::UpdateDiskCache() {
auto strategy = ScriptLoader::GetDiskCacheStrategy();
if (strategy.mIsDisabled) {
return;
}
- JS::FrontendContext* fc = nullptr;
+ mozilla::MutexAutoLock lock(mEncodeMutex);
+
+ if (!mEncodeItems.empty()) {
+ return;
+ }
for (auto iter = mComplete.Iter(); !iter.Done(); iter.Next()) {
JS::loader::LoadedScript* loadedScript = iter.Data().mResource;
@@ -236,40 +278,69 @@ void SharedScriptCache::UpdateDiskCache() {
continue;
}
- if (!fc) {
- // Lazily create the context only when there's at least one script
- // that needs to be saved.
- fc = JS::NewFrontendContext();
- if (!fc) {
- return;
- }
+ if (!mEncodeItems.emplaceBack(loadedScript->GetStencil(),
+ std::move(loadedScript->SRIAndBytecode()),
+ loadedScript)) {
+ continue;
}
+ }
- Vector<uint8_t> compressed;
- if (!ScriptLoader::EncodeAndCompress(
- fc, loadedScript, loadedScript->GetStencil(),
- loadedScript->SRIAndBytecode(), compressed)) {
- loadedScript->DropDiskCacheReference();
- loadedScript->DropBytecode();
- TRACE_FOR_TEST(loadedScript, "diskcache:failed");
- continue;
+ if (mEncodeItems.empty()) {
+ return;
+ }
+
+ RefPtr<ScriptEncodeAndCompressionTask> encodeTask =
+ new ScriptEncodeAndCompressionTask();
+ RefPtr<ScriptSaveTask> saveTask = new ScriptSaveTask();
+ saveTask->AddDependency(encodeTask);
+
+ TaskController::Get()->AddTask(encodeTask.forget());
+ TaskController::Get()->AddTask(saveTask.forget());
+}
+
+void SharedScriptCache::EncodeAndCompress() {
+ JS::FrontendContext* fc = JS::NewFrontendContext();
+ if (!fc) {
+ return;
+ }
+
+ mozilla::MutexAutoLock lock(mEncodeMutex);
+
+ for (auto& item : mEncodeItems) {
+ if (!ScriptLoader::EncodeAndCompress(fc, item.mLoadedScript, item.mStencil,
+ item.mSRI, item.mCompressed)) {
+ item.mCompressed.clear();
}
+ }
+
+ JS::DestroyFrontendContext(fc);
+}
+
+void SharedScriptCache::SaveToDiskCache() {
+ MOZ_ASSERT(NS_IsMainThread());
- if (!ScriptLoader::SaveToDiskCache(loadedScript, compressed)) {
- loadedScript->DropDiskCacheReference();
- loadedScript->DropBytecode();
- TRACE_FOR_TEST(loadedScript, "diskcache:failed");
+ mozilla::MutexAutoLock lock(mEncodeMutex);
+
+ for (const auto& item : mEncodeItems) {
+ if (item.mCompressed.empty()) {
+ item.mLoadedScript->DropDiskCacheReference();
+ item.mLoadedScript->DropBytecode();
+ TRACE_FOR_TEST(item.mLoadedScript, "diskcache:failed");
continue;
}
- loadedScript->DropDiskCacheReference();
- loadedScript->DropBytecode();
- TRACE_FOR_TEST(loadedScript, "diskcache:saved");
- }
+ if (!ScriptLoader::SaveToDiskCache(item.mLoadedScript, item.mCompressed)) {
+ item.mLoadedScript->DropDiskCacheReference();
+ item.mLoadedScript->DropBytecode();
+ TRACE_FOR_TEST(item.mLoadedScript, "diskcache:failed");
+ }
- if (fc) {
- JS::DestroyFrontendContext(fc);
+ item.mLoadedScript->DropDiskCacheReference();
+ item.mLoadedScript->DropBytecode();
+ TRACE_FOR_TEST(item.mLoadedScript, "diskcache:saved");
}
+
+ mEncodeItems.clear();
}
} // namespace mozilla::dom
diff --git a/dom/script/SharedScriptCache.h b/dom/script/SharedScriptCache.h
@@ -14,8 +14,10 @@
#include "js/loader/ScriptLoadRequest.h" // JS::loader::ScriptLoadRequest
#include "mozilla/CORSMode.h" // mozilla::CORSMode
#include "mozilla/MemoryReporting.h" // MallocSizeOf
+#include "mozilla/Mutex.h" // Mutex, GUARDED_BY, MutexAutoLock
#include "mozilla/RefPtr.h" // RefPtr
#include "mozilla/SharedSubResourceCache.h" // SharedSubResourceCache, SharedSubResourceCacheLoadingValueBase, SubResourceNetworkMetadataHolder
+#include "mozilla/ThreadSafety.h" // MOZ_GUARDED_BY
#include "mozilla/WeakPtr.h" // SupportsWeakPtr
#include "mozilla/dom/CacheExpirationTime.h" // CacheExpirationTime
#include "mozilla/dom/SRIMetadata.h" // mozilla::dom::SRIMetadata
@@ -196,6 +198,9 @@ class SharedScriptCache final
bool MaybeScheduleUpdateDiskCache();
void UpdateDiskCache();
+ void EncodeAndCompress();
+ void SaveToDiskCache();
+
// This has to be static because it's also called for loaders that don't have
// a sheet cache (loaders that are not owned by a document).
static void LoadCompleted(SharedScriptCache*, ScriptLoadData&);
@@ -210,6 +215,28 @@ class SharedScriptCache final
protected:
~SharedScriptCache();
+
+ private:
+ class EncodeItem {
+ public:
+ EncodeItem(JS::Stencil* aStencil, JS::TranscodeBuffer&& aSRI,
+ JS::loader::LoadedScript* aLoadedScript)
+ : mStencil(aStencil),
+ mSRI(std::move(aSRI)),
+ mLoadedScript(aLoadedScript) {}
+
+ // These fields can be touched from multiple threads.
+ RefPtr<JS::Stencil> mStencil;
+ JS::TranscodeBuffer mSRI;
+ Vector<uint8_t> mCompressed;
+
+ // This can be dereferenced only from the main thread.
+ // Reading the pointer itself is allowed also off main thread.
+ RefPtr<JS::loader::LoadedScript> mLoadedScript;
+ };
+
+ Mutex mEncodeMutex{"SharedScriptCache::mEncodeMutex"};
+ Vector<EncodeItem> mEncodeItems MOZ_GUARDED_BY(mEncodeMutex);
};
} // namespace dom