Backed out changeset 428f1770d076 (bug 1357012) for Assertion failure: missing call to js::ReportOutOfMemory()

This commit is contained in:
Iris Hsiao 2017-04-19 14:58:22 +08:00
Родитель 981bde9d05
Коммит 37eb3d6689
7 изменённых файлов: 127 добавлений и 90 удалений

Просмотреть файл

@ -57,6 +57,7 @@ class MOZ_STACK_CLASS BytecodeCompiler
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());
@ -82,6 +83,7 @@ class MOZ_STACK_CLASS BytecodeCompiler
RootedScriptSource sourceObject;
ScriptSource* scriptSource;
SourceCompressionTask* sourceCompressionTask_;
Maybe<UsedNameTracker> usedNames;
Maybe<Parser<SyntaxParseHandler>> syntaxParser;
@ -171,6 +173,7 @@ BytecodeCompiler::BytecodeCompiler(JSContext* cx,
enclosingScope(cx, enclosingScope),
sourceObject(cx),
scriptSource(nullptr),
sourceCompressionTask_(nullptr),
directives(options.strictOption),
startPosition(keepAtoms),
script(cx)
@ -216,6 +219,40 @@ BytecodeCompiler::createScriptSource(const Maybe<uint32_t>& parameterListEnd)
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()
{
@ -379,7 +416,7 @@ BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
script->scriptSource()->recordParseEnded();
// Enqueue an off-thread source compression task after finishing parsing.
if (!scriptSource->tryCompressOffThread(cx))
if (!enqueueOffThreadSourceCompression())
return nullptr;
MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
@ -444,7 +481,7 @@ BytecodeCompiler::compileModule()
module->setInitialEnvironment(env);
// Enqueue an off-thread source compression task after finishing parsing.
if (!scriptSource->tryCompressOffThread(cx))
if (!enqueueOffThreadSourceCompression())
return nullptr;
MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
@ -500,7 +537,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
return false;
// Enqueue an off-thread source compression task after finishing parsing.
if (!scriptSource->tryCompressOffThread(cx))
if (!enqueueOffThreadSourceCompression())
return false;
return true;
@ -512,6 +549,12 @@ 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() */)
@ -563,17 +606,22 @@ class MOZ_STACK_CLASS AutoInitializeSourceObject
{
BytecodeCompiler& compiler_;
ScriptSourceObject** sourceObjectOut_;
SourceCompressionTask** sourceCompressionTaskOut_;
public:
AutoInitializeSourceObject(BytecodeCompiler& compiler,
ScriptSourceObject** sourceObjectOut)
ScriptSourceObject** sourceObjectOut,
SourceCompressionTask** sourceCompressionTaskOut)
: compiler_(compiler),
sourceObjectOut_(sourceObjectOut)
sourceObjectOut_(sourceObjectOut),
sourceCompressionTaskOut_(sourceCompressionTaskOut)
{ }
~AutoInitializeSourceObject() {
if (sourceObjectOut_)
*sourceObjectOut_ = compiler_.sourceObjectPtr();
if (sourceCompressionTaskOut_)
*sourceCompressionTaskOut_ = compiler_.sourceCompressionTask();
}
};
@ -581,11 +629,12 @@ JSScript*
frontend::CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut)
ScriptSourceObject** sourceObjectOut,
SourceCompressionTask** sourceCompressionTaskOut)
{
MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
BytecodeCompiler compiler(cx, alloc, options, srcBuf, /* enclosingScope = */ nullptr);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut);
return compiler.compileGlobalScript(scopeKind);
}
@ -594,17 +643,19 @@ frontend::CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
HandleObject environment, HandleScope enclosingScope,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut)
ScriptSourceObject** sourceObjectOut,
SourceCompressionTask** sourceCompressionTaskOut)
{
BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingScope);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut);
return compiler.compileEvalScript(environment, enclosingScope);
}
ModuleObject*
frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
SourceBufferHolder& srcBuf, LifoAlloc& alloc,
ScriptSourceObject** sourceObjectOut)
ScriptSourceObject** sourceObjectOut,
SourceCompressionTask** sourceCompressionTaskOut)
{
MOZ_ASSERT(srcBuf.get());
MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
@ -616,7 +667,7 @@ frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInpu
RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
BytecodeCompiler compiler(cx, alloc, options, srcBuf, emptyGlobalScope);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut, sourceCompressionTaskOut);
return compiler.compileModule();
}

