tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mdom/script/SharedScriptCache.cpp | 123++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mdom/script/SharedScriptCache.h | 27+++++++++++++++++++++++++++
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