Backed out changeset 10616214c160 (bug 1091916) for Android x86 S4 permafail.

This commit is contained in:
Ryan VanderMeulen 2014-10-31 18:58:42 -04:00
parent 64db9b1b38
commit 776ece8f48
22 changed files with 290 additions and 239 deletions

View File

@ -500,7 +500,7 @@ AsmJSHandleExecutionInterrupt()
{
AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
act->module().setInterrupted(true);
bool ret = CheckForInterrupt(act->cx());
bool ret = HandleExecutionInterrupt(act->cx());
act->module().setInterrupted(false);
return ret;
}
@ -672,8 +672,8 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext *cx)
switch (kind) {
case AsmJSImm_Runtime:
return cx->runtimeAddressForJit();
case AsmJSImm_RuntimeInterruptUint32:
return cx->runtimeAddressOfInterruptUint32();
case AsmJSImm_RuntimeInterrupt:
return cx->runtimeAddressOfInterrupt();
case AsmJSImm_StackLimit:
return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
case AsmJSImm_ReportOverRecursed:

View File

@ -152,7 +152,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext *cx, bool match_only)
// Check if we have space on the stack.
Label stack_ok;
void *stack_limit = runtime->mainThread.addressofJitStackLimit();
void *stack_limit = &runtime->mainThread.jitStackLimit;
masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok);
// Exit with an exception. There is not enough space on the stack
@ -502,7 +502,7 @@ NativeRegExpMacroAssembler::Backtrack()
// Check for an interrupt.
Label noInterrupt;
masm.branch32(Assembler::Equal,
AbsoluteAddress(runtime->addressOfInterruptUint32()), Imm32(0),
AbsoluteAddress(&runtime->interrupt), Imm32(0),
&noInterrupt);
masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
masm.jump(&exit_label_);

View File

@ -496,7 +496,7 @@ bool
BaselineCompiler::emitStackCheck(bool earlyCheck)
{
Label skipCall;
void *limitAddr = cx->runtime()->mainThread.addressofJitStackLimit();
uintptr_t *limitAddr = &cx->runtime()->mainThread.jitStackLimit;
uint32_t slotsSize = script->nslots() * sizeof(Value);
uint32_t tolerance = earlyCheck ? slotsSize : 0;
@ -646,7 +646,7 @@ BaselineCompiler::emitInterruptCheck()
frame.syncStack(0);
Label done;
void *interrupt = cx->runtimeAddressOfInterruptUint32();
void *interrupt = (void *)&cx->runtime()->interrupt;
masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done);
prepareVMCall();

View File

