author | Shu-yu Guo <shu@rfrn.org> |
Wed, 19 Apr 2017 13:58:01 -0700 | |
changeset 353984 | bcf9341329ab5ade13acd349acc96cdafe5e883e |
parent 353983 | 70debe6d9f951070571152bf144da9d406ca3058 |
child 353985 | 0a2fca8aa2e64c151f566c189e03f6dbf303e47b |
push id | 31684 |
push user | cbook@mozilla.com |
push date | Thu, 20 Apr 2017 09:13:26 +0000 |
treeherder | mozilla-central@27311156637f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jonco |
bugs | 1357012 |
milestone | 55.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -52,17 +52,16 @@ class MOZ_STACK_CLASS BytecodeCompiler ScriptSourceObject* sourceObjectPtr() const; SourceCompressionTask* sourceCompressionTask() const; private: JSScript* compileScript(HandleObject environment, SharedContext* sc); bool checkLength(); bool createScriptSource(const Maybe<uint32_t>& parameterListEnd); - bool enqueueOffThreadSourceCompression(); bool canLazilyParse(); bool createParser(); bool createSourceAndParser(const Maybe<uint32_t>& parameterListEnd = Nothing()); // If toString{Start,End} are not explicitly passed, assume the script's // offsets in the source used to parse it are the same as what should be // used to compute its Function.prototype.toString() value. bool createScript(); @@ -78,17 +77,16 @@ class MOZ_STACK_CLASS BytecodeCompiler LifoAlloc& alloc; const ReadOnlyCompileOptions& options; SourceBufferHolder& sourceBuffer; RootedScope enclosingScope; RootedScriptSource sourceObject; ScriptSource* scriptSource; - SourceCompressionTask* sourceCompressionTask_; Maybe<UsedNameTracker> usedNames; Maybe<Parser<SyntaxParseHandler>> syntaxParser; Maybe<Parser<FullParseHandler>> parser; Directives directives; TokenStream::Position startPosition; @@ -168,17 +166,16 @@ BytecodeCompiler::BytecodeCompiler(JSCon : keepAtoms(cx), cx(cx), alloc(alloc), options(options), sourceBuffer(sourceBuffer), enclosingScope(cx, enclosingScope), sourceObject(cx), scriptSource(nullptr), - sourceCompressionTask_(nullptr), directives(options.strictOption), startPosition(keepAtoms), script(cx) { MOZ_ASSERT(sourceBuffer.get()); } bool @@ -215,50 +212,16 @@ BytecodeCompiler::createScriptSource(con return false; } } return true; } bool -BytecodeCompiler::enqueueOffThreadSourceCompression() -{ - // There are several cases where source compression is not a good idea: - // - If the script is tiny, then compression will save little or no space. - // - If there is only one core, then compression will contend with JS - // execution (which hurts benchmarketing). - // - // Otherwise, enqueue a compression task to be processed when a major - // GC is requested. - - if (!scriptSource->hasUncompressedSource()) - return true; - - bool canCompressOffThread = - HelperThreadState().cpuCount > 1 && - HelperThreadState().threadCount >= 2 && - CanUseExtraThreads(); - const size_t TINY_SCRIPT = 256; - if (TINY_SCRIPT <= sourceBuffer.length() && canCompressOffThread) { - // Heap allocate the task. It will be freed upon compression - // completing in AttachFinishedCompressedSources. - SourceCompressionTask* task = cx->new_<SourceCompressionTask>(cx->runtime(), - scriptSource); - if (!task) - return false; - if (!EnqueueOffThreadCompression(cx, task)) - return false; - sourceCompressionTask_ = task; - } - - return true; -} - -bool BytecodeCompiler::canLazilyParse() { return options.canLazilyParse && !(enclosingScope && enclosingScope->hasOnChain(ScopeKind::NonSyntactic)) && !cx->compartment()->behaviors().disableLazyParsing() && !cx->compartment()->behaviors().discardSource() && !options.sourceIsLazy && !cx->lcovEnabled(); @@ -411,17 +374,17 @@ BytecodeCompiler::compileScript(HandleOb usedNames->reset(); } // We have just finished parsing the source. Inform the source so that we // can compute statistics (e.g. how much time our functions remain lazy). script->scriptSource()->recordParseEnded(); // Enqueue an off-thread source compression task after finishing parsing. - if (!enqueueOffThreadSourceCompression()) + if (!scriptSource->tryCompressOffThread(cx)) return nullptr; MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending()); return script; } JSScript* @@ -476,17 +439,17 @@ BytecodeCompiler::compileModule() RootedModuleEnvironmentObject env(cx, ModuleEnvironmentObject::create(cx, module)); if (!env) return nullptr; module->setInitialEnvironment(env); // Enqueue an off-thread source compression task after finishing parsing. - if (!enqueueOffThreadSourceCompression()) + if (!scriptSource->tryCompressOffThread(cx)) return nullptr; MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending()); return module; } // Compile a standalone JS function, which might appear as the value of an // event handler attribute in an HTML <INPUT> tag, or in a Function() @@ -532,34 +495,28 @@ BytecodeCompiler::compileStandaloneFunct fun.set(fn->pn_funbox->function()); MOZ_ASSERT(IsAsmJSModule(fun)); } if (!NameFunctions(cx, fn)) return false; // Enqueue an off-thread source compression task after finishing parsing. - if (!enqueueOffThreadSourceCompression()) + if (!scriptSource->tryCompressOffThread(cx)) return false; return true; } ScriptSourceObject* BytecodeCompiler::sourceObjectPtr() const { return sourceObject.get(); } -SourceCompressionTask* -BytecodeCompiler::sourceCompressionTask() const -{ - return sourceCompressionTask_; -} - ScriptSourceObject* frontend::CreateScriptSourceObject(JSContext* cx, const ReadOnlyCompileOptions& options, const Maybe<uint32_t>& parameterListEnd /* = Nothing() */) { ScriptSource* ss = cx->new_<ScriptSource>(); if (!ss) return nullptr; ScriptSourceHolder ssHolder(ss); @@ -601,78 +558,70 @@ frontend::CreateScriptSourceObject(JSCon // Whatever happens to the top-level script compilation (even if it fails and // returns null), we must finish initializing the SSO. This is because there // may be valid inner scripts observable by the debugger which reference the // partially-initialized SSO. class MOZ_STACK_CLASS AutoInitializeSourceObject { BytecodeCompiler& compiler_; ScriptSourceObject** sourceObjectOut_; - SourceCompressionTask** sourceCompressionTaskOut_; public: AutoInitializeSourceObject(BytecodeCompiler& compiler, - ScriptSourceObject** sourceObjectOut, - SourceCompressionTask** sourceCompressionTaskOut) + ScriptSourceObject** sourceObjectOut) : compiler_(compiler), - sourceObjectOut_(sourceObjectOut), - sourceCompressionTaskOut_(sourceCompressionTaskOut) + sourceObjectOut_(sourceObjectOut) { } ~AutoInitializeSourceObject() { if (sourceObjectOut_) *sourceObjectOut_ = compiler_.sourceObjectPtr(); - if (sourceCompressionTaskOut_) - *sourceCompressionTaskOut_ = compiler_.sourceCompressionTask(); } }; JSScript* frontend::CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, - ScriptSourceObject** sourceObjectOut, - SourceCompressionTask** sourceCompressionTaskOut) + ScriptSourceObject** sourceObjectOut) { MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic); BytecodeCompiler compiler(cx, alloc, options, srcBuf, /* enclosingScope = */ nullptr); - AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut); + AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut); return compiler.compileGlobalScript(scopeKind); } JSScript* frontend::CompileEvalScript(JSContext* cx, LifoAlloc& alloc, HandleObject environment, HandleScope enclosingScope, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, - ScriptSourceObject** sourceObjectOut, - SourceCompressionTask** sourceCompressionTaskOut) + ScriptSourceObject** sourceObjectOut) { BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingScope); - AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut); + AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut); return compiler.compileEvalScript(environment, enclosingScope); } ModuleObject* frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInput, SourceBufferHolder& srcBuf, LifoAlloc& alloc, - ScriptSourceObject** sourceObjectOut, - SourceCompressionTask** sourceCompressionTaskOut) + ScriptSourceObject** sourceObjectOut) { MOZ_ASSERT(srcBuf.get()); MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr); CompileOptions options(cx, optionsInput); options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code. options.setIsRunOnce(true); options.allowHTMLComments = false; RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope()); BytecodeCompiler compiler(cx, alloc, options, srcBuf, emptyGlobalScope); - AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut); + AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut); return compiler.compileModule(); } ModuleObject* frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf) { if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
--- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -18,48 +18,44 @@ class JSLinearString; namespace js { class LazyScript; class LifoAlloc; class ModuleObject; class ScriptSourceObject; -class SourceCompressionTask; namespace frontend { class TokenStream; class FunctionBox; class ParseNode; JSScript* CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, - ScriptSourceObject** sourceObjectOut = nullptr, - SourceCompressionTask** sourceCompressionTaskOut = nullptr); + ScriptSourceObject** sourceObjectOut = nullptr); JSScript* CompileEvalScript(JSContext* cx, LifoAlloc& alloc, HandleObject scopeChain, HandleScope enclosingScope, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, - ScriptSourceObject** sourceObjectOut = nullptr, - SourceCompressionTask** sourceCompressionTaskOut = nullptr); + ScriptSourceObject** sourceObjectOut = nullptr); ModuleObject* CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf); ModuleObject* CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, LifoAlloc& alloc, - ScriptSourceObject** sourceObjectOut = nullptr, - SourceCompressionTask** sourceCompressionTaskOut = nullptr); + ScriptSourceObject** sourceObjectOut = nullptr); MOZ_MUST_USE bool CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length); // // Compile a single function. The source in srcBuf must match the ECMA-262 // FunctionExpression production. //
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -5048,32 +5048,28 @@ SweepMiscTask::run() /* virtual */ void SweepCompressionTasksTask::run() { AutoLockHelperThreadState lock; // Attach finished compression tasks. auto& finished = HelperThreadState().compressionFinishedList(lock); for (size_t i = 0; i < finished.length(); i++) { - SourceCompressionTask* task = finished[i]; - if (task->runtimeMatches(runtime())) { + if (finished[i]->runtimeMatches(runtime())) { + UniquePtr<SourceCompressionTask> task(Move(finished[i])); HelperThreadState().remove(finished, &i); task->complete(); - js_delete(task); } } // Sweep pending tasks that are holding onto should-be-dead ScriptSources. auto& pending = HelperThreadState().compressionPendingList(lock); for (size_t i = 0; i < pending.length(); i++) { - SourceCompressionTask* task = pending[i]; - if (task->shouldCancel()) { + if (pending[i]->shouldCancel()) HelperThreadState().remove(pending, &i); - js_delete(task); - } } } void GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked) { if (!task.startWithLockHeld(locked)) { AutoUnlockHelperThreadState unlock(locked);
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1816,16 +1816,60 @@ ScriptSource::setSource(JSContext* cx, void ScriptSource::setSource(SharedImmutableTwoByteString&& string) { MOZ_ASSERT(data.is<Missing>()); data = SourceType(Uncompressed(mozilla::Move(string))); } +bool +ScriptSource::tryCompressOffThread(JSContext* cx) +{ + if (!data.is<Uncompressed>()) + return true; + + // There are several cases where source compression is not a good idea: + // - If the script is tiny, then compression will save little or no space. + // - If there is only one core, then compression will contend with JS + // execution (which hurts benchmarketing). + // + // Otherwise, enqueue a compression task to be processed when a major + // GC is requested. + + bool canCompressOffThread = + HelperThreadState().cpuCount > 1 && + HelperThreadState().threadCount >= 2 && + CanUseExtraThreads(); + const size_t TINY_SCRIPT = 256; + if (TINY_SCRIPT > length() || !canCompressOffThread) + return true; + + // The SourceCompressionTask needs to record the major GC number for + // scheduling. If we're parsing off thread, this number is not safe to + // access. + // + // When parsing on the main thread, the attempts made to compress off + // thread in BytecodeCompiler will succeed. + // + // When parsing off-thread, the above attempts will fail and the attempt + // made in ParseTask::finish will succeed. + if (!CurrentThreadCanAccessRuntime(cx->runtime())) + return true; + + // Heap allocate the task. It will be freed upon compression + // completing in AttachFinishedCompressedSources. + auto task = MakeUnique<SourceCompressionTask>(cx->runtime(), this); + if (!task) { + ReportOutOfMemory(cx); + return false; + } + return EnqueueOffThreadCompression(cx, Move(task)); +} + MOZ_MUST_USE bool ScriptSource::setCompressedSource(JSContext* cx, mozilla::UniquePtr<char[], JS::FreePolicy>&& raw, size_t rawLength, size_t sourceLength) { MOZ_ASSERT(raw); auto& cache = cx->zone()->runtimeFromAnyThread()->sharedImmutableStrings();
--- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -568,16 +568,18 @@ class ScriptSource void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ScriptSourceInfo* info) const; MOZ_MUST_USE bool setSource(JSContext* cx, UniqueTwoByteChars&& source, size_t length); void setSource(SharedImmutableTwoByteString&& string); + MOZ_MUST_USE bool tryCompressOffThread(JSContext* cx); + MOZ_MUST_USE bool setCompressedSource(JSContext* cx, UniqueChars&& raw, size_t rawLength, size_t sourceLength); void setCompressedSource(SharedImmutableString&& raw, size_t sourceLength); // XDR handling template <XDRMode mode>
--- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -296,29 +296,29 @@ static const JSClass parseTaskGlobalClas ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal, const char16_t* chars, size_t length, JS::OffThreadCompileCallback callback, void* callbackData) : kind(kind), options(cx), chars(chars), length(length), alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), parseGlobal(parseGlobal), callback(callback), callbackData(callbackData), - script(nullptr), sourceObject(nullptr), sourceCompressionTask(nullptr), + script(nullptr), sourceObject(nullptr), overRecursed(false), outOfMemory(false) { } ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal, JS::TranscodeBuffer& buffer, size_t cursor, JS::OffThreadCompileCallback callback, void* callbackData) : kind(kind), options(cx), buffer(&buffer), cursor(cursor), alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), parseGlobal(parseGlobal), callback(callback), callbackData(callbackData), - script(nullptr), sourceObject(nullptr), sourceCompressionTask(nullptr), + script(nullptr), sourceObject(nullptr), overRecursed(false), outOfMemory(false) { } bool ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options) { if (!this->options.copy(cx, options)) @@ -335,18 +335,18 @@ ParseTask::activate(JSRuntime* rt) bool ParseTask::finish(JSContext* cx) { if (sourceObject) { RootedScriptSource sso(cx, sourceObject); if (!ScriptSourceObject::initFromOptions(cx, sso, options)) return false; - if (sourceCompressionTask) - sourceCompressionTask->fixupMajorGCNumber(cx->runtime()); + if (!sso->source()->tryCompressOffThread(cx)) + return false; } return true; } ParseTask::~ParseTask() { for (size_t i = 0; i < errors.length(); i++) @@ -380,34 +380,32 @@ ScriptParseTask::ScriptParseTask(JSConte } void ScriptParseTask::parse(JSContext* cx) { SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership); script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global, options, srcBuf, - /* sourceObjectOut = */ &sourceObject, - /* sourceCompressionTaskOut = */ &sourceCompressionTask); + /* sourceObjectOut = */ &sourceObject); } ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal, const char16_t* chars, size_t length, JS::OffThreadCompileCallback callback, void* callbackData) : ParseTask(ParseTaskKind::Module, cx, parseGlobal, chars, length, callback, callbackData) { } void ModuleParseTask::parse(JSContext* cx) { SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership); - ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject, - &sourceCompressionTask); + ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject); if (module) script = module->script(); } ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, JSObject* parseGlobal, JS::TranscodeBuffer& buffer, size_t cursor, JS::OffThreadCompileCallback callback, void* callbackData) : ParseTask(ParseTaskKind::ScriptDecode, cx, parseGlobal, @@ -1118,23 +1116,23 @@ GlobalHelperThreadState::startHandlingCo notifyOne(PRODUCER, lock); } void GlobalHelperThreadState::scheduleCompressionTasks(const AutoLockHelperThreadState& lock) { auto& pending = compressionPendingList(lock); auto& worklist = compressionWorklist(lock); - MOZ_ASSERT(worklist.capacity() >= pending.length()); for (size_t i = 0; i < pending.length(); i++) { - SourceCompressionTask* task = pending[i]; - if (task->shouldStart()) { + if (pending[i]->shouldStart()) { + // OOMing during appending results in the task not being scheduled + // and deleted. + Unused << worklist.append(Move(pending[i])); remove(pending, &i); - worklist.infallibleAppend(task); } } } bool GlobalHelperThreadState::canStartGCHelperTask(const AutoLockHelperThreadState& lock) { return !gcHelperWorklist(lock).empty() && @@ -1725,73 +1723,67 @@ HelperThread::handleParseWorkload(AutoLo } void HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked) { MOZ_ASSERT(HelperThreadState().canStartCompressionTask(locked)); MOZ_ASSERT(idle()); - currentTask.emplace(HelperThreadState().compressionWorklist(locked).popCopy()); - SourceCompressionTask* task = compressionTask(); + UniquePtr<SourceCompressionTask> task; + { + auto& worklist = HelperThreadState().compressionWorklist(locked); + task = Move(worklist.back()); + worklist.popBack(); + currentTask.emplace(task.get()); + } { AutoUnlockHelperThreadState unlock(locked); TraceLoggerThread* logger = TraceLoggerForCurrentThread(); AutoTraceLog logCompile(logger, TraceLogger_CompressSource); task->work(); } { AutoEnterOOMUnsafeRegion oomUnsafe; - if (!HelperThreadState().compressionFinishedList(locked).append(task)) + if (!HelperThreadState().compressionFinishedList(locked).append(Move(task))) oomUnsafe.crash("handleCompressionWorkload"); } currentTask.reset(); // Notify the active thread in case it is waiting for the compression to finish. HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked); } bool -js::EnqueueOffThreadCompression(JSContext* cx, SourceCompressionTask* task) +js::EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task) { AutoLockHelperThreadState lock; auto& pending = HelperThreadState().compressionPendingList(lock); - auto& worklist = HelperThreadState().compressionWorklist(lock); - if (!pending.append(task)) { + if (!pending.append(Move(task))) { if (!cx->helperThread()) ReportOutOfMemory(cx); - js_delete(task); - return false; - } - if (!worklist.reserve(pending.length())) { - if (!cx->helperThread()) - ReportOutOfMemory(cx); - pending.popBack(); return false; } return true; } template <typename T> static void ClearCompressionTaskList(T& list, JSRuntime* runtime) { for (size_t i = 0; i < list.length(); i++) { - SourceCompressionTask* task = list[i]; - if (task->runtimeMatches(runtime)) { + if (list[i]->runtimeMatches(runtime)) HelperThreadState().remove(list, &i); - js_delete(task); - } } } void js::CancelOffThreadCompressions(JSRuntime* runtime) { AutoLockHelperThreadState lock;
--- a/js/src/vm/HelperThreads.h +++ b/js/src/vm/HelperThreads.h @@ -67,17 +67,17 @@ class GlobalHelperThreadState // May be accessed without locking. size_t cpuCount; // Number of threads to create. May be accessed without locking. size_t threadCount; typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector; typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector; - typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector; + typedef Vector<UniquePtr<SourceCompressionTask>, 0, SystemAllocPolicy> SourceCompressionTaskVector; typedef Vector<GCHelperState*, 0, SystemAllocPolicy> GCHelperStateVector; typedef Vector<GCParallelTask*, 0, SystemAllocPolicy> GCParallelTaskVector; typedef Vector<PromiseTask*, 0, SystemAllocPolicy> PromiseTaskVector; // List of available threads, or null if the thread state has not been initialized. using HelperThreadVector = Vector<HelperThread, 0, SystemAllocPolicy>; UniquePtr<HelperThreadVector> threads; @@ -160,17 +160,20 @@ class GlobalHelperThreadState mozilla::TimeDuration timeout = mozilla::TimeDuration::Forever()); void notifyAll(CondVar which, const AutoLockHelperThreadState&); void notifyOne(CondVar which, const AutoLockHelperThreadState&); // Helper method for removing items from the vectors below while iterating over them. template <typename T> void remove(T& vector, size_t* index) { - vector[(*index)--] = vector.back(); + // Self-moving is undefined behavior. + if (*index != vector.length() - 1) + vector[*index] = mozilla::Move(vector.back()); + (*index)--; vector.popBack(); } IonBuilderVector& ionWorklist(const AutoLockHelperThreadState&) { return ionWorklist_; } IonBuilderVector& ionFinishedList(const AutoLockHelperThreadState&) { return ionFinishedList_; @@ -545,17 +548,17 @@ EnqueuePendingParseTasksAfterGC(JSRuntim struct AutoEnqueuePendingParseTasksAfterGC { const gc::GCRuntime& gc_; explicit AutoEnqueuePendingParseTasksAfterGC(const gc::GCRuntime& gc) : gc_(gc) {} ~AutoEnqueuePendingParseTasksAfterGC(); }; // Enqueue a compression job to be processed if there's a major GC. bool -EnqueueOffThreadCompression(JSContext* cx, SourceCompressionTask* task); +EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task); // Cancel all scheduled, in progress, or finished compression tasks for // runtime. void CancelOffThreadCompressions(JSRuntime* runtime); class MOZ_RAII AutoLockHelperThreadState : public LockGuard<Mutex> { @@ -618,20 +621,16 @@ struct ParseTask // Holds the final script between the invocation of the callback and the // point where FinishOffThreadScript is called, which will destroy the // ParseTask. JSScript* script; // Holds the ScriptSourceObject generated for the script compilation. ScriptSourceObject* sourceObject; - // Holds the SourceCompressionTask, if any were enqueued for the - // ScriptSource of sourceObject. - SourceCompressionTask* sourceCompressionTask; - // Any errors or warnings produced during compilation. These are reported // when finishing the script. Vector<CompileError*, 0, SystemAllocPolicy> errors; bool overRecursed; bool outOfMemory; ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal, const char16_t* chars, size_t length, @@ -699,54 +698,41 @@ class SourceCompressionTask friend struct 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_; // The major GC number of the runtime when the task was enqueued. - static const uint64_t MajorGCNumberWaitingForFixup = UINT64_MAX; uint64_t majorGCNumber_; // The source to be compressed. ScriptSourceHolder sourceHolder_; // The resultant compressed string. If the compressed string is larger // than the original, or we OOM'd during compression, or nothing else // except the task is holding the ScriptSource alive when scheduled to // compress, this will remain None upon completion. mozilla::Maybe<SharedImmutableString> resultString_; public: - // The majorGCNumber is used for scheduling tasks. If the task is being - // enqueued from an off-thread parsing task, leave the GC number - // UINT64_MAX to be fixed up when the parse task finishes. + // The majorGCNumber is used for scheduling tasks. SourceCompressionTask(JSRuntime* rt, ScriptSource* source) : runtime_(rt), - majorGCNumber_(CurrentThreadCanAccessRuntime(rt) - ? rt->gc.majorGCCount() - : MajorGCNumberWaitingForFixup), + majorGCNumber_(rt->gc.majorGCCount()), sourceHolder_(source) { } bool runtimeMatches(JSRuntime* runtime) const { return runtime == runtime_; } - - void fixupMajorGCNumber(JSRuntime* runtime) { - MOZ_ASSERT(majorGCNumber_ == MajorGCNumberWaitingForFixup); - majorGCNumber_ = runtime->gc.majorGCCount(); - } - bool shouldStart() const { // We wait 2 major GCs to start compressing, in order to avoid // immediate compression. - if (majorGCNumber_ == MajorGCNumberWaitingForFixup) - return false; return runtime_->gc.majorGCCount() > majorGCNumber_ + 1; } bool shouldCancel() const { // If the refcount is exactly 1, then nothing else is holding on to the // ScriptSource, so no reason to compress it and we should cancel the task. return sourceHolder_.get()->refs == 1; }