Bug 949296 - Ignore DontStopIon interrupt triggers during ForkJoin. (r=nmatsakis)

This commit is contained in:
Shu-yu Guo 2014-02-07 14:40:31 -08:00
parent c6165cfa30
commit 488bdb76a7
25 changed files with 113 additions and 81 deletions

View File

@ -17,6 +17,5 @@ function test() {
} }
} }
// FIXME: Bug 949296. Broken due to all interrupt triggers aborting PJS. if (getBuildConfiguration().parallelJS)
//if (getBuildConfiguration().parallelJS) test();
// test();

View File

@ -275,7 +275,7 @@ CanEnterBaselineJIT(JSContext *cx, HandleScript script, bool osr)
// parallel execution. We want to avoid the situation of OSRing during // parallel execution. We want to avoid the situation of OSRing during
// warmup and only gathering type information for the loop, and not the // warmup and only gathering type information for the loop, and not the
// rest of the function. // rest of the function.
if (IsJSDEnabled(cx) || cx->runtime()->parallelWarmup > 0) { if (IsJSDEnabled(cx) || cx->runtime()->forkJoinWarmup > 0) {
if (osr) if (osr)
return Method_Skipped; return Method_Skipped;
} else if (script->incUseCount() <= js_JitOptions.baselineUsesBeforeCompile) { } else if (script->incUseCount() <= js_JitOptions.baselineUsesBeforeCompile) {

View File

@ -2851,7 +2851,7 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
if (!addOutOfLineCode(ool)) if (!addOutOfLineCode(ool))
return false; return false;
masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry()); masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
masm.checkInterruptFlagsPar(tempReg, ool->entry()); masm.checkInterruptFlagPar(tempReg, ool->entry());
masm.bind(ool->rejoin()); masm.bind(ool->rejoin());
return true; return true;
@ -2886,41 +2886,36 @@ CodeGenerator::visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool
} }
// Out-of-line path to report over-recursed error and fail. // Out-of-line path to report over-recursed error and fail.
class OutOfLineCheckInterruptPar : public OutOfLineCodeBase<CodeGenerator> class OutOfLineInterruptCheckPar : public OutOfLineCodeBase<CodeGenerator>
{ {
public: public:
LCheckInterruptPar *const lir; LInterruptCheckPar *const lir;
OutOfLineCheckInterruptPar(LCheckInterruptPar *lir) OutOfLineInterruptCheckPar(LInterruptCheckPar *lir)
: lir(lir) : lir(lir)
{ } { }
bool accept(CodeGenerator *codegen) { bool accept(CodeGenerator *codegen) {
return codegen->visitOutOfLineCheckInterruptPar(this); return codegen->visitOutOfLineInterruptCheckPar(this);
} }
}; };
bool bool
CodeGenerator::visitCheckInterruptPar(LCheckInterruptPar *lir) CodeGenerator::visitInterruptCheckPar(LInterruptCheckPar *lir)
{ {
// First check for cx->shared->interrupt_. // First check for cx->shared->interrupt_.
OutOfLineCheckInterruptPar *ool = new(alloc()) OutOfLineCheckInterruptPar(lir); OutOfLineInterruptCheckPar *ool = new(alloc()) OutOfLineInterruptCheckPar(lir);
if (!addOutOfLineCode(ool)) if (!addOutOfLineCode(ool))
return false; return false;
// We must check two flags:
// - runtime->interrupt
// - runtime->parallelAbort
// See vm/ForkJoin.h for discussion on why we use this design.
Register tempReg = ToRegister(lir->getTempReg()); Register tempReg = ToRegister(lir->getTempReg());
masm.checkInterruptFlagsPar(tempReg, ool->entry()); masm.checkInterruptFlagPar(tempReg, ool->entry());
masm.bind(ool->rejoin()); masm.bind(ool->rejoin());
return true; return true;
} }
bool bool
CodeGenerator::visitOutOfLineCheckInterruptPar(OutOfLineCheckInterruptPar *ool) CodeGenerator::visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool)
{ {
OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir); OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir);
if (!bail) if (!bail)
@ -2929,7 +2924,7 @@ CodeGenerator::visitOutOfLineCheckInterruptPar(OutOfLineCheckInterruptPar *ool)
// Avoid saving/restoring the temp register since we will put the // Avoid saving/restoring the temp register since we will put the
// ReturnReg into it below and we don't want to clobber that // ReturnReg into it below and we don't want to clobber that
// during PopRegsInMask(): // during PopRegsInMask():
LCheckInterruptPar *lir = ool->lir; LInterruptCheckPar *lir = ool->lir;
Register tempReg = ToRegister(lir->getTempReg()); Register tempReg = ToRegister(lir->getTempReg());
RegisterSet saveSet(lir->safepoint()->liveRegs()); RegisterSet saveSet(lir->safepoint()->liveRegs());
saveSet.takeUnchecked(tempReg); saveSet.takeUnchecked(tempReg);
@ -2938,7 +2933,7 @@ CodeGenerator::visitOutOfLineCheckInterruptPar(OutOfLineCheckInterruptPar *ool)
masm.movePtr(ToRegister(ool->lir->forkJoinContext()), CallTempReg0); masm.movePtr(ToRegister(ool->lir->forkJoinContext()), CallTempReg0);
masm.setupUnalignedABICall(1, CallTempReg1); masm.setupUnalignedABICall(1, CallTempReg1);
masm.passABIArg(CallTempReg0); masm.passABIArg(CallTempReg0);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CheckInterruptPar)); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InterruptCheckPar));
masm.movePtr(ReturnReg, tempReg); masm.movePtr(ReturnReg, tempReg);
masm.PopRegsInMask(saveSet); masm.PopRegsInMask(saveSet);
masm.branchIfFalseBool(tempReg, bail->entry()); masm.branchIfFalseBool(tempReg, bail->entry());