Просмотреть файл

@ -23,6 +23,7 @@ class LazyScript;
class LifoAlloc;
class ModuleObject;
class ScriptSourceObject;
class SourceCompressionTask;
namespace frontend {
@ -34,14 +35,16 @@ JSScript*
CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut = nullptr);
ScriptSourceObject** sourceObjectOut = nullptr,
SourceCompressionTask** sourceCompressionTaskOut = nullptr);
JSScript*
CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
HandleObject scopeChain, HandleScope enclosingScope,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
ScriptSourceObject** sourceObjectOut = nullptr);
ScriptSourceObject** sourceObjectOut = nullptr,
SourceCompressionTask** sourceCompressionTaskOut = nullptr);
ModuleObject*
CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
@ -50,7 +53,8 @@ CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
ModuleObject*
CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, LifoAlloc& alloc,
ScriptSourceObject** sourceObjectOut = nullptr);
ScriptSourceObject** sourceObjectOut = nullptr,
SourceCompressionTask** sourceCompressionTaskOut = nullptr);
MOZ_MUST_USE bool
CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);

Просмотреть файл

@ -5053,18 +5053,22 @@ SweepCompressionTasksTask::run()
// Attach finished compression tasks.
auto& finished = HelperThreadState().compressionFinishedList(lock);
for (size_t i = 0; i < finished.length(); i++) {
if (finished[i]->runtimeMatches(runtime())) {
UniquePtr<SourceCompressionTask> task(Move(finished[i]));
SourceCompressionTask* task = finished[i];
if (task->runtimeMatches(runtime())) {
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++) {
if (pending[i]->shouldCancel())
SourceCompressionTask* task = pending[i];
if (task->shouldCancel()) {
HelperThreadState().remove(pending, &i);
js_delete(task);
}
}
}

Просмотреть файл

@ -1821,48 +1821,6 @@ ScriptSource::setSource(SharedImmutableTwoByteString&& string)
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)
return false;
return EnqueueOffThreadCompression(cx, Move(task));
}
MOZ_MUST_USE bool
ScriptSource::setCompressedSource(JSContext* cx,
mozilla::UniquePtr<char[], JS::FreePolicy>&& raw,

Просмотреть файл

@ -573,8 +573,6 @@ class ScriptSource
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,

Просмотреть файл

@ -301,7 +301,7 @@ ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr),
script(nullptr), sourceObject(nullptr), sourceCompressionTask(nullptr),
overRecursed(false), outOfMemory(false)
{
}
@ -313,7 +313,7 @@ ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx, JSObject* parseGlobal,
alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
parseGlobal(parseGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr),
script(nullptr), sourceObject(nullptr), sourceCompressionTask(nullptr),
overRecursed(false), outOfMemory(false)
{
}
@ -340,8 +340,8 @@ ParseTask::finish(JSContext* cx)
RootedScriptSource sso(cx, sourceObject);
if (!ScriptSourceObject::initFromOptions(cx, sso, options))
return false;
if (!sso->source()->tryCompressOffThread(cx))
return false;
if (sourceCompressionTask)
sourceCompressionTask->fixupMajorGCNumber(cx->runtime());
}
return true;
@ -385,7 +385,8 @@ ScriptParseTask::parse(JSContext* cx)
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
script = frontend::CompileGlobalScript(cx, alloc, ScopeKind::Global,
options, srcBuf,
/* sourceObjectOut = */ &sourceObject);
/* sourceObjectOut = */ &sourceObject,
/* sourceCompressionTaskOut = */ &sourceCompressionTask);
}
ModuleParseTask::ModuleParseTask(JSContext* cx, JSObject* parseGlobal,
@ -400,7 +401,8 @@ void
ModuleParseTask::parse(JSContext* cx)
{
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject);
ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, alloc, &sourceObject,
&sourceCompressionTask);
if (module)
script = module->script();
}
@ -1121,13 +1123,13 @@ GlobalHelperThreadState::scheduleCompressionTasks(const AutoLockHelperThreadStat
{
auto& pending = compressionPendingList(lock);
auto& worklist = compressionWorklist(lock);
MOZ_ASSERT(worklist.capacity() >= pending.length());
for (size_t i = 0; i < pending.length(); i++) {
if (pending[i]->shouldStart()) {
// OOMing during appending results in the task not being scheduled
// and deleted.
Unused << worklist.append(Move(pending[i]));
SourceCompressionTask* task = pending[i];
if (task->shouldStart()) {
remove(pending, &i);
worklist.infallibleAppend(task);
}
}
}
@ -1728,13 +1730,8 @@ HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
MOZ_ASSERT(HelperThreadState().canStartCompressionTask(locked));
MOZ_ASSERT(idle());
UniquePtr<SourceCompressionTask> task;
{
auto& worklist = HelperThreadState().compressionWorklist(locked);
task = Move(worklist.back());
worklist.popBack();
currentTask.emplace(task.get());
}
currentTask.emplace(HelperThreadState().compressionWorklist(locked).popCopy());
SourceCompressionTask* task = compressionTask();
{
AutoUnlockHelperThreadState unlock(locked);
@ -1747,7 +1744,7 @@ HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
{
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!HelperThreadState().compressionFinishedList(locked).append(Move(task)))
if (!HelperThreadState().compressionFinishedList(locked).append(task))
oomUnsafe.crash("handleCompressionWorkload");
}
@ -1758,14 +1755,22 @@ HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
}
bool
js::EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task)
js::EnqueueOffThreadCompression(JSContext* cx, SourceCompressionTask* task)
{
AutoLockHelperThreadState lock;
auto& pending = HelperThreadState().compressionPendingList(lock);
if (!pending.append(Move(task))) {
auto& worklist = HelperThreadState().compressionWorklist(lock);
if (!pending.append(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;
}
@ -1777,8 +1782,11 @@ static void
ClearCompressionTaskList(T& list, JSRuntime* runtime)
{
for (size_t i = 0; i < list.length(); i++) {
if (list[i]->runtimeMatches(runtime))
SourceCompressionTask* task = list[i];
if (task->runtimeMatches(runtime)) {
HelperThreadState().remove(list, &i);
js_delete(task);
}
}
}

Просмотреть файл

@ -72,7 +72,7 @@ class GlobalHelperThreadState
typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
typedef Vector<UniquePtr<SourceCompressionTask>, 0, SystemAllocPolicy> SourceCompressionTaskVector;
typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
typedef Vector<GCHelperState*, 0, SystemAllocPolicy> GCHelperStateVector;
typedef Vector<GCParallelTask*, 0, SystemAllocPolicy> GCParallelTaskVector;
typedef Vector<PromiseTask*, 0, SystemAllocPolicy> PromiseTaskVector;
@ -165,10 +165,7 @@ class GlobalHelperThreadState
template <typename T>
void remove(T& vector, size_t* index)
{
// Self-moving is undefined behavior.
if (*index != vector.length() - 1)
vector[*index] = mozilla::Move(vector.back());
(*index)--;
vector[(*index)--] = vector.back();
vector.popBack();
}
@ -553,7 +550,7 @@ struct AutoEnqueuePendingParseTasksAfterGC {
// Enqueue a compression job to be processed if there's a major GC.
bool
EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task);
EnqueueOffThreadCompression(JSContext* cx, SourceCompressionTask* task);
// Cancel all scheduled, in progress, or finished compression tasks for
// runtime.
@ -626,6 +623,10 @@ struct ParseTask
// 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;
@ -703,6 +704,7 @@ class SourceCompressionTask
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.
@ -715,19 +717,31 @@ class SourceCompressionTask
mozilla::Maybe<SharedImmutableString> resultString_;
public:
// The majorGCNumber is used for scheduling tasks.
// 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.
SourceCompressionTask(JSRuntime* rt, ScriptSource* source)
: runtime_(rt),
majorGCNumber_(rt->gc.majorGCCount()),
majorGCNumber_(CurrentThreadCanAccessRuntime(rt)
? rt->gc.majorGCCount()
: MajorGCNumberWaitingForFixup),
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;
}