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:
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_;