View File

@ -30,7 +30,7 @@ class OutOfLineNewArray;
class OutOfLineNewObject; class OutOfLineNewObject;
class CheckOverRecursedFailure; class CheckOverRecursedFailure;
class CheckOverRecursedFailurePar; class CheckOverRecursedFailurePar;
class OutOfLineCheckInterruptPar; class OutOfLineInterruptCheckPar;
class OutOfLineInterruptCheckImplicit; class OutOfLineInterruptCheckImplicit;
class OutOfLineUnboxFloatingPoint; class OutOfLineUnboxFloatingPoint;
class OutOfLineStoreElementHole; class OutOfLineStoreElementHole;
@ -291,8 +291,8 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitCheckOverRecursedPar(LCheckOverRecursedPar *lir); bool visitCheckOverRecursedPar(LCheckOverRecursedPar *lir);
bool visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool); bool visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool);
bool visitCheckInterruptPar(LCheckInterruptPar *lir); bool visitInterruptCheckPar(LInterruptCheckPar *lir);
bool visitOutOfLineCheckInterruptPar(OutOfLineCheckInterruptPar *ool); bool visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool);
bool visitInterruptCheckImplicit(LInterruptCheckImplicit *ins); bool visitInterruptCheckImplicit(LInterruptCheckImplicit *ins);
bool visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ins); bool visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ins);

View File

@ -71,6 +71,14 @@ CompileRuntime::addressOfInterrupt()
return &runtime()->interrupt; return &runtime()->interrupt;
} }
#ifdef JS_THREADSAFE
const void *
CompileRuntime::addressOfInterruptPar()
{
return &runtime()->interruptPar;
}
#endif
const JitRuntime * const JitRuntime *
CompileRuntime::jitRuntime() CompileRuntime::jitRuntime()
{ {

View File

@ -52,6 +52,10 @@ class CompileRuntime
const void *addressOfInterrupt(); const void *addressOfInterrupt();
#ifdef JS_THREADSAFE
const void *addressOfInterruptPar();
#endif
const JitRuntime *jitRuntime(); const JitRuntime *jitRuntime();
// Compilation does not occur off thread when the SPS profiler is enabled. // Compilation does not occur off thread when the SPS profiler is enabled.

View File

@ -433,6 +433,7 @@ jit::TriggerOperationCallbackForIonCode(JSRuntime *rt,
break; break;
case JSRuntime::TriggerCallbackAnyThreadDontStopIon: case JSRuntime::TriggerCallbackAnyThreadDontStopIon:
case JSRuntime::TriggerCallbackAnyThreadForkJoin:
// When the trigger does not require Ion code to be interrupted, // When the trigger does not require Ion code to be interrupted,
// nothing more needs to be done. // nothing more needs to be done.
break; break;

View File

@ -875,11 +875,14 @@ MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register
} }
void void
MacroAssembler::checkInterruptFlagsPar(const Register &tempReg, MacroAssembler::checkInterruptFlagPar(const Register &tempReg, Label *fail)
Label *fail)
{ {
movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterrupt()), tempReg); #ifdef JS_THREADSAFE
movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptPar()), tempReg);
branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail); branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail);
#else
MOZ_ASSUME_UNREACHABLE("JSRuntime::interruptPar doesn't exist on non-threadsafe builds.");
#endif
} }
static void static void