@ -3747,7 +3747,7 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
Register tempReg = ToRegister(lir->getTempReg());
masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
masm.loadPtr(Address(tempReg, PerThreadData::offsetOfJitStackLimit()), tempReg);
masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg);
// Conditional forward (unlikely) branch to failure.
CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
@ -9789,7 +9789,7 @@ CodeGenerator::visitInterruptCheck(LInterruptCheck *lir)
if (!ool)
return false;
AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterruptUint32());
AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterrupt());
masm.branch32(Assembler::NotEqual, interruptAddr, Imm32(0), ool->entry());
masm.bind(ool->rejoin());
return true;
@ -9799,8 +9799,8 @@ bool
CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir)
{
Register scratch = ToRegister(lir->scratch());
masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterruptUint32), scratch);
masm.load32(Address(scratch, 0), scratch);
masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterrupt), scratch);
masm.load8ZeroExtend(Address(scratch, 0), scratch);
Label rejoin;
masm.branch32(Assembler::Equal, scratch, Imm32(0), &rejoin);
{

View File

@ -43,7 +43,7 @@ CompileRuntime::addressOfJitTop()
const void *
CompileRuntime::addressOfJitStackLimit()
{
return runtime()->mainThread.addressofJitStackLimit();
return &runtime()->mainThread.jitStackLimit;
}
const void *
@ -73,15 +73,15 @@ CompileRuntime::addressOfGCZeal()
#endif
const void *
CompileRuntime::addressOfInterruptUint32()
CompileRuntime::addressOfInterrupt()
{
return runtime()->addressOfInterruptUint32();
return &runtime()->interrupt;
}
const void *
CompileRuntime::addressOfInterruptParUint32()
CompileRuntime::addressOfInterruptPar()
{
return runtime()->addressOfInterruptParUint32();
return &runtime()->interruptPar;
}
const void *

View File

@ -50,8 +50,8 @@ class CompileRuntime
const void *addressOfGCZeal();
#endif
const void *addressOfInterruptUint32();
const void *addressOfInterruptParUint32();
const void *addressOfInterrupt();
const void *addressOfInterruptPar();
const void *addressOfThreadPool();

View File

@ -423,7 +423,7 @@ JitRuntime::ensureIonCodeAccessible(JSRuntime *rt)
ionCodeProtected_ = false;
}
if (rt->hasPendingInterrupt()) {
if (rt->interrupt) {
// The interrupt handler needs to be invoked by this thread, but we may
// be inside a signal handler and have no idea what is above us on the
// stack (probably we are executing Ion code at an arbitrary point, but
@ -1157,7 +1157,7 @@ IonScript::copyPatchableBackedges(JSContext *cx, JitCode *code,
// whether an interrupt is currently desired, matching the targets
// established by ensureIonCodeAccessible() above. We don't handle the
// interrupt immediately as the interrupt lock is held here.
if (cx->runtime()->hasPendingInterrupt())
if (cx->runtime()->interrupt)
PatchBackedge(backedge, interruptCheck, JitRuntime::BackedgeInterruptCheck);
else
PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader);

View File

@ -1168,7 +1168,7 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output)
void
MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail)
{
movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptParUint32()), tempReg);
movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptPar()), tempReg);
branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail);
}

View File

@ -147,7 +147,7 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx)
}
#endif
if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit(), &stackDummy_)) {
if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit, &stackDummy_)) {
cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
return false;
}

View File

@ -113,17 +113,28 @@ NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap)
bool
CheckOverRecursed(JSContext *cx)
{
// We just failed the jitStackLimit check. There are two possible reasons:
// - jitStackLimit was the real stack limit and we're over-recursed
// - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt
// and we need to call JSRuntime::handleInterrupt.
// IonMonkey's stackLimit is equal to nativeStackLimit by default. When we
// request an interrupt, we set the jitStackLimit to nullptr, which causes
// the stack limit check to fail.
//
// There are two states we're concerned about here:
// (1) The interrupt bit is set, and we need to fire the interrupt callback.
// (2) The stack limit has been exceeded, and we need to throw an error.
//
// Note that we can reach here if jitStackLimit is MAXADDR, but interrupt
// has not yet been set to 1. That's okay; it will be set to 1 very shortly,
// and in the interim we might just fire a few useless calls to
// CheckOverRecursed.
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false);
#else
JS_CHECK_RECURSION(cx, return false);
#endif
gc::MaybeVerifyBarriers(cx);
return cx->runtime()->handleInterrupt(cx);
if (cx->runtime()->interrupt)
return InterruptCheck(cx);
return true;
}
// This function can get called in two contexts. In the usual context, it's
@ -167,8 +178,10 @@ CheckOverRecursedWithExtra(JSContext *cx, BaselineFrame *frame,
JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false);
#endif
gc::MaybeVerifyBarriers(cx);
return cx->runtime()->handleInterrupt(cx);
if (cx->runtime()->interrupt)
return InterruptCheck(cx);
return true;
}
bool

View File

