tor-browser

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

commit cf057d8b8aa0fd023ecac8bb7d094db042a4267a
parent 17d6d5813a804ff2d5226d298cbab2e01eefc8ea
Author: Jan de Mooij <jdemooij@mozilla.com>
Date:   Fri, 19 Dec 2025 08:40:39 +0000

Bug 2004893 part 2 - Support storing multiple script sources in a SourceCompressionTask. r=jonco

This adds `SourceCompressionTaskEntry` for the per-`ScriptSource` state. A source
compression task now has a vector of entries. Currently each task still has a single
entry but that will change in a later patch.

Differential Revision: https://phabricator.services.mozilla.com/D276485

Diffstat:
Mjs/src/vm/HelperThreadState.h | 72++++++++++++++++++++++++++++++++++++++++++------------------------------
Mjs/src/vm/HelperThreads.h | 1-
Mjs/src/vm/JSScript.cpp | 41++++++++++++++++++++++++++++-------------
Mjs/src/vm/JSScript.h | 6+++---
4 files changed, 73 insertions(+), 47 deletions(-)

diff --git a/js/src/vm/HelperThreadState.h b/js/src/vm/HelperThreadState.h @@ -576,19 +576,8 @@ struct FreeDelazifyTask : public HelperThreadTask { const char* getName() override { return "FreeDelazifyTask"; } }; -// Off-thread task for compressing one or more script sources. -// -// Completed tasks are handled during the sweeping phase by -// AttachFinishedCompressions, which runs in parallel with other GC sweeping -// tasks. -class SourceCompressionTask : public HelperThreadTask { - friend class HelperThread; - friend class ScriptSource; - - // The runtime that the ScriptSource is associated with, in the sense that - // it uses the runtime's immutable string cache. - JSRuntime* runtime_; - +// Entry for a single ScriptSource in a SourceCompressionTask. +class SourceCompressionTaskEntry { // The source to be compressed. RefPtr<ScriptSource> source_; @@ -599,13 +588,7 @@ class SourceCompressionTask : public HelperThreadTask { SharedImmutableString resultString_; public: - SourceCompressionTask(JSRuntime* rt, ScriptSource* source) - : runtime_(rt), source_(source) { - source->noteSourceCompressionTask(); - } - virtual ~SourceCompressionTask() = default; - - bool runtimeMatches(JSRuntime* runtime) const { return runtime == runtime_; } + explicit SourceCompressionTaskEntry(ScriptSource* source) : source_(source) {} bool shouldCancel() const { // If the refcount is exactly 1, then nothing else is holding on to the @@ -613,6 +596,45 @@ class SourceCompressionTask : public HelperThreadTask { return source_->refs == 1; } + // The work algorithm, aware whether it's compressing one-byte UTF-8 source + // text or UTF-16, for CharT either Utf8Unit or char16_t. Invoked by + // work() after doing a type-test of the ScriptSource*. + template <typename CharT> + void workEncodingSpecific(); + + void runTask(); + void complete(); + + struct PerformTaskWork; + friend struct PerformTaskWork; +}; + +// Off-thread task for compressing one or more script sources. +// +// Completed tasks are handled during the sweeping phase by +// AttachFinishedCompressions, which runs in parallel with other GC sweeping +// tasks. +class SourceCompressionTask final : public HelperThreadTask { + friend class HelperThread; + friend class ScriptSource; + + // The runtime that the task is associated with, in the sense that it uses the + // runtime's immutable string cache. + JSRuntime* runtime_; + + // The script sources to compress. + Vector<SourceCompressionTaskEntry, 4, SystemAllocPolicy> entries_; + + public: + SourceCompressionTask(JSRuntime* rt, ScriptSource* source) : runtime_(rt) { + static_assert(decltype(entries_)::InlineLength >= 1, + "Appending one entry should be infallible"); + MOZ_ALWAYS_TRUE(entries_.emplaceBack(source)); + } + virtual ~SourceCompressionTask() = default; + + bool runtimeMatches(JSRuntime* runtime) const { return runtime == runtime_; } + void runTask(); void runHelperThreadTask(AutoLockHelperThreadState& locked) override; void complete(); @@ -620,16 +642,6 @@ class SourceCompressionTask : public HelperThreadTask { ThreadType threadType() override { return ThreadType::THREAD_TYPE_COMPRESS; } const char* getName() override { return "SourceCompressionTask"; } - - private: - struct PerformTaskWork; - friend struct PerformTaskWork; - - // The work algorithm, aware whether it's compressing one-byte UTF-8 source - // text or UTF-16, for CharT either Utf8Unit or char16_t. Invoked by - // work() after doing a type-test of the ScriptSource*. - template <typename CharT> - void workEncodingSpecific(); }; // A PromiseHelperTask is an OffThreadPromiseTask that executes a single job on diff --git a/js/src/vm/HelperThreads.h b/js/src/vm/HelperThreads.h @@ -39,7 +39,6 @@ namespace js { class AutoLockHelperThreadState; struct PromiseHelperTask; -class SourceCompressionTask; namespace frontend { struct InitialStencilAndDelazifications; diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp @@ -1723,7 +1723,7 @@ template bool ScriptSource::assignSource(FrontendContext* fc, } template <typename Unit> -void SourceCompressionTask::workEncodingSpecific() { +void SourceCompressionTaskEntry::workEncodingSpecific() { MOZ_ASSERT(source_->isUncompressed<Unit>()); // Try to keep the maximum memory usage down by only allocating half the @@ -1800,10 +1800,10 @@ PendingSourceCompressionEntry::PendingSourceCompressionEntry( source->noteSourceCompressionTask(); } -struct SourceCompressionTask::PerformTaskWork { - SourceCompressionTask* const task_; +struct SourceCompressionTaskEntry::PerformTaskWork { + SourceCompressionTaskEntry* const task_; - explicit PerformTaskWork(SourceCompressionTask* task) : task_(task) {} + explicit PerformTaskWork(SourceCompressionTaskEntry* task) : task_(task) {} template <typename Unit, SourceRetrievable CanRetrieve> void operator()(const ScriptSource::Uncompressed<Unit, CanRetrieve>&) { @@ -1818,12 +1818,12 @@ struct SourceCompressionTask::PerformTaskWork { } }; -void ScriptSource::performTaskWork(SourceCompressionTask* task) { +void ScriptSource::performTaskWork(SourceCompressionTaskEntry* task) { MOZ_ASSERT(hasUncompressedSource()); - data.match(SourceCompressionTask::PerformTaskWork(task)); + data.match(SourceCompressionTaskEntry::PerformTaskWork(task)); } -void SourceCompressionTask::runTask() { +void SourceCompressionTaskEntry::runTask() { if (shouldCancel()) { return; } @@ -1833,6 +1833,13 @@ void SourceCompressionTask::runTask() { source_->performTaskWork(this); } +void SourceCompressionTask::runTask() { + MOZ_ASSERT(!entries_.empty()); + for (auto& entry : entries_) { + entry.runTask(); + } +} + void SourceCompressionTask::runHelperThreadTask( AutoLockHelperThreadState& locked) { { @@ -1853,12 +1860,19 @@ void ScriptSource::triggerConvertToCompressedSourceFromTask( data.match(TriggerConvertToCompressedSourceFromTask(this, compressed)); } -void SourceCompressionTask::complete() { +void SourceCompressionTaskEntry::complete() { if (!shouldCancel() && resultString_) { source_->triggerConvertToCompressedSourceFromTask(std::move(resultString_)); } } +void SourceCompressionTask::complete() { + MOZ_ASSERT(!entries_.empty()); + for (auto& entry : entries_) { + entry.complete(); + } +} + bool js::SynchronouslyCompressSource(JSContext* cx, JS::Handle<BaseScript*> script) { // Finish all pending source compressions, including the single compression @@ -1898,10 +1912,11 @@ bool js::SynchronouslyCompressSource(JSContext* cx, #endif MOZ_ASSERT(sourceRefs > 0, "at least |script| here should have a ref"); - // |SourceCompressionTask::shouldCancel| can periodically result in source - // compression being canceled if we're not careful. Guarantee that two refs - // to |ss| are always live in this function (at least one preexisting and - // one held by the task) so that compression is never canceled. + // |SourceCompressionTaskEntry::shouldCancel| can periodically result in + // source compression being canceled if we're not careful. Guarantee that + // two refs to |ss| are always live in this function (at least one + // preexisting and one held by the task) so that compression is never + // canceled. auto task = MakeUnique<SourceCompressionTask>(cx->runtime(), ss); if (!task) { ReportOutOfMemory(cx); @@ -1913,7 +1928,7 @@ bool js::SynchronouslyCompressSource(JSContext* cx, // Attempt to compress. This may not succeed if OOM happens, but (because // it ordinarily happens on a helper thread) no error will ever be set here. MOZ_ASSERT(!cx->isExceptionPending()); - ss->performTaskWork(task.get()); + task->runTask(); MOZ_ASSERT(!cx->isExceptionPending()); // Convert |ss| from uncompressed to compressed data. diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h @@ -77,7 +77,7 @@ class JitScript; class ModuleObject; class RegExpObject; -class SourceCompressionTask; +class SourceCompressionTaskEntry; class Shape; class SrcNote; class DebugScript; @@ -393,7 +393,7 @@ class ScriptSource { // on the main thread. friend class PendingSourceCompressionEntry; - friend class SourceCompressionTask; + friend class SourceCompressionTaskEntry; friend bool SynchronouslyCompressSource(JSContext* cx, JS::Handle<BaseScript*> script); @@ -1007,7 +1007,7 @@ class ScriptSource { size_t sourceLength); private: - void performTaskWork(SourceCompressionTask* task); + void performTaskWork(SourceCompressionTaskEntry* task); struct TriggerConvertToCompressedSourceFromTask { ScriptSource* const source_;