View File

@ -797,7 +797,7 @@ class MacroAssembler : public MacroAssemblerSpecific
// Checks the flags that signal that parallel code may need to interrupt or // Checks the flags that signal that parallel code may need to interrupt or
// abort. Branches to fail in that case. // abort. Branches to fail in that case.
void checkInterruptFlagsPar(const Register &tempReg, Label *fail); void checkInterruptFlagPar(const Register &tempReg, Label *fail);
// If the JitCode that created this assembler needs to transition into the VM, // If the JitCode that created this assembler needs to transition into the VM,
// we want to store the JitCode on the stack in order to mark it during a GC. // we want to store the JitCode on the stack in order to mark it during a GC.

View File

@ -737,12 +737,12 @@ class LInterruptCheckImplicit : public LInstructionHelper<0, 0, 0>
} }
}; };
class LCheckInterruptPar : public LInstructionHelper<0, 1, 1> class LInterruptCheckPar : public LInstructionHelper<0, 1, 1>
{ {
public: public:
LIR_HEADER(CheckInterruptPar); LIR_HEADER(InterruptCheckPar);
LCheckInterruptPar(const LAllocation &cx, const LDefinition &tempReg) { LInterruptCheckPar(const LAllocation &cx, const LDefinition &tempReg) {
setOperand(0, cx); setOperand(0, cx);
setTemp(0, tempReg); setTemp(0, tempReg);
} }

View File

@ -285,7 +285,7 @@
_(AsmJSPassStackArg) \ _(AsmJSPassStackArg) \
_(AsmJSCall) \ _(AsmJSCall) \
_(AsmJSCheckOverRecursed) \ _(AsmJSCheckOverRecursed) \
_(CheckInterruptPar) \ _(InterruptCheckPar) \
_(RecompileCheck) \ _(RecompileCheck) \
_(AssertRangeI) \ _(AssertRangeI) \
_(AssertRangeD) \ _(AssertRangeD) \

View File

@ -2140,10 +2140,10 @@ LIRGenerator::visitInterruptCheck(MInterruptCheck *ins)
} }
bool bool
LIRGenerator::visitCheckInterruptPar(MCheckInterruptPar *ins) LIRGenerator::visitInterruptCheckPar(MInterruptCheckPar *ins)
{ {
LCheckInterruptPar *lir = LInterruptCheckPar *lir =
new(alloc()) LCheckInterruptPar(useRegister(ins->forkJoinContext()), temp()); new(alloc()) LInterruptCheckPar(useRegister(ins->forkJoinContext()), temp());
if (!add(lir, ins)) if (!add(lir, ins))
return false; return false;
if (!assignSafepoint(lir, ins)) if (!assignSafepoint(lir, ins))

View File

@ -164,7 +164,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitForkJoinContext(MForkJoinContext *ins); bool visitForkJoinContext(MForkJoinContext *ins);
bool visitGuardThreadExclusive(MGuardThreadExclusive *ins); bool visitGuardThreadExclusive(MGuardThreadExclusive *ins);
bool visitInterruptCheck(MInterruptCheck *ins); bool visitInterruptCheck(MInterruptCheck *ins);
bool visitCheckInterruptPar(MCheckInterruptPar *ins); bool visitInterruptCheckPar(MInterruptCheckPar *ins);
bool visitStoreSlot(MStoreSlot *ins); bool visitStoreSlot(MStoreSlot *ins);
bool visitTypeBarrier(MTypeBarrier *ins); bool visitTypeBarrier(MTypeBarrier *ins);
bool visitMonitorTypes(MMonitorTypes *ins); bool visitMonitorTypes(MMonitorTypes *ins);

View File

@ -4747,9 +4747,9 @@ class MCheckOverRecursedPar : public MUnaryInstruction
}; };
// Check for an interrupt (or rendezvous) in parallel mode. // Check for an interrupt (or rendezvous) in parallel mode.
class MCheckInterruptPar : public MUnaryInstruction class MInterruptCheckPar : public MUnaryInstruction
{ {
MCheckInterruptPar(MDefinition *cx) MInterruptCheckPar(MDefinition *cx)
: MUnaryInstruction(cx) : MUnaryInstruction(cx)
{ {
setResultType(MIRType_None); setResultType(MIRType_None);
@ -4758,10 +4758,10 @@ class MCheckInterruptPar : public MUnaryInstruction
} }
public: public:
INSTRUCTION_HEADER(CheckInterruptPar); INSTRUCTION_HEADER(InterruptCheckPar);
static MCheckInterruptPar *New(TempAllocator &alloc, MDefinition *cx) { static MInterruptCheckPar *New(TempAllocator &alloc, MDefinition *cx) {
return new(alloc) MCheckInterruptPar(cx); return new(alloc) MInterruptCheckPar(cx);
} }
MDefinition *forkJoinContext() const { MDefinition *forkJoinContext() const {

View File

@ -213,7 +213,7 @@ namespace jit {
_(RestPar) \ _(RestPar) \
_(ForkJoinContext) \ _(ForkJoinContext) \
_(GuardThreadExclusive) \ _(GuardThreadExclusive) \
_(CheckInterruptPar) \ _(InterruptCheckPar) \
_(RecompileCheck) _(RecompileCheck)
// Forward declarations of MIR types. // Forward declarations of MIR types.

View File

@ -208,11 +208,11 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx)
return false; return false;
} }
return CheckInterruptPar(cx); return InterruptCheckPar(cx);
} }
bool bool
jit::CheckInterruptPar(ForkJoinContext *cx) jit::InterruptCheckPar(ForkJoinContext *cx)
{ {
JS_ASSERT(ForkJoinContext::current() == cx); JS_ASSERT(ForkJoinContext::current() == cx);
bool result = cx->check(); bool result = cx->check();

View File

@ -21,7 +21,7 @@ JSObject *NewGCThingPar(ForkJoinContext *cx, gc::AllocKind allocKind);
bool ParallelWriteGuard(ForkJoinContext *cx, JSObject *object); bool ParallelWriteGuard(ForkJoinContext *cx, JSObject *object);
bool IsInTargetRegion(ForkJoinContext *cx, TypedDatum *object); bool IsInTargetRegion(ForkJoinContext *cx, TypedDatum *object);
bool CheckOverRecursedPar(ForkJoinContext *cx); bool CheckOverRecursedPar(ForkJoinContext *cx);
bool CheckInterruptPar(ForkJoinContext *cx); bool InterruptCheckPar(ForkJoinContext *cx);
// Extends the given array with `length` new holes. Returns nullptr on // Extends the given array with `length` new holes. Returns nullptr on
// failure or else `array`, which is convenient during code // failure or else `array`, which is convenient during code

View File

@ -291,7 +291,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
UNSAFE_OP(In) UNSAFE_OP(In)
UNSAFE_OP(InArray) UNSAFE_OP(InArray)
SAFE_OP(GuardThreadExclusive) SAFE_OP(GuardThreadExclusive)
SAFE_OP(CheckInterruptPar) SAFE_OP(InterruptCheckPar)
SAFE_OP(CheckOverRecursedPar) SAFE_OP(CheckOverRecursedPar)
SAFE_OP(FunctionDispatch) SAFE_OP(FunctionDispatch)
SAFE_OP(TypeObjectDispatch) SAFE_OP(TypeObjectDispatch)
@ -746,7 +746,7 @@ ParallelSafetyVisitor::visitCheckOverRecursed(MCheckOverRecursed *ins)
bool bool
ParallelSafetyVisitor::visitInterruptCheck(MInterruptCheck *ins) ParallelSafetyVisitor::visitInterruptCheck(MInterruptCheck *ins)
{ {
return replace(ins, MCheckInterruptPar::New(alloc(), ForkJoinContext())); return replace(ins, MInterruptCheckPar::New(alloc(), ForkJoinContext()));
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////

View File

@ -951,7 +951,7 @@ CodeGeneratorShared::labelForBackedgeWithImplicitCheck(MBasicBlock *mir)
} else { } else {
// The interrupt check should be the first instruction in the // The interrupt check should be the first instruction in the
// loop header other than the initial label and move groups. // loop header other than the initial label and move groups.
JS_ASSERT(iter->isInterruptCheck() || iter->isCheckInterruptPar()); JS_ASSERT(iter->isInterruptCheck() || iter->isInterruptCheckPar());
return nullptr; return nullptr;
} }
} }