@ -783,7 +783,7 @@ enum AsmJSImmKind
AsmJSImm_PowD = AsmJSExit::Builtin_PowD,
AsmJSImm_ATan2D = AsmJSExit::Builtin_ATan2D,
AsmJSImm_Runtime,
AsmJSImm_RuntimeInterruptUint32,
AsmJSImm_RuntimeInterrupt,
AsmJSImm_StackLimit,
AsmJSImm_ReportOverRecursed,
AsmJSImm_OnDetached,

View File

@ -2029,48 +2029,68 @@ JS_GetExternalStringFinalizer(JSString *str)
}
static void
SetNativeStackQuotaAndLimit(JSRuntime *rt, StackKind kind, size_t stackSize)
SetNativeStackQuota(JSRuntime *rt, StackKind kind, size_t stackSize)
{
rt->nativeStackQuota[kind] = stackSize;
if (rt->nativeStackBase)
RecomputeStackLimit(rt, kind);
}
void
js::RecomputeStackLimit(JSRuntime *rt, StackKind kind)
{
size_t stackSize = rt->nativeStackQuota[kind];
#if JS_STACK_GROWTH_DIRECTION > 0
if (stackSize == 0) {
rt->mainThread.nativeStackLimit[kind] = UINTPTR_MAX;
} else {
MOZ_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize);
rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase + stackSize - 1;
rt->mainThread.nativeStackLimit[kind] =
rt->nativeStackBase + stackSize - 1;
}
#else
if (stackSize == 0) {
rt->mainThread.nativeStackLimit[kind] = 0;
} else {
MOZ_ASSERT(rt->nativeStackBase >= stackSize);
rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase - (stackSize - 1);
rt->mainThread.nativeStackLimit[kind] =
rt->nativeStackBase - (stackSize - 1);
}
#endif
// If there's no pending interrupt request set on the runtime's main thread's
// jitStackLimit, then update it so that it reflects the new nativeStacklimit.
//
// Note that, for now, we use the untrusted limit for ion. This is fine,
// because it's the most conservative limit, and if we hit it, we'll bail
// out of ion into the interpeter, which will do a proper recursion check.
if (kind == StackForUntrustedScript) {
JSRuntime::AutoLockForInterrupt lock(rt);
if (rt->mainThread.jitStackLimit != uintptr_t(-1)) {
rt->mainThread.jitStackLimit = rt->mainThread.nativeStackLimit[kind];
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
rt->mainThread.jitStackLimit = jit::Simulator::StackLimit();
#endif
}
}
}
JS_PUBLIC_API(void)
JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize, size_t trustedScriptStackSize,
JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize,
size_t trustedScriptStackSize,
size_t untrustedScriptStackSize)
{
MOZ_ASSERT(rt->requestDepth == 0);
MOZ_ASSERT_IF(trustedScriptStackSize,
trustedScriptStackSize < systemCodeStackSize);
if (!trustedScriptStackSize)
trustedScriptStackSize = systemCodeStackSize;
else
MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize);
MOZ_ASSERT_IF(untrustedScriptStackSize,
untrustedScriptStackSize < trustedScriptStackSize);
if (!untrustedScriptStackSize)
untrustedScriptStackSize = trustedScriptStackSize;
else
MOZ_ASSERT(untrustedScriptStackSize < trustedScriptStackSize);
SetNativeStackQuotaAndLimit(rt, StackForSystemCode, systemCodeStackSize);
SetNativeStackQuotaAndLimit(rt, StackForTrustedScript, trustedScriptStackSize);
SetNativeStackQuotaAndLimit(rt, StackForUntrustedScript, untrustedScriptStackSize);
rt->mainThread.initJitStackLimit();
SetNativeStackQuota(rt, StackForSystemCode, systemCodeStackSize);
SetNativeStackQuota(rt, StackForTrustedScript, trustedScriptStackSize);
SetNativeStackQuota(rt, StackForUntrustedScript, untrustedScriptStackSize);
}
/************************************************************************/

View File

@ -2272,9 +2272,6 @@ JS_GetExternalStringFinalizer(JSString *str);
* The stack quotas for each kind of code should be monotonically descending,
* and may be specified with this function. If 0 is passed for a given kind
* of code, it defaults to the value of the next-highest-priority kind.
*
* This function may only be called immediately after the runtime is initialized
* and before any code is executed and/or interrupts requested.
*/
extern JS_PUBLIC_API(void)
JS_SetNativeStackQuota(JSRuntime *cx, size_t systemCodeStackSize,

View File

@ -41,6 +41,7 @@
#include "gc/Marking.h"
#include "jit/Ion.h"
#include "js/CharacterEncoding.h"
#include "vm/Debugger.h"
#include "vm/HelperThreads.h"
#include "vm/Shape.h"
@ -970,6 +971,90 @@ js_GetErrorMessage(void *userRef, const unsigned errorNumber)
return nullptr;
}
bool
js::InvokeInterruptCallback(JSContext *cx)
{
MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
JSRuntime *rt = cx->runtime();
MOZ_ASSERT(rt->interrupt);
// Reset the callback counter first, then run GC and yield. If another
// thread is racing us here we will accumulate another callback request
// which will be serviced at the next opportunity.
rt->interrupt = false;
// IonMonkey sets its stack limit to UINTPTR_MAX to trigger interrupt
// callbacks.
rt->resetJitStackLimit();
cx->gcIfNeeded();
rt->interruptPar = false;
// A worker thread may have requested an interrupt after finishing an Ion
// compilation.
jit::AttachFinishedCompilations(cx);
// Important: Additional callbacks can occur inside the callback handler
// if it re-enters the JS engine. The embedding must ensure that the
// callback is disconnected before attempting such re-entry.
JSInterruptCallback cb = cx->runtime()->interruptCallback;
if (!cb)
return true;
if (cb(cx)) {
// Debugger treats invoking the interrupt callback as a "step", so
// invoke the onStep handler.
if (cx->compartment()->debugMode()) {
ScriptFrameIter iter(cx);
if (iter.script()->stepModeEnabled()) {
RootedValue rval(cx);
switch (Debugger::onSingleStep(cx, &rval)) {
case JSTRAP_ERROR:
return false;
case JSTRAP_CONTINUE:
return true;
case JSTRAP_RETURN:
// See note in Debugger::propagateForcedReturn.
Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
return false;
case JSTRAP_THROW:
cx->setPendingException(rval);
return false;
default:;
}
}
}
return true;
}
// No need to set aside any pending exception here: ComputeStackString
// already does that.
JSString *stack = ComputeStackString(cx);
JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr;
const char16_t *chars;
AutoStableStringChars stableChars(cx);
if (flat && stableChars.initTwoByte(cx, flat))
chars = stableChars.twoByteRange().start().get();
else
chars = MOZ_UTF16("(stack not available)");
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
JSMSG_TERMINATED, chars);
return false;
}
bool
js::HandleExecutionInterrupt(JSContext *cx)
{
if (cx->runtime()->interrupt)
return InvokeInterruptCallback(cx);
return true;
}
ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
: ContextFriendFields(rt),
contextKind_(kind),

View File