View File

@ -1019,6 +1019,10 @@ js_InvokeOperationCallback(JSContext *cx)
js::gc::GCIfNeeded(cx); js::gc::GCIfNeeded(cx);
#ifdef JS_ION #ifdef JS_ION
#ifdef JS_THREADSAFE
rt->interruptPar = false;
#endif
/* /*
* A worker thread may have set the callback after finishing an Ion * A worker thread may have set the callback after finishing an Ion
* compilation. * compilation.

View File

@ -174,7 +174,7 @@ ExecuteSequentially(JSContext *cx, HandleValue funVal)
return false; return false;
args.setCallee(funVal); args.setCallee(funVal);
args.setThis(UndefinedValue()); args.setThis(UndefinedValue());
args[0].setBoolean(!!cx->runtime()->parallelWarmup); args[0].setBoolean(!!cx->runtime()->forkJoinWarmup);
return fig.invoke(cx); return fig.invoke(cx);
} }
@ -389,7 +389,7 @@ class ForkJoinShared : public ParallelJob, public Monitor
void requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason); void requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason);
// Requests that computation abort. // Requests that computation abort.
void setAbortFlag(bool fatal); void setAbortFlagAndTriggerOperationCallback(bool fatal);
// Set the fatal flag for the next abort. // Set the fatal flag for the next abort.
void setPendingAbortFatal() { fatal_ = true; } void setPendingAbortFatal() { fatal_ = true; }
@ -407,8 +407,8 @@ class AutoEnterWarmup
JSRuntime *runtime_; JSRuntime *runtime_;
public: public:
AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->parallelWarmup++; } AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->forkJoinWarmup++; }
~AutoEnterWarmup() { runtime_->parallelWarmup--; } ~AutoEnterWarmup() { runtime_->forkJoinWarmup--; }
}; };
class AutoSetForkJoinContext class AutoSetForkJoinContext
@ -841,18 +841,8 @@ ForkJoinOperation::compileForParallelExecution(ExecutionStatus *status)
} }
} }
if (allScriptsPresent) { if (allScriptsPresent)
// For testing modes, we want to make sure that all off thread
// compilation tasks are finished, so we don't race with
// off-main-thread-compilation setting an interrupt flag while we
// are in the middle of a test, causing unexpected bailouts.
if (mode_ != ForkJoinModeNormal) {
StopAllOffThreadCompilations(cx_->compartment());
if (!js_HandleExecutionInterrupt(cx_))
return fatalError(status);
}
break; break;
}
} }
Spew(SpewCompile, "Compilation complete (final worklist length %d)", Spew(SpewCompile, "Compilation complete (final worklist length %d)",
@ -1406,7 +1396,7 @@ ForkJoinShared::execute()
// Sometimes a GC request occurs *just before* we enter into the // Sometimes a GC request occurs *just before* we enter into the
// parallel section. Rather than enter into the parallel section // parallel section. Rather than enter into the parallel section
// and then abort, we just check here and abort early. // and then abort, we just check here and abort early.
if (cx_->runtime()->interrupt) if (cx_->runtime()->interruptPar)
return TP_RETRY_SEQUENTIALLY; return TP_RETRY_SEQUENTIALLY;
AutoLockMonitor lock(*this); AutoLockMonitor lock(*this);
@ -1464,7 +1454,7 @@ ForkJoinShared::executeFromWorker(uint32_t workerId, uintptr_t stackLimit)
{ {
PerThreadData thisThread(cx_->runtime()); PerThreadData thisThread(cx_->runtime());
if (!thisThread.init()) { if (!thisThread.init()) {
setAbortFlag(true); setAbortFlagAndTriggerOperationCallback(true);
return false; return false;
} }
TlsPerThreadData.set(&thisThread); TlsPerThreadData.set(&thisThread);
@ -1526,7 +1516,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, uint32_t workerId)
// and fallback. // and fallback.
Spew(SpewOps, "Down (Script no longer present)"); Spew(SpewOps, "Down (Script no longer present)");
cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent); cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent);
setAbortFlag(false); setAbortFlagAndTriggerOperationCallback(false);
} else { } else {
ParallelIonInvoke<2> fii(cx_->runtime(), fun_, 1); ParallelIonInvoke<2> fii(cx_->runtime(), fun_, 1);
@ -1535,7 +1525,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, uint32_t workerId)
bool ok = fii.invoke(perThread); bool ok = fii.invoke(perThread);
JS_ASSERT(ok == !cx.bailoutRecord->topScript); JS_ASSERT(ok == !cx.bailoutRecord->topScript);
if (!ok) if (!ok)
setAbortFlag(false); setAbortFlagAndTriggerOperationCallback(false);
} }
Spew(SpewOps, "Down"); Spew(SpewOps, "Down");
@ -1544,7 +1534,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, uint32_t workerId)
bool bool
ForkJoinShared::check(ForkJoinContext &cx) ForkJoinShared::check(ForkJoinContext &cx)
{ {
JS_ASSERT(cx_->runtime()->interrupt); JS_ASSERT(cx_->runtime()->interruptPar);
if (abort_) if (abort_)
return false; return false;
@ -1555,14 +1545,14 @@ ForkJoinShared::check(ForkJoinContext &cx)
if (cx.isMainThread() || !threadPool_->isMainThreadActive()) { if (cx.isMainThread() || !threadPool_->isMainThreadActive()) {
JS_ASSERT(!cx_->runtime()->gcIsNeeded); JS_ASSERT(!cx_->runtime()->gcIsNeeded);
if (cx_->runtime()->interrupt) { if (cx_->runtime()->interruptPar) {
// The GC Needed flag should not be set during parallel // The GC Needed flag should not be set during parallel
// execution. Instead, one of the requestGC() or // execution. Instead, one of the requestGC() or
// requestZoneGC() methods should be invoked. // requestZoneGC() methods should be invoked.
JS_ASSERT(!cx_->runtime()->gcIsNeeded); JS_ASSERT(!cx_->runtime()->gcIsNeeded);
cx.bailoutRecord->setCause(ParallelBailoutInterrupt); cx.bailoutRecord->setCause(ParallelBailoutInterrupt);
setAbortFlag(false); setAbortFlagAndTriggerOperationCallback(false);
return false; return false;
} }
} }
@ -1571,16 +1561,16 @@ ForkJoinShared::check(ForkJoinContext &cx)
} }
void void
ForkJoinShared::setAbortFlag(bool fatal) ForkJoinShared::setAbortFlagAndTriggerOperationCallback(bool fatal)
{ {
AutoLockMonitor lock(*this); AutoLockMonitor lock(*this);
abort_ = true; abort_ = true;
fatal_ = fatal_ || fatal; fatal_ = fatal_ || fatal;
// Note: DontStopIon here avoids the expensive memory protection needed to // Note: The ForkJoin trigger here avoids the expensive memory protection needed to
// interrupt Ion code compiled for sequential execution. // interrupt Ion code compiled for sequential execution.
cx_->runtime()->triggerOperationCallback(JSRuntime::TriggerCallbackAnyThreadDontStopIon); cx_->runtime()->triggerOperationCallback(JSRuntime::TriggerCallbackAnyThreadForkJoin);
} }
void void
@ -1681,7 +1671,7 @@ ForkJoinContext::hasAcquiredJSContext() const
bool bool
ForkJoinContext::check() ForkJoinContext::check()
{ {
if (runtime()->interrupt) if (runtime()->interruptPar)
return shared->check(*this); return shared->check(*this);
else else
return true; return true;
@ -1692,7 +1682,7 @@ ForkJoinContext::requestGC(JS::gcreason::Reason reason)
{ {
shared->requestGC(reason); shared->requestGC(reason);
bailoutRecord->setCause(ParallelBailoutRequestedGC); bailoutRecord->setCause(ParallelBailoutRequestedGC);
shared->setAbortFlag(false); shared->setAbortFlagAndTriggerOperationCallback(false);
} }
void void
@ -1700,7 +1690,7 @@ ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
{ {
shared->requestZoneGC(zone, reason); shared->requestZoneGC(zone, reason);
bailoutRecord->setCause(ParallelBailoutRequestedZoneGC); bailoutRecord->setCause(ParallelBailoutRequestedZoneGC);
shared->setAbortFlag(false); shared->setAbortFlagAndTriggerOperationCallback(false);
} }
bool bool
@ -2145,6 +2135,14 @@ js::ParallelTestsShouldPass(JSContext *cx)
cx->runtime()->gcZeal() == 0; cx->runtime()->gcZeal() == 0;
} }
void
js::TriggerOperationCallbackForForkJoin(JSRuntime *rt,
JSRuntime::OperationCallbackTrigger trigger)
{
if (trigger != JSRuntime::TriggerCallbackAnyThreadDontStopIon)
rt->interruptPar = true;
}
bool bool
js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp) js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
{ {

View File

@ -465,6 +465,9 @@ bool InExclusiveParallelSection();
bool ParallelTestsShouldPass(JSContext *cx); bool ParallelTestsShouldPass(JSContext *cx);
void TriggerOperationCallbackForForkJoin(JSRuntime *rt,
JSRuntime::OperationCallbackTrigger trigger);
bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo; extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo;

View File

@ -120,6 +120,9 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
), ),
mainThread(this), mainThread(this),
interrupt(0), interrupt(0),
#if defined(JS_THREADSAFE) && defined(JS_ION)
interruptPar(false),
#endif
handlingSignal(false), handlingSignal(false),
operationCallback(nullptr), operationCallback(nullptr),
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
@ -293,7 +296,7 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
threadPool(this), threadPool(this),
defaultJSContextCallback(nullptr), defaultJSContextCallback(nullptr),
ctypesActivityCallback(nullptr), ctypesActivityCallback(nullptr),
parallelWarmup(0), forkJoinWarmup(0),
ionReturnOverride_(MagicValue(JS_ARG_POISON)), ionReturnOverride_(MagicValue(JS_ARG_POISON)),
useHelperThreads_(useHelperThreads), useHelperThreads_(useHelperThreads),
parallelIonCompilationEnabled_(true), parallelIonCompilationEnabled_(true),
@ -654,6 +657,10 @@ JSRuntime::triggerOperationCallback(OperationCallbackTrigger trigger)
interrupt = 1; interrupt = 1;
#ifdef JS_ION #ifdef JS_ION
#ifdef JS_THREADSAFE
TriggerOperationCallbackForForkJoin(this, trigger);
#endif
/* /*
* asm.js and, optionally, normal Ion code use memory protection and signal * asm.js and, optionally, normal Ion code use memory protection and signal
* handlers to halt running code. * handlers to halt running code.

View File

@ -689,6 +689,15 @@ struct JSRuntime : public JS::shadow::Runtime,
int32_t interrupt; int32_t interrupt;
#endif #endif
#if defined(JS_THREADSAFE) && defined(JS_ION)
/*
* If non-zero, ForkJoin should service an interrupt. This is a separate
* flag from |interrupt| because we cannot use the mprotect trick with PJS
* code and ignore the TriggerCallbackAnyThreadDontStopIon trigger.
*/
mozilla::Atomic<bool, mozilla::Relaxed> interruptPar;
#endif
/* Set when handling a signal for a thread associated with this runtime. */ /* Set when handling a signal for a thread associated with this runtime. */
bool handlingSignal; bool handlingSignal;
@ -1627,9 +1636,9 @@ struct JSRuntime : public JS::shadow::Runtime,
js::CTypesActivityCallback ctypesActivityCallback; js::CTypesActivityCallback ctypesActivityCallback;
// Non-zero if this is a parallel warmup execution. See // Non-zero if this is a ForkJoin warmup execution. See
// js::parallel::Do() for more information. // js::ForkJoin() for more information.
uint32_t parallelWarmup; uint32_t forkJoinWarmup;
private: private:
// In certain cases, we want to optimize certain opcodes to typed instructions, // In certain cases, we want to optimize certain opcodes to typed instructions,
@ -1722,7 +1731,8 @@ struct JSRuntime : public JS::shadow::Runtime,
enum OperationCallbackTrigger { enum OperationCallbackTrigger {
TriggerCallbackMainThread, TriggerCallbackMainThread,
TriggerCallbackAnyThread, TriggerCallbackAnyThread,
TriggerCallbackAnyThreadDontStopIon TriggerCallbackAnyThreadDontStopIon,
TriggerCallbackAnyThreadForkJoin
}; };
void triggerOperationCallback(OperationCallbackTrigger trigger); void triggerOperationCallback(OperationCallbackTrigger trigger);

View File

@ -605,7 +605,7 @@ js::intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
args.rval().setBoolean(cx->runtime()->parallelWarmup || args.rval().setBoolean(cx->runtime()->forkJoinWarmup ||
InParallelSection()); InParallelSection());
#else #else
args.rval().setBoolean(true); args.rval().setBoolean(true);