@ -289,7 +289,7 @@ struct ThreadSafeContext : ContextFriendFields,
PropertyName *emptyString() { return runtime_->emptyString; }
FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
void *runtimeAddressForJit() { return runtime_; }
void *runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
void *runtimeAddressOfInterrupt() { return &runtime_->interrupt; }
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
void *stackLimitAddressForJitCode(StackKind kind);
size_t gcSystemPageSize() { return gc::SystemPageSize(); }
@ -782,15 +782,33 @@ extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
namespace js {
/*
* Invoke the interrupt callback and return false if the current execution
* is to be terminated.
*/
bool
InvokeInterruptCallback(JSContext *cx);
bool
HandleExecutionInterrupt(JSContext *cx);
/*
* Process any pending interrupt requests. Long-running inner loops in C++ must
* call this periodically to make sure they are interruptible --- that is, to
* make sure they do not prevent the slow script dialog from appearing.
*
* This can run a full GC or call the interrupt callback, which could do
* anything. In the browser, it displays the slow script dialog.
*
* If this returns true, the caller can continue; if false, the caller must
* break out of its loop. This happens if, for example, the user clicks "Stop
* script" on the slow script dialog; treat it as an uncatchable error.
*/
MOZ_ALWAYS_INLINE bool
CheckForInterrupt(JSContext *cx)
{
// Add an inline fast-path since we have to check for interrupts in some hot
// C++ loops of library builtins.
JSRuntime *rt = cx->runtime();
if (rt->hasPendingInterrupt())
return rt->handleInterrupt(cx);
return true;
MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
return !cx->runtime()->interrupt || InvokeInterruptCallback(cx);
}
/************************************************************************/

View File

@ -526,7 +526,7 @@ CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind)
rt->gc.runDebugGC();
#endif
if (rt->hasPendingInterrupt()) {
if (rt->interrupt) {
// Invoking the interrupt callback can fail and we can't usefully
// handle that here. Just check in case we need to collect instead.
ncx->gcIfNeeded();

View File

@ -18,7 +18,6 @@ inline uintptr_t
GetNativeStackBase()
{
uintptr_t stackBase = reinterpret_cast<uintptr_t>(GetNativeStackBaseImpl());
MOZ_ASSERT(stackBase != 0);
MOZ_ASSERT(stackBase % sizeof(void *) == 0);
return stackBase;
}

View File

@ -1440,7 +1440,7 @@ ForkJoinShared::execute()
// Sometimes a GC request occurs *just before* we enter into the
// parallel section. Rather than enter into the parallel section
// and then abort, we just check here and abort early.
if (cx_->runtime()->hasPendingInterruptPar())
if (cx_->runtime()->interruptPar)
return TP_RETRY_SEQUENTIALLY;
AutoLockMonitor lock(*this);
@ -1518,7 +1518,7 @@ ForkJoinShared::executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit
// Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the
// lock has not been initialized in these cases.
thisThread.initJitStackLimitPar(stackLimit);
thisThread.jitStackLimit = stackLimit;
executePortion(&thisThread, worker);
TlsPerThreadData.set(nullptr);
@ -1551,7 +1551,7 @@ ForkJoinShared::executeFromMainThread(ThreadPoolWorker *worker)
//
// Thus, use GetNativeStackLimit instead of just propagating the
// main thread's.
thisThread.initJitStackLimitPar(GetNativeStackLimit(cx_));
thisThread.jitStackLimit = GetNativeStackLimit(cx_);
executePortion(&thisThread, worker);
TlsPerThreadData.set(oldData);
@ -1647,7 +1647,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
void
ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx)
{
MOZ_ASSERT(cx_->runtime()->hasPendingInterruptPar());
MOZ_ASSERT(cx_->runtime()->interruptPar);
// The GC Needed flag should not be set during parallel
// execution. Instead, one of the requestGC() or
// requestZoneGC() methods should be invoked.
@ -1826,7 +1826,7 @@ ForkJoinContext::hasAcquiredJSContext() const
bool
ForkJoinContext::check()
{
if (runtime()->hasPendingInterruptPar()) {
if (runtime()->interruptPar) {
shared_->setAbortFlagDueToInterrupt(*this);
return false;
}
@ -2273,6 +2273,13 @@ js::ParallelTestsShouldPass(JSContext *cx)
cx->runtime()->gcZeal() == 0;
}
void
js::RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode)
{
if (mode != JSRuntime::RequestInterruptAnyThreadDontStopIon)
rt->interruptPar = true;
}
bool
js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
{

View File

@ -546,6 +546,8 @@ bool InExclusiveParallelSection();
bool ParallelTestsShouldPass(JSContext *cx);
void RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode);
bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo;

View File

@ -621,8 +621,14 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start,
// in the bytecode interpreter, which can execute while tolerating
// future interrupts. Otherwise, if we keep getting interrupted we
// will never finish executing the regexp.
if (cx->runtime()->hasPendingInterrupt()) {
if (!cx->runtime()->handleInterrupt(cx))
bool interrupted;
{
JSRuntime::AutoLockForInterrupt lock(cx->runtime());
interrupted = cx->runtime()->interrupt;
}
if (interrupted) {
if (!InvokeInterruptCallback(cx))
return RegExpRunStatus_Error;
break;
}

View File

@ -36,7 +36,6 @@
#include "jit/PcScriptCache.h"
#include "js/MemoryMetrics.h"
#include "js/SliceBudget.h"
#include "vm/Debugger.h"
#include "jscntxtinlines.h"
#include "jsgcinlines.h"
@ -74,7 +73,7 @@ PerThreadData::PerThreadData(JSRuntime *runtime)
runtime_(runtime),
jitTop(nullptr),
jitJSContext(nullptr),
jitStackLimit_(0xbad),
jitStackLimit(0),
#ifdef JS_TRACE_LOGGING
traceLogger(nullptr),
#endif
@ -136,8 +135,8 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
),
mainThread(this),
parentRuntime(parentRuntime),
interrupt_(false),
interruptPar_(false),
interrupt(false),
interruptPar(false),
handlingSignal(false),
interruptCallback(nullptr),
interruptLock(nullptr),
@ -157,7 +156,7 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
execAlloc_(nullptr),
jitRuntime_(nullptr),
selfHostingGlobal_(nullptr),
nativeStackBase(GetNativeStackBase()),
nativeStackBase(0),
cxCallback(nullptr),
destroyCompartmentCallback(nullptr),
destroyZoneCallback(nullptr),
@ -323,6 +322,8 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
return false;
#endif
nativeStackBase = GetNativeStackBase();
jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
jitSupportsSimd = js::jit::JitSupportsSimd();
@ -464,6 +465,17 @@ NewObjectCache::clearNurseryObjects(JSRuntime *rt)
#endif
}
void
JSRuntime::resetJitStackLimit()
{
AutoLockForInterrupt lock(this);
mainThread.setJitStackLimit(mainThread.nativeStackLimit[js::StackForUntrustedScript]);
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
mainThread.setJitStackLimit(js::jit::Simulator::StackLimit());
#endif
}
void
JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes)
{
@ -518,120 +530,33 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim
#endif
}
static bool
InvokeInterruptCallback(JSContext *cx)
{
MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
cx->gcIfNeeded();
// A worker thread may have requested an interrupt after finishing an Ion
// compilation.
jit::AttachFinishedCompilations(cx);
// Important: Additional callbacks can occur inside the callback handler
// if it re-enters the JS engine. The embedding must ensure that the
// callback is disconnected before attempting such re-entry.
JSInterruptCallback cb = cx->runtime()->interruptCallback;
if (!cb)
return true;
if (cb(cx)) {
// Debugger treats invoking the interrupt callback as a "step", so
// invoke the onStep handler.
if (cx->compartment()->debugMode()) {
ScriptFrameIter iter(cx);
if (iter.script()->stepModeEnabled()) {
RootedValue rval(cx);
switch (Debugger::onSingleStep(cx, &rval)) {
case JSTRAP_ERROR:
return false;
case JSTRAP_CONTINUE:
return true;
case JSTRAP_RETURN:
// See note in Debugger::propagateForcedReturn.
Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
return false;
case JSTRAP_THROW:
cx->setPendingException(rval);
return false;
default:;
}
}
}
return true;
}
// No need to set aside any pending exception here: ComputeStackString
// already does that.
JSString *stack = ComputeStackString(cx);
JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr;
const char16_t *chars;
AutoStableStringChars stableChars(cx);
if (flat && stableChars.initTwoByte(cx, flat))
chars = stableChars.twoByteRange().start().get();
else
chars = MOZ_UTF16("(stack not available)");
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
JSMSG_TERMINATED, chars);
return false;
}
void
PerThreadData::resetJitStackLimit()
{
// Note that, for now, we use the untrusted limit for ion. This is fine,
// because it's the most conservative limit, and if we hit it, we'll bail
// out of ion into the interpeter, which will do a proper recursion check.
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
jitStackLimit_ = jit::Simulator::StackLimit();
#else
jitStackLimit_ = nativeStackLimit[StackForUntrustedScript];
#endif
}
void
PerThreadData::initJitStackLimit()
{
resetJitStackLimit();
}
void
PerThreadData::initJitStackLimitPar(uintptr_t limit)
{
jitStackLimit_ = limit;
}
void
JSRuntime::requestInterrupt(InterruptMode mode)
{
interrupt_ = true;
interruptPar_ = true;
mainThread.jitStackLimit_ = UINTPTR_MAX;
AutoLockForInterrupt lock(this);
/*
* Invalidate ionTop to trigger its over-recursion check. Note this must be
* set before interrupt, to avoid racing with js::InvokeInterruptCallback,
* into a weird state where interrupt is stuck at 0 but jitStackLimit is
* MAXADDR.
*/
mainThread.setJitStackLimit(-1);
interrupt = true;
RequestInterruptForForkJoin(this, mode);
/*
* asm.js and normal Ion code optionally use memory protection and signal
* handlers to halt running code.
*/
if (canUseSignalHandlers()) {
AutoLockForInterrupt lock(this);
RequestInterruptForAsmJSCode(this, mode);
jit::RequestInterruptForIonCode(this, mode);
}
}
bool
JSRuntime::handleInterrupt(JSContext *cx)
{
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
if (interrupt_ || mainThread.jitStackLimit_ == UINTPTR_MAX) {
interrupt_ = false;
interruptPar_ = false;
mainThread.resetJitStackLimit();
return InvokeInterruptCallback(cx);
}
return true;
}
jit::ExecutableAllocator *
JSRuntime::createExecutableAllocator(JSContext *cx)
{

View File

@ -519,20 +519,13 @@ class PerThreadData : public PerThreadDataFriendFields
*/
JSContext *jitJSContext;
/* See comment for JSRuntime::interrupt_. */
private:
mozilla::Atomic<uintptr_t, mozilla::Relaxed> jitStackLimit_;
void resetJitStackLimit();
friend struct ::JSRuntime;
public:
void initJitStackLimit();
void initJitStackLimitPar(uintptr_t limit);
/*
* The stack limit checked by JIT code. This stack limit may be temporarily
* set to null to force JIT code to exit (e.g., for the operation callback).
*/
uintptr_t jitStackLimit;
uintptr_t jitStackLimit() const { return jitStackLimit_; }
// For read-only JIT use:
void *addressofJitStackLimit() { return &jitStackLimit_; }
static size_t offsetOfJitStackLimit() { return offsetof(PerThreadData, jitStackLimit_); }
inline void setJitStackLimit(uintptr_t limit);
// Information about the heap allocated backtrack stack used by RegExp JIT code.
irregexp::RegExpStack regexpStack;
@ -685,6 +678,8 @@ class PerThreadData : public PerThreadDataFriendFields
class AutoLockForExclusiveAccess;
void RecomputeStackLimit(JSRuntime *rt, StackKind kind);
} // namespace js
struct JSRuntime : public JS::shadow::Runtime,
@ -708,56 +703,18 @@ struct JSRuntime : public JS::shadow::Runtime,
*/
JSRuntime *parentRuntime;
private:
mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptPar_;
public:
/*
* If true, we've been asked to call the interrupt callback as soon as
* possible.
*/
mozilla::Atomic<bool, mozilla::Relaxed> interrupt;
enum InterruptMode {
RequestInterruptMainThread,
RequestInterruptAnyThread,
RequestInterruptAnyThreadDontStopIon,
RequestInterruptAnyThreadForkJoin
};
// Any thread can call requestInterrupt() to request that the main JS thread
// stop running and call the interrupt callback (allowing the interrupt
// callback to halt execution). To stop the main JS thread, requestInterrupt
// sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to
// UINTPTR_MAX). The JS engine must continually poll one of these fields
// and call handleInterrupt if either field has the interrupt value. (The
// point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already
// needs to guard on jitStackLimit_ in every function prologue to avoid
// stack overflow, so we avoid a second branch on interrupt_ by setting
// jitStackLimit_ to a value that is guaranteed to fail the guard.)
//
// Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed
// Atomic so, while the writes are guaranteed to eventually be visible to
// the main thread, it can happen in any order. handleInterrupt calls the
// interrupt callback if either is set, so it really doesn't matter as long
// as the JS engine is continually polling at least one field. In corner
// cases, this relaxed ordering could lead to an interrupt handler being
// called twice in succession after a single requestInterrupt call, but
// that's fine.
void requestInterrupt(InterruptMode mode);
bool handleInterrupt(JSContext *cx);
MOZ_ALWAYS_INLINE bool hasPendingInterrupt() const {
return interrupt_;
}
MOZ_ALWAYS_INLINE bool hasPendingInterruptPar() const {
return interruptPar_;
}
// For read-only JIT use:
void *addressOfInterruptUint32() {
static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers");
return &interrupt_;
}
void *addressOfInterruptParUint32() {
static_assert(sizeof(interruptPar_) == sizeof(uint32_t), "Assumed by JIT callers");
return &interruptPar_;
}
/*
* 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;
/* Set when handling a signal for a thread associated with this runtime. */
bool handlingSignal;
@ -949,7 +906,7 @@ struct JSRuntime : public JS::shadow::Runtime,
void setDefaultVersion(JSVersion v) { defaultVersion_ = v; }
/* Base address of the native stack for the current thread. */
const uintptr_t nativeStackBase;
uintptr_t nativeStackBase;
/* The native stack size limit that runtime should not exceed. */
size_t nativeStackQuota[js::StackKindCount];
@ -1315,6 +1272,10 @@ struct JSRuntime : public JS::shadow::Runtime,
bool jitSupportsFloatingPoint;
bool jitSupportsSimd;
// Used to reset stack limit after a signaled interrupt (i.e. jitStackLimit_ = -1)
// has been noticed by Ion/Baseline.
void resetJitStackLimit();
// Cache for jit::GetPcScript().
js::jit::PcScriptCache *ionPcScriptCache;
@ -1375,6 +1336,17 @@ struct JSRuntime : public JS::shadow::Runtime,
/* onOutOfMemory but can call the largeAllocationFailureCallback. */
JS_FRIEND_API(void *) onOutOfMemoryCanGC(void *p, size_t bytes);
// Ways in which the interrupt callback on the runtime can be triggered,
// varying based on which thread is triggering the callback.
enum InterruptMode {
RequestInterruptMainThread,
RequestInterruptAnyThread,
RequestInterruptAnyThreadDontStopIon,
RequestInterruptAnyThreadForkJoin
};
void requestInterrupt(InterruptMode mode);
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *runtime);
private:
@ -1598,6 +1570,13 @@ class MOZ_STACK_CLASS AutoKeepAtoms
}
};
inline void
PerThreadData::setJitStackLimit(uintptr_t limit)
{
MOZ_ASSERT(runtime_->currentThreadOwnsInterruptLock());
jitStackLimit = limit;
}
inline JSRuntime *
PerThreadData::runtimeFromMainThread()
{