Bug 1091912 - stop using mprotect to halt Ion/asm.js execution (r=bhackett)
authorLuke Wagner <luke@mozilla.com>
Tue, 11 Nov 2014 08:36:52 -0600
changeset 215515 7db30249d1d86f01440b619f78d33e99d1e3fd9b
parent 215514 97408585e41c165f6e5d0fdc0eeedda92cd237b4
child 215516 90d067dbe461e1cfd1ed9002d973d2ce121da961
push id27818
push userryanvm@gmail.com
push dateThu, 13 Nov 2014 20:19:09 +0000
treeherdermozilla-central@292ed84594c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1091912
milestone36.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
Bug 1091912 - stop using mprotect to halt Ion/asm.js execution (r=bhackett)
js/src/asmjs/AsmJSModule.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSSignalHandlers.cpp
js/src/asmjs/AsmJSSignalHandlers.h
js/src/jit/CodeGenerator.cpp
js/src/jit/ExecutableAllocator.cpp
js/src/jit/ExecutableAllocator.h
js/src/jit/ExecutableAllocatorPosix.cpp
js/src/jit/ExecutableAllocatorWin.cpp
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/IonLinker.h
js/src/jit/JitCompartment.h
js/src/jit/VMFunctions.cpp
js/src/jsapi.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsgc.cpp
js/src/vm/ForkJoin.cpp
js/src/vm/HelperThreads.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/Stack.cpp
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -49,46 +49,16 @@ using namespace js;
 using namespace jit;
 using namespace frontend;
 using mozilla::BinarySearch;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::Compression::LZ4;
 using mozilla::Swap;
 
-// At any time, the executable code of an asm.js module can be protected (as
-// part of RequestInterruptForAsmJSCode). When we touch the executable outside
-// of executing it (which the AsmJSFaultHandler will correctly handle), we need
-// to guard against this by unprotecting the code (if it has been protected) and
-// preventing it from being protected while we are touching it.
-class AutoUnprotectCode
-{
-    JSRuntime *rt_;
-    JSRuntime::AutoLockForInterrupt lock_;
-    const AsmJSModule &module_;
-    const bool protectedBefore_;
-
-  public:
-    AutoUnprotectCode(JSContext *cx, const AsmJSModule &module)
-      : rt_(cx->runtime()),
-        lock_(rt_),
-        module_(module),
-        protectedBefore_(module_.codeIsProtected(rt_))
-    {
-        if (protectedBefore_)
-            module_.unprotectCode(rt_);
-    }
-
-    ~AutoUnprotectCode()
-    {
-        if (protectedBefore_)
-            module_.protectCode(rt_);
-    }
-};
-
 static uint8_t *
 AllocateExecutableMemory(ExclusiveContext *cx, size_t bytes)
 {
 #ifdef XP_WIN
     unsigned permissions = PAGE_EXECUTE_READWRITE;
 #else
     unsigned permissions = PROT_READ | PROT_WRITE | PROT_EXEC;
 #endif
@@ -108,18 +78,17 @@ AsmJSModule::AsmJSModule(ScriptSource *s
     bufferArgumentName_(nullptr),
     code_(nullptr),
     interruptExit_(nullptr),
     prevLinked_(nullptr),
     nextLinked_(nullptr),
     dynamicallyLinked_(false),
     loadedFromCache_(false),
     profilingEnabled_(false),
-    interrupted_(false),
-    codeIsProtected_(false)
+    interrupted_(false)
 {
     mozilla::PodZero(&pod);
     pod.funcPtrTableAndExitBytes_ = SIZE_MAX;
     pod.functionBytes_ = UINT32_MAX;
     pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
     pod.maxHeapLength_ = 0x80000000;
     pod.strict_ = strict;
     pod.usesSignalHandlers_ = canUseSignalHandlers;
@@ -825,17 +794,16 @@ AsmJSModule::initHeap(Handle<ArrayBuffer
     uint32_t heapLength = heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         jit::Assembler::UpdateBoundsCheck(heapLength,
                                           (jit::Instruction*)(heapAccesses_[i].offset() + code_));
     }
 #endif
 }
 
-// This method assumes the caller has a live AutoUnprotectCode.
 void
 AsmJSModule::restoreHeapToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer)
 {
 #if defined(JS_CODEGEN_X86)
     if (maybePrevBuffer) {
         // Subtract out the base-pointer added by AsmJSModule::initHeap.
         uint8_t *ptrBase = maybePrevBuffer->dataPointer();
         for (unsigned i = 0; i < heapAccesses_.length(); i++) {
@@ -847,17 +815,16 @@ AsmJSModule::restoreHeapToInitialState(A
         }
     }
 #endif
 
     maybeHeap_ = nullptr;
     heapDatum() = nullptr;
 }
 
-// This method assumes the caller has a live AutoUnprotectCode.
 void
 AsmJSModule::restoreToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer,
                                    uint8_t *prevCode,
                                    ExclusiveContext *cx)
 {
 #ifdef DEBUG
     // Put the absolute links back to -1 so PatchDataWithValueCheck assertions
     // in staticallyLink are valid.
@@ -902,17 +869,16 @@ AsmJSModule::detachHeap(JSContext *cx)
     }
 
     // Even if this->active(), to reach here, the activation must have called
     // out via an FFI stub. FFI stubs check if heapDatum() is null on reentry
     // and throw an exception if so.
     MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_IonFFI ||
                             activation()->exitReason() == AsmJSExit::Reason_SlowFFI);
 
-    AutoUnprotectCode auc(cx, *this);
     restoreHeapToInitialState(maybeHeap_);
 
     MOZ_ASSERT(hasDetachedHeap());
     return true;
 }
 
 bool
 js::OnDetachAsmJSArrayBuffer(JSContext *cx, Handle<ArrayBufferObject*> buffer)
@@ -1563,18 +1529,16 @@ AsmJSModule::deserialize(ExclusiveContex
     loadedFromCache_ = true;
 
     return cursor;
 }
 
 bool
 AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const
 {
-    AutoUnprotectCode auc(cx, *this);
-
     *moduleOut = cx->new_<AsmJSModule>(scriptSource_, srcStart_, srcBodyStart_, pod.strict_,
                                        pod.usesSignalHandlers_);
     if (!*moduleOut)
         return false;
 
     AsmJSModule &out = **moduleOut;
 
     // Mirror the order of serialize/deserialize in cloning:
@@ -1623,17 +1587,16 @@ AsmJSModule::changeHeap(Handle<ArrayBuff
 
     // Content JS should not be able to run (and change heap) from within an
     // interrupt callback, but in case it does, fail to change heap. Otherwise,
     // the heap can change at every single instruction which would prevent
     // future optimizations like heap-base hoisting.
     if (interrupted_)
         return false;
 
-    AutoUnprotectCode auc(cx, *this);
     restoreHeapToInitialState(maybeHeap_);
     initHeap(newHeap, cx);
     return true;
 }
 
 void
 AsmJSModule::setProfilingEnabled(bool enabled, JSContext *cx)
 {
@@ -1664,19 +1627,16 @@ AsmJSModule::setProfilingEnabled(bool en
     } else {
         profilingLabels_.clear();
     }
 
     // Conservatively flush the icache for the entire module.
     AutoFlushICache afc("AsmJSModule::setProfilingEnabled");
     setAutoFlushICacheRange();
 
-    // To enable profiling, we need to patch 3 kinds of things:
-    AutoUnprotectCode auc(cx, *this);
-
     // Patch all internal (asm.js->asm.js) callsites to call the profiling
     // prologues:
     for (size_t i = 0; i < callSites_.length(); i++) {
         CallSite &cs = callSites_[i];
         if (cs.kind() != CallSite::Relative)
             continue;
 
         uint8_t *callerRetAddr = code_ + cs.returnAddressOffset();
@@ -1813,69 +1773,16 @@ AsmJSModule::setProfilingEnabled(bool en
                                                PatchedImmPtr(to),
                                                PatchedImmPtr(from));
         }
     }
 
     profilingEnabled_ = enabled;
 }
 
-void
-AsmJSModule::protectCode(JSRuntime *rt) const
-{
-    MOZ_ASSERT(isDynamicallyLinked());
-    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
-
-    codeIsProtected_ = true;
-
-    if (!pod.functionBytes_)
-        return;
-
-    // Technically, we should be able to only take away the execute permissions,
-    // however this seems to break our emulators which don't always check
-    // execute permissions while executing code.
-#if defined(XP_WIN)
-    DWORD oldProtect;
-    if (!VirtualProtect(codeBase(), functionBytes(), PAGE_NOACCESS, &oldProtect))
-        MOZ_CRASH();
-#else  // assume Unix
-    if (mprotect(codeBase(), functionBytes(), PROT_NONE))
-        MOZ_CRASH();
-#endif
-}
-
-void
-AsmJSModule::unprotectCode(JSRuntime *rt) const
-{
-    MOZ_ASSERT(isDynamicallyLinked());
-    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
-
-    codeIsProtected_ = false;
-
-    if (!pod.functionBytes_)
-        return;
-
-#if defined(XP_WIN)
-    DWORD oldProtect;
-    if (!VirtualProtect(codeBase(), functionBytes(), PAGE_EXECUTE_READWRITE, &oldProtect))
-        MOZ_CRASH();
-#else  // assume Unix
-    if (mprotect(codeBase(), functionBytes(), PROT_READ | PROT_WRITE | PROT_EXEC))
-        MOZ_CRASH();
-#endif
-}
-
-bool
-AsmJSModule::codeIsProtected(JSRuntime *rt) const
-{
-    MOZ_ASSERT(isDynamicallyLinked());
-    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
-    return codeIsProtected_;
-}
-
 static bool
 GetCPUID(uint32_t *cpuId)
 {
     enum Arch {
         X86 = 0x1,
         X64 = 0x2,
         ARM = 0x3,
         MIPS = 0x4,
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -821,20 +821,16 @@ class AsmJSModule
     HeapPtrArrayBufferObjectMaybeShared   maybeHeap_;
     AsmJSModule **                        prevLinked_;
     AsmJSModule *                         nextLinked_;
     bool                                  dynamicallyLinked_;
     bool                                  loadedFromCache_;
     bool                                  profilingEnabled_;
     bool                                  interrupted_;
 
-    // This field is accessed concurrently when requesting an interrupt.
-    // Access must be synchronized via the runtime's interrupt lock.
-    mutable bool                          codeIsProtected_;
-
     void restoreHeapToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer);
     void restoreToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer, uint8_t *prevCode,
                                ExclusiveContext *cx);
 
   public:
     explicit AsmJSModule(ScriptSource *scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
                          bool strict, bool canUseSignalHandlers);
     void trace(JSTracer *trc);
@@ -1524,22 +1520,16 @@ class AsmJSModule
         MOZ_ASSERT(isDynamicallyLinked());
         return profilingEnabled_;
     }
     void setProfilingEnabled(bool enabled, JSContext *cx);
     void setInterrupted(bool interrupted) {
         MOZ_ASSERT(isDynamicallyLinked());
         interrupted_ = interrupted;
     }
-
-    // Additionally, these functions may only be called while holding the
-    // runtime's interrupt lock.
-    void protectCode(JSRuntime *rt) const;
-    void unprotectCode(JSRuntime *rt) const;
-    bool codeIsProtected(JSRuntime *rt) const;
 };
 
 // Store the just-parsed module in the cache using AsmJSCacheOps.
 extern JS::AsmJSCacheResult
 StoreAsmJSModuleInCache(AsmJSParser &parser,
                         const AsmJSModule &module,
                         ExclusiveContext *cx);
 
--- a/js/src/asmjs/AsmJSSignalHandlers.cpp
+++ b/js/src/asmjs/AsmJSSignalHandlers.cpp
@@ -14,25 +14,71 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/AsmJSSignalHandlers.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/PodOperations.h"
 
 #include "asmjs/AsmJSModule.h"
 #include "vm/Runtime.h"
 
 using namespace js;
 using namespace js::jit;
 
 using JS::GenericNaN;
 using mozilla::DebugOnly;
+using mozilla::PodArrayZero;
+
+#if defined(ANDROID)
+# include <sys/system_properties.h>
+# if defined(MOZ_LINKER)
+extern "C" MFBT_API bool IsSignalHandlingBroken();
+# endif
+#endif
+
+// For platforms where the signal/exception handler runs on the same
+// thread/stack as the victim (Unix and Windows), we can use TLS to find any
+// currently executing asm.js code.
+static JSRuntime *
+RuntimeForCurrentThread()
+{
+    PerThreadData *threadData = TlsPerThreadData.get();
+    if (!threadData)
+        return nullptr;
+
+    return threadData->runtimeIfOnOwnerThread();
+}
+
+// Crashing inside the signal handler can cause the handler to be recursively
+// invoked, eventually blowing the stack without actually showing a crash
+// report dialog via Breakpad. To guard against this we watch for such
+// recursion and fall through to the next handler immediately rather than
+// trying to handle it.
+class AutoSetHandlingSignal
+{
+    JSRuntime *rt;
+
+  public:
+    explicit AutoSetHandlingSignal(JSRuntime *rt)
+      : rt(rt)
+    {
+        MOZ_ASSERT(!rt->handlingSignal);
+        rt->handlingSignal = true;
+    }
+
+    ~AutoSetHandlingSignal()
+    {
+        MOZ_ASSERT(rt->handlingSignal);
+        rt->handlingSignal = false;
+    }
+};
 
 #if defined(XP_WIN)
 # define XMM_sig(p,i) ((p)->Xmm##i)
 # define EIP_sig(p) ((p)->Eip)
 # define RIP_sig(p) ((p)->Rip)
 # define RAX_sig(p) ((p)->Rax)
 # define RCX_sig(p) ((p)->Rcx)
 # define RDX_sig(p) ((p)->Rdx)
@@ -147,93 +193,34 @@ using mozilla::DebugOnly;
 # define R13_sig(p) ((p)->uc_mcontext.mc_r13)
 # define R14_sig(p) ((p)->uc_mcontext.mc_r14)
 # if defined(__FreeBSD__) && defined(__arm__)
 #  define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15])
 # else
 #  define R15_sig(p) ((p)->uc_mcontext.mc_r15)
 # endif
 #elif defined(XP_MACOSX)
-// Mach requires special treatment.
+# define EIP_sig(p) ((p)->uc_mcontext->__ss.__eip)
+# define RIP_sig(p) ((p)->uc_mcontext->__ss.__rip)
 #else
 # error "Don't know how to read/write to the thread state via the mcontext_t."
 #endif
 
-// For platforms where the signal/exception handler runs on the same
-// thread/stack as the victim (Unix and Windows), we can use TLS to find any
-// currently executing asm.js code.
-#if !defined(XP_MACOSX)
-static JSRuntime *
-RuntimeForCurrentThread()
-{
-    PerThreadData *threadData = TlsPerThreadData.get();
-    if (!threadData)
-        return nullptr;
-
-    return threadData->runtimeIfOnOwnerThread();
-}
-#endif // !defined(XP_MACOSX)
-
-// Crashing inside the signal handler can cause the handler to be recursively
-// invoked, eventually blowing the stack without actually showing a crash
-// report dialog via Breakpad. To guard against this we watch for such
-// recursion and fall through to the next handler immediately rather than
-// trying to handle it.
-class AutoSetHandlingSignal
-{
-    JSRuntime *rt;
-
-  public:
-    explicit AutoSetHandlingSignal(JSRuntime *rt)
-      : rt(rt)
-    {
-        MOZ_ASSERT(!rt->handlingSignal);
-        rt->handlingSignal = true;
-    }
-
-    ~AutoSetHandlingSignal()
-    {
-        MOZ_ASSERT(rt->handlingSignal);
-        rt->handlingSignal = false;
-    }
-};
-
-#if defined(JS_CODEGEN_X64)
-template <class T>
-static void
-SetXMMRegToNaN(bool isFloat32, T *xmm_reg)
-{
-    if (isFloat32) {
-        JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float));
-        float *floats = reinterpret_cast<float*>(xmm_reg);
-        floats[0] = GenericNaN();
-        floats[1] = 0;
-        floats[2] = 0;
-        floats[3] = 0;
-    } else {
-        JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double));
-        double *dbls = reinterpret_cast<double*>(xmm_reg);
-        dbls[0] = GenericNaN();
-        dbls[1] = 0;
-    }
-}
-#endif
-
 #if defined(XP_WIN)
 # include "jswin.h"
 #else
 # include <signal.h>
 # include <sys/mman.h>
 #endif
 
 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 # include <sys/ucontext.h> // for ucontext_t, mcontext_t
 #endif
 
-#if defined(JS_CODEGEN_X64)
+#if defined(JS_CPU_X64)
 # if defined(__DragonFly__)
 #  include <machine/npx.h> // for union savefpu
 # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
        defined(__NetBSD__) || defined(__OpenBSD__)
 #  include <machine/fpu.h> // for struct savefpu/fxsave64
 # endif
 #endif
 
@@ -312,77 +299,57 @@ typedef struct ucontext {
     mcontext_t uc_mcontext;
     // Other fields are not used by V8, don't define them here.
 } ucontext_t;
 enum { REG_EIP = 14 };
 #  endif  // defined(__i386__)
 # endif  // !defined(__BIONIC_HAVE_UCONTEXT_T)
 #endif // defined(ANDROID)
 
-#if defined(ANDROID) && defined(MOZ_LINKER)
-// Apparently, on some Android systems, the signal handler is always passed
-// nullptr as the faulting address. This would cause the asm.js signal handler
-// to think that a safe out-of-bounds access was a nullptr-deref. This
-// brokenness is already detected by ElfLoader (enabled by MOZ_LINKER), so
-// reuse that check to disable asm.js compilation on systems where the signal
-// handler is broken.
-extern "C" MFBT_API bool IsSignalHandlingBroken();
-#else
-static bool IsSignalHandlingBroken() { return false; }
-#endif // defined(MOZ_LINKER)
-
 #if !defined(XP_WIN)
 # define CONTEXT ucontext_t
 #endif
 
 #if defined(JS_CPU_X64)
 # define PC_sig(p) RIP_sig(p)
 #elif defined(JS_CPU_X86)
 # define PC_sig(p) EIP_sig(p)
 #elif defined(JS_CPU_ARM)
 # define PC_sig(p) R15_sig(p)
 #elif defined(JS_CPU_MIPS)
 # define PC_sig(p) EPC_sig(p)
 #endif
 
-static bool
-HandleSimulatorInterrupt(JSRuntime *rt, AsmJSActivation *activation, void *faultingAddress)
-{
-    // If the ARM simulator is enabled, the pc is in the simulator C++ code and
-    // not in the generated code, so we check the simulator's pc manually. Also
-    // note that we can't simply use simulator->set_pc() here because the
-    // simulator could be in the middle of an instruction. On ARM, the signal
-    // handlers are currently only used for Odin code, see bug 964258.
-
-#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
-    const AsmJSModule &module = activation->module();
-    if (module.containsFunctionPC((void *)rt->mainThread.simulator()->get_pc()) &&
-        module.containsFunctionPC(faultingAddress))
-    {
-        activation->setResumePC(nullptr);
-        int32_t nextpc = int32_t(module.interruptExit());
-        rt->mainThread.simulator()->set_resume_pc(nextpc);
-        return true;
-    }
-#endif
-    return false;
-}
-
-#if !defined(XP_MACOSX)
 static uint8_t **
 ContextToPC(CONTEXT *context)
 {
-#ifdef JS_CODEGEN_NONE
-    MOZ_CRASH();
-#else
     return reinterpret_cast<uint8_t**>(&PC_sig(context));
-#endif
 }
 
-# if defined(JS_CODEGEN_X64)
+#if defined(JS_CPU_X64)
+template <class T>
+static void
+SetXMMRegToNaN(bool isFloat32, T *xmm_reg)
+{
+    if (isFloat32) {
+        JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float));
+        float *floats = reinterpret_cast<float*>(xmm_reg);
+        floats[0] = GenericNaN();
+        floats[1] = 0;
+        floats[2] = 0;
+        floats[3] = 0;
+    } else {
+        JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double));
+        double *dbls = reinterpret_cast<double*>(xmm_reg);
+        dbls[0] = GenericNaN();
+        dbls[1] = 0;
+    }
+}
+
+# if !defined(XP_MACOSX)
 static void
 SetRegisterToCoercedUndefined(CONTEXT *context, bool isFloat32, AnyRegister reg)
 {
     if (reg.isFloat()) {
         switch (reg.fpu().code()) {
           case X86Registers::xmm0:  SetXMMRegToNaN(isFloat32, &XMM_sig(context, 0)); break;
           case X86Registers::xmm1:  SetXMMRegToNaN(isFloat32, &XMM_sig(context, 1)); break;
           case X86Registers::xmm2:  SetXMMRegToNaN(isFloat32, &XMM_sig(context, 2)); break;
@@ -418,74 +385,55 @@ SetRegisterToCoercedUndefined(CONTEXT *c
           case X86Registers::r12: R12_sig(context) = 0; break;
           case X86Registers::r13: R13_sig(context) = 0; break;
           case X86Registers::r14: R14_sig(context) = 0; break;
           case X86Registers::r15: R15_sig(context) = 0; break;
           default: MOZ_CRASH();
         }
     }
 }
-# endif  // JS_CODEGEN_X64
-#endif   // !XP_MACOSX
+# endif  // !XP_MACOSX
+#endif // JS_CPU_X64
 
 #if defined(XP_WIN)
 
 static bool
-HandleException(PEXCEPTION_POINTERS exception)
+HandleFault(PEXCEPTION_POINTERS exception)
 {
     EXCEPTION_RECORD *record = exception->ExceptionRecord;
     CONTEXT *context = exception->ContextRecord;
 
     if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
         return false;
 
     uint8_t **ppc = ContextToPC(context);
     uint8_t *pc = *ppc;
     MOZ_ASSERT(pc == record->ExceptionAddress);
 
     if (record->NumberParameters < 2)
         return false;
 
-    void *faultingAddress = (void*)record->ExceptionInformation[1];
-
+    // Don't allow recursive handling of signals, see AutoSetHandlingSignal.
     JSRuntime *rt = RuntimeForCurrentThread();
-
-    // Don't allow recursive handling of signals, see AutoSetHandlingSignal.
     if (!rt || rt->handlingSignal)
         return false;
     AutoSetHandlingSignal handling(rt);
 
-    if (rt->jitRuntime() && rt->jitRuntime()->handleAccessViolation(rt, faultingAddress))
-        return true;
-
-    AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
+    AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
     if (!activation)
         return false;
 
     const AsmJSModule &module = activation->module();
     if (!module.containsFunctionPC(pc))
         return false;
 
-    // If we faulted trying to execute code in 'module', this must be an
-    // interrupt callback (see RequestInterruptForAsmJSCode). Redirect
-    // execution to a trampoline which will call js::HandleExecutionInterrupt.
-    // The trampoline will jump to activation->resumePC if execution isn't
-    // interrupted.
-    if (module.containsFunctionPC(faultingAddress)) {
-        activation->setResumePC(pc);
-        *ppc = module.interruptExit();
-
-        JSRuntime::AutoLockForInterrupt lock(rt);
-        module.unprotectCode(rt);
-        return true;
-    }
-
-# if defined(JS_CODEGEN_X64)
+# if defined(JS_CPU_X64)
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
+    void *faultingAddress = (void*)record->ExceptionInformation[1];
     if (!module.maybeHeap() ||
         faultingAddress < module.maybeHeap() ||
         faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
     {
         return false;
     }
 
     const AsmJSHeapAccess *heapAccess = module.lookupHeapAccess(pc);
@@ -507,43 +455,41 @@ HandleException(PEXCEPTION_POINTERS exce
     *ppc += heapAccess->opLength();
     return true;
 # else
     return false;
 # endif
 }
 
 static LONG WINAPI
-AsmJSExceptionHandler(LPEXCEPTION_POINTERS exception)
+AsmJSFaultHandler(LPEXCEPTION_POINTERS exception)
 {
-    if (HandleException(exception))
+    if (HandleFault(exception))
         return EXCEPTION_CONTINUE_EXECUTION;
 
     // No need to worry about calling other handlers, the OS does this for us.
     return EXCEPTION_CONTINUE_SEARCH;
 }
 
 #elif defined(XP_MACOSX)
 # include <mach/exc.h>
 
 static uint8_t **
 ContextToPC(x86_thread_state_t &state)
 {
-# if defined(JS_CODEGEN_X64)
+# if defined(JS_CPU_X64)
     JS_STATIC_ASSERT(sizeof(state.uts.ts64.__rip) == sizeof(void*));
     return reinterpret_cast<uint8_t**>(&state.uts.ts64.__rip);
-# elif defined(JS_CODEGEN_NONE)
-    MOZ_CRASH();
 # else
     JS_STATIC_ASSERT(sizeof(state.uts.ts32.__eip) == sizeof(void*));
     return reinterpret_cast<uint8_t**>(&state.uts.ts32.__eip);
 # endif
 }
 
-# if defined(JS_CODEGEN_X64)
+# if defined(JS_CPU_X64)
 static bool
 SetRegisterToCoercedUndefined(mach_port_t rtThread, x86_thread_state64_t &state,
                               const AsmJSHeapAccess &heapAccess)
 {
     if (heapAccess.loadedReg().isFloat()) {
         kern_return_t kret;
 
         x86_float_state64_t fstate;
@@ -645,55 +591,28 @@ HandleMachException(JSRuntime *rt, const
         return false;
 
     uint8_t **ppc = ContextToPC(state);
     uint8_t *pc = *ppc;
 
     if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
         return false;
 
-    void *faultingAddress = (void*)request.body.code[1];
-
-    if (rt->jitRuntime() && rt->jitRuntime()->handleAccessViolation(rt, faultingAddress))
-        return true;
-
     AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
     if (!activation)
         return false;
 
     const AsmJSModule &module = activation->module();
-    if (HandleSimulatorInterrupt(rt, activation, faultingAddress)) {
-        JSRuntime::AutoLockForInterrupt lock(rt);
-        module.unprotectCode(rt);
-        return true;
-    }
-
     if (!module.containsFunctionPC(pc))
         return false;
 
-    // If we faulted trying to execute code in 'module', this must be an
-    // interrupt callback (see RequestInterruptForAsmJSCode). Redirect
-    // execution to a trampoline which will call js::HandleExecutionInterrupt.
-    // The trampoline will jump to activation->resumePC if execution isn't
-    // interrupted.
-    if (module.containsFunctionPC(faultingAddress)) {
-        activation->setResumePC(pc);
-        *ppc = module.interruptExit();
-
-        JSRuntime::AutoLockForInterrupt lock(rt);
-        module.unprotectCode(rt);
-
-        // Update the thread state with the new pc.
-        kret = thread_set_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, x86_THREAD_STATE_COUNT);
-        return kret == KERN_SUCCESS;
-    }
-
-# if defined(JS_CODEGEN_X64)
+# if defined(JS_CPU_X64)
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
+    void *faultingAddress = (void*)request.body.code[1];
     if (!module.maybeHeap() ||
         faultingAddress < module.maybeHeap() ||
         faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
     {
         return false;
     }
 
     const AsmJSHeapAccess *heapAccess = module.lookupHeapAccess(pc);
@@ -874,65 +793,40 @@ AsmJSMachExceptionHandler::install(JSRun
     return false;
 }
 
 #else  // If not Windows or Mac, assume Unix
 
 // Be very cautious and default to not handling; we don't want to accidentally
 // silence real crashes from real bugs.
 static bool
-HandleSignal(int signum, siginfo_t *info, void *ctx)
+HandleFault(int signum, siginfo_t *info, void *ctx)
 {
     CONTEXT *context = (CONTEXT *)ctx;
     uint8_t **ppc = ContextToPC(context);
     uint8_t *pc = *ppc;
 
-    void *faultingAddress = info->si_addr;
-
+    // Don't allow recursive handling of signals, see AutoSetHandlingSignal.
     JSRuntime *rt = RuntimeForCurrentThread();
-
-    // Don't allow recursive handling of signals, see AutoSetHandlingSignal.
     if (!rt || rt->handlingSignal)
         return false;
     AutoSetHandlingSignal handling(rt);
 
-    if (rt->jitRuntime() && rt->jitRuntime()->handleAccessViolation(rt, faultingAddress))
-        return true;
-
-    AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
+    AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
     if (!activation)
         return false;
 
     const AsmJSModule &module = activation->module();
-    if (HandleSimulatorInterrupt(rt, activation, faultingAddress)) {
-        JSRuntime::AutoLockForInterrupt lock(rt);
-        module.unprotectCode(rt);
-        return true;
-    }
-
     if (!module.containsFunctionPC(pc))
         return false;
 
-    // If we faulted trying to execute code in 'module', this must be an
-    // interrupt callback (see RequestInterruptForAsmJSCode). Redirect
-    // execution to a trampoline which will call js::HandleExecutionInterrupt.
-    // The trampoline will jump to activation->resumePC if execution isn't
-    // interrupted.
-    if (module.containsFunctionPC(faultingAddress)) {
-        activation->setResumePC(pc);
-        *ppc = module.interruptExit();
-
-        JSRuntime::AutoLockForInterrupt lock(rt);
-        module.unprotectCode(rt);
-        return true;
-    }
-
-# if defined(JS_CODEGEN_X64)
+# if defined(JS_CPU_X64)
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
+    void *faultingAddress = info->si_addr;
     if (!module.maybeHeap() ||
         faultingAddress < module.maybeHeap() ||
         faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
     {
         return false;
     }
 
     const AsmJSHeapAccess *heapAccess = module.lookupHeapAccess(pc);
@@ -949,120 +843,231 @@ HandleSignal(int signum, siginfo_t *info
         SetRegisterToCoercedUndefined(context, heapAccess->isFloat32Load(), heapAccess->loadedReg());
     *ppc += heapAccess->opLength();
     return true;
 # else
     return false;
 # endif
 }
 
-static struct sigaction sPrevHandler;
+static struct sigaction sPrevSEGVHandler;
 
 static void
 AsmJSFaultHandler(int signum, siginfo_t *info, void *context)
 {
-    if (HandleSignal(signum, info, context))
+    if (HandleFault(signum, info, context))
         return;
 
     // This signal is not for any asm.js code we expect, so we need to forward
     // the signal to the next handler. If there is no next handler (SIG_IGN or
     // SIG_DFL), then it's time to crash. To do this, we set the signal back to
     // its original disposition and return. This will cause the faulting op to
     // be re-executed which will crash in the normal way. The advantage of
     // doing this to calling _exit() is that we remove ourselves from the crash
     // stack which improves crash reports. If there is a next handler, call it.
     // It will either crash synchronously, fix up the instruction so that
     // execution can continue and return, or trigger a crash by returning the
     // signal to it's original disposition and returning.
     //
     // Note: the order of these tests matter.
-    if (sPrevHandler.sa_flags & SA_SIGINFO)
-        sPrevHandler.sa_sigaction(signum, info, context);
-    else if (sPrevHandler.sa_handler == SIG_DFL || sPrevHandler.sa_handler == SIG_IGN)
-        sigaction(signum, &sPrevHandler, nullptr);
+    if (sPrevSEGVHandler.sa_flags & SA_SIGINFO)
+        sPrevSEGVHandler.sa_sigaction(signum, info, context);
+    else if (sPrevSEGVHandler.sa_handler == SIG_DFL || sPrevSEGVHandler.sa_handler == SIG_IGN)
+        sigaction(signum, &sPrevSEGVHandler, nullptr);
     else
-        sPrevHandler.sa_handler(signum);
+        sPrevSEGVHandler.sa_handler(signum);
 }
 #endif
 
-#if !defined(XP_MACOSX)
-static bool sInstalledHandlers = false;
+static void
+RedirectIonBackedgesToInterruptCheck(JSRuntime *rt)
+{
+    if (jit::JitRuntime *jitRuntime = rt->jitRuntime()) {
+        // If the backedge list is being mutated, the pc must be in C++ code and
+        // thus not in a JIT iloop. We assume that the interrupt flag will be
+        // checked at least once before entering JIT code (if not, no big deal;
+        // the browser will just request another interrupt in a second).
+        if (!jitRuntime->mutatingBackedgeList())
+            jitRuntime->patchIonBackedges(rt, jit::JitRuntime::BackedgeInterruptCheck);
+    }
+}
+
+static void
+RedirectJitCodeToInterruptCheck(JSRuntime *rt, CONTEXT *context)
+{
+    RedirectIonBackedgesToInterruptCheck(rt);
+
+    if (AsmJSActivation *activation = rt->mainThread.asmJSActivationStack()) {
+        const AsmJSModule &module = activation->module();
+
+#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
+        if (module.containsFunctionPC((void*)rt->mainThread.simulator()->get_pc()))
+            rt->mainThread.simulator()->set_resume_pc(int32_t(module.interruptExit()));
+#endif
+
+        uint8_t **ppc = ContextToPC(context);
+        uint8_t *pc = *ppc;
+        if (module.containsFunctionPC(pc)) {
+            activation->setResumePC(pc);
+            *ppc = module.interruptExit();
+        }
+    }
+}
+
+#if !defined(XP_WIN)
+// For the interrupt signal, pick a signal number that:
+//  - is not otherwise used by mozilla or standard libraries
+//  - defaults to nostop and noprint on gdb/lldb so that noone is bothered
+// SIGVTALRM a relative of SIGALRM, so intended for user code, but, unlike
+// SIGALRM, not used anywhere else in Mozilla.
+static const int sInterruptSignal = SIGVTALRM;
+
+static void
+JitInterruptHandler(int signum, siginfo_t *info, void *context)
+{
+    if (JSRuntime *rt = RuntimeForCurrentThread())
+        RedirectJitCodeToInterruptCheck(rt, (CONTEXT*)context);
+}
 #endif
 
 bool
-js::EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt)
+js::EnsureSignalHandlersInstalled(JSRuntime *rt)
 {
-#ifdef JS_CODEGEN_NONE
-    // Don't install signal handlers in builds with the JIT disabled.
-    return false;
+#if defined(XP_MACOSX)
+    // On OSX, each JSRuntime gets its own handler thread.
+    if (!rt->asmJSMachExceptionHandler.installed() && !rt->asmJSMachExceptionHandler.install(rt))
+        return false;
 #endif
 
+    // All the rest of the handlers are process-wide and thus must only be
+    // installed once. We assume that there are no races creating the first
+    // JSRuntime of the process.
+    static bool sTried = false;
+    static bool sResult = false;
+    if (sTried)
+        return sResult;
+    sTried = true;
+
+#if defined(ANDROID)
+    // Before Android 4.4 (SDK version 19), there is a bug
+    //   https://android-review.googlesource.com/#/c/52333
+    // in Bionic's pthread_join which causes pthread_join to return early when
+    // pthread_kill is used (on any thread). Nobody expects the pthread_cond_wait
+    // EINTRquisition.
+    char version_string[PROP_VALUE_MAX];
+    PodArrayZero(version_string);
+    if (__system_property_get("ro.build.version.sdk", version_string) > 0) {
+        if (atol(version_string) < 19)
+            return false;
+    }
+# if defined(MOZ_LINKER)
+    // Signal handling is broken on some android systems.
     if (IsSignalHandlingBroken())
         return false;
+# endif
+#endif
 
-#if defined(XP_MACOSX)
-    // On OSX, each JSRuntime gets its own handler.
-    return rt->asmJSMachExceptionHandler.installed() || rt->asmJSMachExceptionHandler.install(rt);
+#if defined(XP_WIN)
+    // Windows uses SuspendThread to stop the main thread from another thread,
+    // so the only handler we need is for asm.js out-of-bound faults.
+    if (!AddVectoredExceptionHandler(/* FirstHandler = */true, AsmJSFaultHandler))
+        MOZ_CRASH("unable to install vectored exception handler");
 #else
-    // Assume Windows or Unix. For these platforms, there is a single,
-    // process-wide signal handler installed. Take care to only install it once.
-    if (sInstalledHandlers)
-        return true;
+    // The interrupt handler allows the main thread to be paused from another
+    // thread (see InterruptRunningJitCode).
+    struct sigaction interruptHandler;
+    interruptHandler.sa_flags = SA_SIGINFO;
+    interruptHandler.sa_sigaction = &JitInterruptHandler;
+    sigemptyset(&interruptHandler.sa_mask);
+    struct sigaction prev;
+    if (sigaction(sInterruptSignal, &interruptHandler, &prev))
+        MOZ_CRASH("unable to install interrupt handler");
 
-# if defined(XP_WIN)
-    if (!AddVectoredExceptionHandler(/* FirstHandler = */true, AsmJSExceptionHandler))
-        return false;
-# else
-    // Assume Unix. SA_NODEFER allows us to reenter the signal handler if we
-    // crash while handling the signal, and fall through to the Breakpad
-    // handler by testing handlingSignal.
-    struct sigaction sigAction;
-    sigAction.sa_flags = SA_SIGINFO | SA_NODEFER;
-    sigAction.sa_sigaction = &AsmJSFaultHandler;
-    sigemptyset(&sigAction.sa_mask);
-    if (sigaction(SIGSEGV, &sigAction, &sPrevHandler))
-        return false;
-# endif
+    // There shouldn't be any other handlers installed for sInterruptSignal. If
+    // there are, we could always forward, but we need to understand what we're
+    // doing to avoid problematic interference.
+    if ((prev.sa_flags & SA_SIGINFO && prev.sa_sigaction) ||
+        (prev.sa_handler != SIG_DFL && prev.sa_handler != SIG_IGN))
+    {
+        MOZ_CRASH("contention for interrupt signal");
+    }
 
-    sInstalledHandlers = true;
-#endif
+    // Lastly, install a SIGSEGV handler to handle safely-out-of-bounds asm.js
+    // heap access. OSX handles seg faults via the Mach exception handler above,
+    // so don't install AsmJSFaultHandler.
+# if !defined(XP_MACOSX)
+    // SA_NODEFER allows us to reenter the signal handler if we crash while
+    // handling the signal, and fall through to the Breakpad handler by testing
+    // handlingSignal.
+    struct sigaction faultHandler;
+    faultHandler.sa_flags = SA_SIGINFO | SA_NODEFER;
+    faultHandler.sa_sigaction = &AsmJSFaultHandler;
+    sigemptyset(&faultHandler.sa_mask);
+    if (sigaction(SIGSEGV, &faultHandler, &sPrevSEGVHandler))
+        MOZ_CRASH("unable to install segv handler");
+# endif // defined(XP_MACOSX)
+#endif // defined(XP_WIN)
+
+    sResult = true;
     return true;
 }
 
-// To interrupt execution of a JSRuntime, any thread may call
-// JS_RequestInterruptCallback (JSRuntime::requestInterruptCallback from inside
-// the engine). In the simplest case, this sets some state that is polled at
-// regular intervals (function prologues, loop headers). For tight loops, this
-// poses non-trivial overhead. For asm.js, we can do better: when another
-// thread requests an interrupt, we simply mprotect all of the innermost asm.js
-// module activation's code. This will trigger a SIGSEGV, taking us into
-// AsmJSFaultHandler. From there, we can manually redirect execution to call
-// js::HandleExecutionInterrupt. The memory is un-protected from the signal
-// handler after control flow is redirected.
+// JSRuntime::requestInterrupt sets interrupt_ (which is checked frequently by
+// C++ code at every Baseline JIT loop backedge) and jitStackLimit_ (which is
+// checked at every Baseline and Ion JIT function prologue). The remaining
+// sources of potential iloops (Ion loop backedges and all asm.js code) are
+// handled by this function:
+//  1. Ion loop backedges are patched to instead point to a stub that handles the
+//     interrupt;
+//  2. if the main thread's pc is inside asm.js code, the pc is updated to point
+//     to a stub that handles the interrupt.
 void
-js::RequestInterruptForAsmJSCode(JSRuntime *rt, int interruptModeRaw)
+js::InterruptRunningJitCode(JSRuntime *rt)
 {
-    switch (JSRuntime::InterruptMode(interruptModeRaw)) {
-      case JSRuntime::RequestInterruptMainThread:
-      case JSRuntime::RequestInterruptAnyThread:
-        break;
-      case JSRuntime::RequestInterruptAnyThreadDontStopIon:
-      case JSRuntime::RequestInterruptAnyThreadForkJoin:
-        // It is ok to wait for asm.js execution to complete; we aren't trying
-        // to break an iloop or anything. Avoid the overhead of protecting all
-        // the code and taking a fault.
+    // If signal handlers weren't installed, then Ion and asm.js emit normal
+    // interrupt checks and don't need asynchronous interruption.
+    if (!rt->canUseSignalHandlers())
+        return;
+
+    // If we are on runtime's main thread, then: pc is not in asm.js code (so
+    // nothing to do for asm.js) and we can patch Ion backedges without any
+    // special synchronization.
+    if (rt == RuntimeForCurrentThread()) {
+        RedirectIonBackedgesToInterruptCheck(rt);
         return;
     }
 
-    AsmJSActivation *activation = rt->mainThread.asmJSActivationStack();
-    if (!activation)
-        return;
+    // We are not on the runtime's main thread, so to do 1 and 2 above, we need
+    // to halt the runtime's main thread first.
+#if defined(XP_WIN)
+    // On Windows, we can simply suspend the main thread and work directly on
+    // its context from this thread.
+    HANDLE thread = (HANDLE)rt->ownerThreadNative();
+    if (SuspendThread(thread) == -1)
+        MOZ_CRASH("Failed to suspend main thread");
+
+    CONTEXT context;
+    context.ContextFlags = CONTEXT_CONTROL;
+    if (!GetThreadContext(thread, &context))
+        MOZ_CRASH("Failed to get suspended thread context");
 
-    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
-    activation->module().protectCode(rt);
+    RedirectJitCodeToInterruptCheck(rt, &context);
+
+    if (!SetThreadContext(thread, &context))
+        MOZ_CRASH("Failed to set suspended thread context");
+
+    if (ResumeThread(thread) == -1)
+        MOZ_CRASH("Failed to resume main thread");
+#else
+    // On Unix, we instead deliver an async signal to the main thread which
+    // halts the thread and callers our JitInterruptHandler (which has already
+    // been installed by EnsureSignalHandlersInstalled).
+    pthread_t thread = (pthread_t)rt->ownerThreadNative();
+    pthread_kill(thread, sInterruptSignal);
+#endif
 }
 
 // This is not supported by clang-cl yet.
 #if defined(MOZ_ASAN) && defined(JS_STANDALONE) && !defined(_MSC_VER)
 // Usually, this definition is found in mozglue (see mozglue/build/AsanOptions.cpp).
 // However, when doing standalone JS builds, mozglue is not used and we must ensure
 // that we still allow custom SIGSEGV handlers for asm.js and ion to work correctly.
 extern "C" MOZ_ASAN_BLACKLIST
--- a/js/src/asmjs/AsmJSSignalHandlers.h
+++ b/js/src/asmjs/AsmJSSignalHandlers.h
@@ -23,25 +23,26 @@ struct JSRuntime;
 
 #ifdef XP_MACOSX
 # include <mach/mach.h>
 # include "jslock.h"
 #endif
 
 namespace js {
 
-// Returns whether signal handlers for asm.js and for JitRuntime access
-// violations have been installed.
+// Set up any signal/exception handlers needed to execute code in the given
+// runtime. Return whether runtime can:
+//  - rely on fault handler support for avoiding asm.js heap bounds checks
+//  - rely on InterruptRunningJitCode to halt running Ion/asm.js from any thread
 bool
-EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt);
+EnsureSignalHandlersInstalled(JSRuntime *rt);
 
-// Force any currently-executing asm.js code to call
-// js::HandleExecutionInterrupt.
+// Force any currently-executing asm.js code to call HandleExecutionInterrupt.
 extern void
-RequestInterruptForAsmJSCode(JSRuntime *rt, int interruptMode);
+InterruptRunningJitCode(JSRuntime *rt);
 
 // On OSX we are forced to use the lower-level Mach exception mechanism instead
 // of Unix signals. Mach exceptions are not handled on the victim's stack but
 // rather require an extra thread. For simplicity, we create one such thread
 // per JSRuntime (upon the first use of asm.js in the JSRuntime). This thread
 // and related resources are owned by AsmJSMachExceptionHandler which is owned
 // by JSRuntime.
 #ifdef XP_MACOSX
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7619,30 +7619,16 @@ CodeGenerator::link(JSContext *cx, types
                      safepointIndices_.length(), osiIndices_.length(),
                      cacheList_.length(), runtimeData_.length(),
                      safepoints_.size(), callTargets.length(),
                      patchableBackedges_.length(), optimizationLevel);
     if (!ionScript)
         return false;
     discardIonCode.ionScript = ionScript;
 
-    // Lock the runtime against interrupt callbacks during the link.
-    // We don't want an interrupt request to protect the code for the script
-    // before it has been filled in, as we could segv before the runtime's
-    // patchable backedges have been fully updated.
-    JSRuntime::AutoLockForInterrupt lock(cx->runtime());
-
-    // Make sure we don't segv while filling in the code, to avoid deadlocking
-    // inside the signal handler.
-    cx->runtime()->jitRuntime()->ensureIonCodeAccessible(cx->runtime());
-
-    // Implicit interrupts are used only for sequential code. In parallel mode
-    // use the normal executable allocator so that we cannot segv during
-    // execution off the main thread.
-    //
     // Also, note that creating the code here during an incremental GC will
     // trace the code and mark all GC things it refers to. This captures any
     // read barriers which were skipped while compiling the script off thread.
     Linker linker(masm);
     AutoFlushICache afc("IonLink");
     JitCode *code = (executionMode == SequentialExecution)
                     ? linker.newCodeForIonScript(cx)
                     : linker.newCode<CanGC>(cx, ION_CODE);
--- a/js/src/jit/ExecutableAllocator.cpp
+++ b/js/src/jit/ExecutableAllocator.cpp
@@ -57,34 +57,8 @@ ExecutableAllocator::addSizeOfCode(JS::C
             sizes->unused   += pool->m_allocation.size - pool->m_ionCodeBytes
                                                        - pool->m_baselineCodeBytes
                                                        - pool->m_regexpCodeBytes
                                                        - pool->m_otherCodeBytes;
         }
     }
 }
 
-void
-ExecutableAllocator::toggleAllCodeAsAccessible(bool accessible)
-{
-    if (!m_pools.initialized())
-        return;
-
-    for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront()) {
-        ExecutablePool* pool = r.front();
-        pool->toggleAllCodeAsAccessible(accessible);
-    }
-}
-
-bool
-ExecutableAllocator::codeContains(char* address)
-{
-    if (!m_pools.initialized())
-        return false;
-
-    for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront()) {
-        ExecutablePool* pool = r.front();
-        if (pool->codeContains(address))
-            return true;
-    }
-
-    return false;
-}
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -165,22 +165,16 @@ private:
         }
         return result;
     }
 
     size_t available() const {
         MOZ_ASSERT(m_end >= m_freePtr);
         return m_end - m_freePtr;
     }
-
-    void toggleAllCodeAsAccessible(bool accessible);
-
-    bool codeContains(char* address) {
-        return address >= m_allocation.pages && address < m_freePtr;
-    }
 };
 
 class ExecutableAllocator {
     typedef void (*DestroyCallback)(void* addr, size_t size);
     enum ProtectionSetting { Writable, Executable };
     DestroyCallback destroyCallback;
 
 public:
@@ -255,18 +249,16 @@ public:
         if (destroyCallback)
             destroyCallback(pool->m_allocation.pages, pool->m_allocation.size);
         systemRelease(pool->m_allocation);
         MOZ_ASSERT(m_pools.initialized());
         m_pools.remove(m_pools.lookup(pool));   // this asserts if |pool| is not in m_pools
     }
 
     void addSizeOfCode(JS::CodeSizes *sizes) const;
-    void toggleAllCodeAsAccessible(bool accessible);
-    bool codeContains(char* address);
 
     void setDestroyCallback(DestroyCallback destroyCallback) {
         this->destroyCallback = destroyCallback;
     }
 
 private:
     static size_t pageSize;
     static size_t largeAllocSize;
--- a/js/src/jit/ExecutableAllocatorPosix.cpp
+++ b/js/src/jit/ExecutableAllocatorPosix.cpp
@@ -86,23 +86,8 @@ void ExecutableAllocator::reprotectRegio
     // Round size up
     size += (pageSize - 1);
     size &= ~(pageSize - 1);
 
     mprotect(pageStart, size, (setting == Writable) ? PROTECTION_FLAGS_RW : PROTECTION_FLAGS_RX);
 }
 #endif
 
-void
-ExecutablePool::toggleAllCodeAsAccessible(bool accessible)
-{
-    char* begin = m_allocation.pages;
-    size_t size = m_freePtr - begin;
-
-    if (size) {
-        // N.B. Some systems, like 32bit Mac OS 10.6, implicitly add PROT_EXEC
-        // when mprotect'ing memory with any flag other than PROT_NONE. Be
-        // sure to use PROT_NONE when making inaccessible.
-        int flags = accessible ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_NONE;
-        if (mprotect(begin, size, flags))
-            MOZ_CRASH();
-    }
-}
--- a/js/src/jit/ExecutableAllocatorWin.cpp
+++ b/js/src/jit/ExecutableAllocatorWin.cpp
@@ -246,27 +246,11 @@ ExecutablePool::Allocation ExecutableAll
     return alloc;
 }
 
 void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
 {
     DeallocateExecutableMemory(alloc.pages, alloc.size, pageSize);
 }
 
-void
-ExecutablePool::toggleAllCodeAsAccessible(bool accessible)
-{
-    char* begin = m_allocation.pages;
-    size_t size = m_freePtr - begin;
-
-    if (size) {
-        // N.B. DEP is not on automatically in Windows XP, so be sure to use
-        // PAGE_NOACCESS instead of PAGE_READWRITE when making inaccessible.
-        DWORD oldProtect;
-        int flags = accessible ? PAGE_EXECUTE_READWRITE : PAGE_NOACCESS;
-        if (!VirtualProtect(begin, size, flags, &oldProtect))
-            MOZ_CRASH();
-    }
-}
-
 #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
 #error "ASSEMBLER_WX_EXCLUSIVE not yet suported on this platform."
 #endif
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -162,17 +162,17 @@ JitRuntime::JitRuntime()
     argumentsRectifierReturnAddr_(nullptr),
     parallelArgumentsRectifier_(nullptr),
     invalidator_(nullptr),
     debugTrapHandler_(nullptr),
     forkJoinGetSliceStub_(nullptr),
     baselineDebugModeOSRHandler_(nullptr),
     functionWrappers_(nullptr),
     osrTempData_(nullptr),
-    ionCodeProtected_(false),
+    mutatingBackedgeList_(false),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
     jitcodeGlobalTable_(nullptr)
 {
 }
 
 JitRuntime::~JitRuntime()
 {
     js_delete(functionWrappers_);
@@ -186,17 +186,16 @@ JitRuntime::~JitRuntime()
     MOZ_ASSERT_IF(jitcodeGlobalTable_, jitcodeGlobalTable_->empty());
     js_delete(jitcodeGlobalTable_);
 }
 
 bool
 JitRuntime::initialize(JSContext *cx)
 {
     MOZ_ASSERT(cx->runtime()->currentThreadHasExclusiveAccess());
-    MOZ_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock());
 
     AutoCompartment ac(cx, cx->atomsCompartment());
 
     IonContext ictx(cx, nullptr);
 
     execAlloc_ = cx->runtime()->getExecAlloc(cx);
     if (!execAlloc_)
         return false;
@@ -361,151 +360,42 @@ JitRuntime::freeOsrTempData()
 {
     js_free(osrTempData_);
     osrTempData_ = nullptr;
 }
 
 ExecutableAllocator *
 JitRuntime::createIonAlloc(JSContext *cx)
 {
-    MOZ_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock());
-
     ionAlloc_ = js_new<ExecutableAllocator>();
     if (!ionAlloc_)
         js_ReportOutOfMemory(cx);
     return ionAlloc_;
 }
 
 void
-JitRuntime::ensureIonCodeProtected(JSRuntime *rt)
-{
-    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
-
-    if (!rt->signalHandlersInstalled() || ionCodeProtected_ || !ionAlloc_)
-        return;
-
-    // Protect all Ion code in the runtime to trigger an access violation the
-    // next time any of it runs on the main thread.
-    ionAlloc_->toggleAllCodeAsAccessible(false);
-    ionCodeProtected_ = true;
-}
-
-bool
-JitRuntime::handleAccessViolation(JSRuntime *rt, void *faultingAddress)
-{
-    if (!rt->signalHandlersInstalled() || !ionAlloc_ || !ionAlloc_->codeContains((char *) faultingAddress))
-        return false;
-
-    // All places where the interrupt lock is taken must either ensure that Ion
-    // code memory won't be accessed within, or call ensureIonCodeAccessible to
-    // render the memory safe for accessing. Otherwise taking the lock below
-    // will deadlock the process.
-    MOZ_ASSERT(!rt->currentThreadOwnsInterruptLock());
-
-    // Taking this lock is necessary to prevent the interrupting thread from marking
-    // the memory as inaccessible while we are patching backedges. This will cause us
-    // to SEGV while still inside the signal handler, and the process will terminate.
-    JSRuntime::AutoLockForInterrupt lock(rt);
-
-    // Ion code in the runtime faulted after it was made inaccessible. Reset
-    // the code privileges and patch all loop backedges to perform an interrupt
-    // check instead.
-    ensureIonCodeAccessible(rt);
-    return true;
-}
-
-void
-JitRuntime::ensureIonCodeAccessible(JSRuntime *rt)
-{
-    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
-
-    // This can only be called on the main thread and while handling signals,
-    // which happens on a separate thread in OS X.
-#ifndef XP_MACOSX
-    MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
-#endif
-
-    if (ionCodeProtected_) {
-        ionAlloc_->toggleAllCodeAsAccessible(true);
-        ionCodeProtected_ = false;
-    }
-
-    if (rt->hasPendingInterrupt()) {
-        // 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
-        // we could be elsewhere, say repatching a jump for an IonCache).
-        // Patch all backedges in the runtime so they will invoke the interrupt
-        // handler the next time they execute.
-        patchIonBackedges(rt, BackedgeInterruptCheck);
-    }
-}
-
-void
 JitRuntime::patchIonBackedges(JSRuntime *rt, BackedgeTarget target)
 {
-#ifndef XP_MACOSX
-    MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
-#endif
+    MOZ_ASSERT_IF(target == BackedgeLoopHeader, mutatingBackedgeList_);
+    MOZ_ASSERT_IF(target == BackedgeInterruptCheck, !mutatingBackedgeList_);
 
     // Patch all loop backedges in Ion code so that they either jump to the
     // normal loop header or to an interrupt handler each time they run.
     for (InlineListIterator<PatchableBackedge> iter(backedgeList_.begin());
          iter != backedgeList_.end();
          iter++)
     {
         PatchableBackedge *patchableBackedge = *iter;
         if (target == BackedgeLoopHeader)
             PatchBackedge(patchableBackedge->backedge, patchableBackedge->loopHeader, target);
         else
             PatchBackedge(patchableBackedge->backedge, patchableBackedge->interruptCheck, target);
     }
 }
 
-void
-jit::RequestInterruptForIonCode(JSRuntime *rt, JSRuntime::InterruptMode mode)
-{
-    JitRuntime *jitRuntime = rt->jitRuntime();
-    if (!jitRuntime)
-        return;
-
-    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
-
-    // The mechanism for interrupting normal ion code varies depending on how
-    // the interrupt is being requested.
-    switch (mode) {
-      case JSRuntime::RequestInterruptMainThread:
-        // When requesting an interrupt from the main thread, Ion loop
-        // backedges can be patched directly. Make sure we don't segv while
-        // patching the backedges, to avoid deadlocking inside the signal
-        // handler.
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
-        jitRuntime->ensureIonCodeAccessible(rt);
-        break;
-
-      case JSRuntime::RequestInterruptAnyThread:
-        // When requesting an interrupt from off the main thread, protect
-        // Ion code memory so that the main thread will fault and enter a
-        // signal handler when trying to execute the code. The signal
-        // handler will unprotect the code and patch loop backedges so
-        // that the interrupt handler is invoked afterwards.
-        jitRuntime->ensureIonCodeProtected(rt);
-        break;
-
-      case JSRuntime::RequestInterruptAnyThreadDontStopIon:
-      case JSRuntime::RequestInterruptAnyThreadForkJoin:
-        // The caller does not require Ion code to be interrupted.
-        // Nothing more needs to be done.
-        break;
-
-      default:
-        MOZ_CRASH("Bad interrupt mode");
-    }
-}
-
 JitCompartment::JitCompartment()
   : stubCodes_(nullptr),
     baselineCallReturnAddr_(nullptr),
     baselineGetPropReturnAddr_(nullptr),
     baselineSetPropReturnAddr_(nullptr),
     stringConcatStub_(nullptr),
     parallelStringConcatStub_(nullptr),
     regExpExecStub_(nullptr),
@@ -865,30 +755,26 @@ JitCode::trace(JSTracer *trc)
         CompactBufferReader reader(start, start + dataRelocTableBytes_);
         MacroAssembler::TraceDataRelocations(trc, this, reader);
     }
 }
 
 void
 JitCode::finalize(FreeOp *fop)
 {
-    // Make sure this can't race with an interrupting thread, which may try
-    // to read the contents of the pool we are releasing references in.
-    MOZ_ASSERT(fop->runtime()->currentThreadOwnsInterruptLock());
-
     // If this jitcode has a bytecode map, de-register it.
     if (hasBytecodeMap_) {
         MOZ_ASSERT(fop->runtime()->jitRuntime()->hasJitcodeGlobalTable());
         fop->runtime()->jitRuntime()->getJitcodeGlobalTable()->removeEntry(raw());
     }
 
     // Buffer can be freed at any time hereafter. Catch use-after-free bugs.
     // Don't do this if the Ion code is protected, as the signal handler will
     // deadlock trying to reacquire the interrupt lock.
-    if (fop->runtime()->jitRuntime() && !fop->runtime()->jitRuntime()->ionCodeProtected())
+    if (fop->runtime()->jitRuntime())
         memset(code_, JS_SWEPT_CODE_PATTERN, bufferSize_);
     code_ = nullptr;
 
     // Code buffers are stored inside JSC pools.
     // Pools are refcounted. Releasing the pool may free it.
     if (pool_) {
         // Horrible hack: if we are using perf integration, we don't
         // want to reuse code addresses, so we just leak the memory instead.
@@ -1139,16 +1025,19 @@ IonScript::copyCallTargetEntries(JSScrip
         callTargetList()[i] = callTargets[i];
 }
 
 void
 IonScript::copyPatchableBackedges(JSContext *cx, JitCode *code,
                                   PatchableBackedgeInfo *backedges,
                                   MacroAssembler &masm)
 {
+    JitRuntime *jrt = cx->runtime()->jitRuntime();
+    JitRuntime::AutoMutateBackedges amb(jrt);
+
     for (size_t i = 0; i < backedgeEntries_; i++) {
         PatchableBackedgeInfo &info = backedges[i];
         PatchableBackedge *patchableBackedge = &backedgeList()[i];
 
         // Convert to actual offsets for the benefit of the ARM backend.
         info.backedge.fixup(&masm);
         uint32_t loopHeaderOffset = masm.actualOffset(info.loopHeader->offset());
         uint32_t interruptCheckOffset = masm.actualOffset(info.interruptCheck->offset());
@@ -1162,17 +1051,17 @@ IonScript::copyPatchableBackedges(JSCont
         // 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())
             PatchBackedge(backedge, interruptCheck, JitRuntime::BackedgeInterruptCheck);
         else
             PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader);
 
-        cx->runtime()->jitRuntime()->addPatchableBackedge(patchableBackedge);
+        jrt->addPatchableBackedge(patchableBackedge);
     }
 }
 
 void
 IonScript::copySafepointIndices(const SafepointIndex *si, MacroAssembler &masm)
 {
     // Jumps in the caches reflect the offset of those jumps in the compiled
     // code, not the absolute positions of the jumps. Update according to the
@@ -1353,21 +1242,20 @@ IonScript::unlinkFromRuntime(FreeOp *fop
 
         fop->delete_(dependentAsmJSModules);
         dependentAsmJSModules = nullptr;
     }
 
     // The writes to the executable buffer below may clobber backedge jumps, so
     // make sure that those backedges are unlinked from the runtime and not
     // reclobbered with garbage if an interrupt is requested.
-    JSRuntime *rt = fop->runtime();
-    for (size_t i = 0; i < backedgeEntries_; i++) {
-        PatchableBackedge *backedge = &backedgeList()[i];
-        rt->jitRuntime()->removePatchableBackedge(backedge);
-    }
+    JitRuntime *jrt = fop->runtime()->jitRuntime();
+    JitRuntime::AutoMutateBackedges amb(jrt);
+    for (size_t i = 0; i < backedgeEntries_; i++)
+        jrt->removePatchableBackedge(&backedgeList()[i]);
 
     // Clear the list of backedges, so that this method is idempotent. It is
     // called during destruction, and may be additionally called when the
     // script is invalidated.
     backedgeEntries_ = 0;
 }
 
 void
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -196,18 +196,16 @@ NumLocalsAndArgs(JSScript *script)
 void ForbidCompilation(JSContext *cx, JSScript *script);
 void ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode);
 
 void PurgeCaches(JSScript *script);
 size_t SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf);
 void DestroyIonScripts(FreeOp *fop, JSScript *script);
 void TraceIonScripts(JSTracer* trc, JSScript *script);
 
-void RequestInterruptForIonCode(JSRuntime *rt, JSRuntime::InterruptMode mode);
-
 bool RematerializeAllFrames(JSContext *cx, JSCompartment *comp);
 bool UpdateForDebugMode(JSContext *maybecx, JSCompartment *comp,
                         AutoDebugModeInvalidation &invalidate);
 
 bool JitSupportsFloatingPoint();
 bool JitSupportsSimd();
 
 } // namespace jit
--- a/js/src/jit/IonLinker.h
+++ b/js/src/jit/IonLinker.h
@@ -77,20 +77,16 @@ class Linker
     }
 
     template <AllowGC allowGC>
     JitCode *newCode(JSContext *cx, CodeKind kind) {
         return newCode<allowGC>(cx, cx->runtime()->jitRuntime()->execAlloc(), kind);
     }
 
     JitCode *newCodeForIonScript(JSContext *cx) {
-        // The caller must lock the runtime against interrupt requests, as the
-        // thread requesting an interrupt may use the executable allocator below.
-        MOZ_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock());
-
         ExecutableAllocator *alloc = cx->runtime()->jitRuntime()->getIonAlloc(cx);
         if (!alloc)
             return nullptr;
 
         return newCode<CanGC>(cx, alloc, ION_CODE);
     }
 };
 
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -212,22 +212,21 @@ class JitRuntime
     typedef WeakCache<const VMFunction *, JitCode *> VMWrapperMap;
     VMWrapperMap *functionWrappers_;
 
     // Buffer for OSR from baseline to Ion. To avoid holding on to this for
     // too long, it's also freed in JitCompartment::mark and in EnterBaseline
     // (after returning from JIT code).
     uint8_t *osrTempData_;
 
-    // Whether all Ion code in the runtime is protected, and will fault if it
-    // is accessed.
-    bool ionCodeProtected_;
-
-    // If signal handlers are installed, this contains all loop backedges for
-    // IonScripts in the runtime.
+    // List of all backedges in all Ion code. The backedge edge list is accessed
+    // asynchronously when the main thread is paused and mutatingBackedgeList_
+    // is false. Thus, the list must only be mutated while mutatingBackedgeList_
+    // is true.
+    volatile bool mutatingBackedgeList_;
     InlineList<PatchableBackedge> backedgeList_;
 
     // In certain cases, we want to optimize certain opcodes to typed instructions,
     // to avoid carrying an extra register to feed into an unbox. Unfortunately,
     // that's not always possible. For example, a GetPropertyCacheT could return a
     // typed double, but if it takes its out-of-line path, it could return an
     // object, and trigger invalidation. The invalidation bailout will consider the
     // return value to be a double, and create a garbage Value.
@@ -269,53 +268,59 @@ class JitRuntime
     uint8_t *allocateOsrTempData(size_t size);
     void freeOsrTempData();
 
     static void Mark(JSTracer *trc);
 
     ExecutableAllocator *execAlloc() const {
         return execAlloc_;
     }
-
     ExecutableAllocator *getIonAlloc(JSContext *cx) {
-        MOZ_ASSERT(cx->runtime()->currentThreadOwnsInterruptLock());
         return ionAlloc_ ? ionAlloc_ : createIonAlloc(cx);
     }
-
     ExecutableAllocator *ionAlloc(JSRuntime *rt) {
-        MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
         return ionAlloc_;
     }
-
     bool hasIonAlloc() const {
         return !!ionAlloc_;
     }
 
-    bool ionCodeProtected() {
-        return ionCodeProtected_;
+    class AutoMutateBackedges
+    {
+        JitRuntime *jrt_;
+      public:
+        AutoMutateBackedges(JitRuntime *jrt) : jrt_(jrt) {
+            MOZ_ASSERT(!jrt->mutatingBackedgeList_);
+            jrt->mutatingBackedgeList_ = true;
+        }
+        ~AutoMutateBackedges() {
+            MOZ_ASSERT(jrt_->mutatingBackedgeList_);
+            jrt_->mutatingBackedgeList_ = false;
+        }
+    };
+
+    bool mutatingBackedgeList() const {
+        return mutatingBackedgeList_;
     }
-
     void addPatchableBackedge(PatchableBackedge *backedge) {
+        MOZ_ASSERT(mutatingBackedgeList_);
         backedgeList_.pushFront(backedge);
     }
     void removePatchableBackedge(PatchableBackedge *backedge) {
+        MOZ_ASSERT(mutatingBackedgeList_);
         backedgeList_.remove(backedge);
     }
 
     enum BackedgeTarget {
         BackedgeLoopHeader,
         BackedgeInterruptCheck
     };
 
-    void ensureIonCodeProtected(JSRuntime *rt);
-    void ensureIonCodeAccessible(JSRuntime *rt);
     void patchIonBackedges(JSRuntime *rt, BackedgeTarget target);
 
-    bool handleAccessViolation(JSRuntime *rt, void *faultingAddress);
-
     JitCode *getVMWrapper(const VMFunction &f) const;
     JitCode *debugTrapHandler(JSContext *cx);
     JitCode *getBaselineDebugModeOSRHandler(JSContext *cx);
     void *getBaselineDebugModeOSRHandlerAddress(JSContext *cx, bool popFrameReg);
 
     JitCode *getGenericBailoutHandler(ExecutionMode mode) const {
         switch (mode) {
           case SequentialExecution: return bailoutHandler_;
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -517,25 +517,21 @@ SetProperty(JSContext *cx, HandleObject 
     return JSObject::setGeneric(cx, obj, obj, id, &v, strict);
 }
 
 bool
 InterruptCheck(JSContext *cx)
 {
     gc::MaybeVerifyBarriers(cx);
 
-    // Fix loop backedges so that they do not invoke the interrupt again.
-    // No lock is held here and it's possible we could segv in the middle here
-    // and end up with a state where some fraction of the backedges point to
-    // the interrupt handler and some don't. This is ok since the interrupt
-    // is definitely about to be handled; if there are still backedges
-    // afterwards which point to the interrupt handler, the next time they are
-    // taken the backedges will just be reset again.
-    cx->runtime()->jitRuntime()->patchIonBackedges(cx->runtime(),
-                                                   JitRuntime::BackedgeLoopHeader);
+    {
+        JitRuntime *jrt = cx->runtime()->jitRuntime();
+        JitRuntime::AutoMutateBackedges amb(jrt);
+        jrt->patchIonBackedges(cx->runtime(), JitRuntime::BackedgeLoopHeader);
+    }
 
     return CheckForInterrupt(cx);
 }
 
 void *
 MallocWrapper(JSRuntime *rt, size_t nbytes)
 {
     return rt->pod_malloc<uint8_t>(nbytes);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5102,17 +5102,17 @@ JS_PUBLIC_API(JSInterruptCallback)
 JS_GetInterruptCallback(JSRuntime *rt)
 {
     return rt->interruptCallback;
 }
 
 JS_PUBLIC_API(void)
 JS_RequestInterruptCallback(JSRuntime *rt)
 {
-    rt->requestInterrupt(JSRuntime::RequestInterruptAnyThread);
+    rt->requestInterrupt(JSRuntime::RequestInterruptUrgent);
 }
 
 JS_PUBLIC_API(bool)
 JS_IsRunning(JSContext *cx)
 {
     return cx->currentlyRunning();
 }
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -288,17 +288,16 @@ struct ThreadSafeContext : ContextFriend
     const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; }
     PropertyName *emptyString() { return runtime_->emptyString; }
     FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
     void *runtimeAddressForJit() { return runtime_; }
     void *runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
     void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void *stackLimitAddressForJitCode(StackKind kind);
     size_t gcSystemPageSize() { return gc::SystemPageSize(); }
-    bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); }
     bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); }
     bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
     bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; }
 
     // Thread local data that may be accessed freely.
     DtoaState *dtoaState() {
         return perThreadData->dtoaState;
     }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -28,16 +28,17 @@
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
+using namespace js::jit;
 
 using mozilla::DebugOnly;
 
 JSCompartment::JSCompartment(Zone *zone, const JS::CompartmentOptions &options = JS::CompartmentOptions())
   : options_(options),
     zone_(zone),
     runtime_(zone->runtimeFromMainThread()),
     principals(nullptr),
@@ -120,27 +121,27 @@ JSCompartment::init(JSContext *cx)
 
 jit::JitRuntime *
 JSRuntime::createJitRuntime(JSContext *cx)
 {
     // The shared stubs are created in the atoms compartment, which may be
     // accessed by other threads with an exclusive context.
     AutoLockForExclusiveAccess atomsLock(cx);
 
-    // The runtime will only be created on its owning thread, but reads of a
-    // runtime's jitRuntime() can occur when another thread is requesting an
-    // interrupt.
-    AutoLockForInterrupt lock(this);
-
     MOZ_ASSERT(!jitRuntime_);
 
-    jitRuntime_ = cx->new_<jit::JitRuntime>();
+    jit::JitRuntime *jrt = cx->new_<jit::JitRuntime>();
+    if (!jrt)
+        return nullptr;
 
-    if (!jitRuntime_)
-        return nullptr;
+    // Protect jitRuntime_ from being observed (by InterruptRunningJitCode)
+    // while it is being initialized. Unfortunately, initialization depends on
+    // jitRuntime_ being non-null, so we can't just wait to assign jitRuntime_.
+    JitRuntime::AutoMutateBackedges amb(jrt);
+    jitRuntime_ = jrt;
 
     if (!jitRuntime_->initialize(cx)) {
         js_delete(jitRuntime_);
         jitRuntime_ = nullptr;
 
         JSCompartment *comp = cx->runtime()->atomsCompartment();
         if (comp->jitCompartment_) {
             js_delete(comp->jitCompartment_);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -640,22 +640,17 @@ FinalizeArenas(FreeOp *fop,
         return FinalizeTypedArenas<JSString>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_FAT_INLINE_STRING:
         return FinalizeTypedArenas<JSFatInlineString>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_EXTERNAL_STRING:
         return FinalizeTypedArenas<JSExternalString>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_SYMBOL:
         return FinalizeTypedArenas<JS::Symbol>(fop, src, dest, thingKind, budget, keepArenas);
       case FINALIZE_JITCODE:
-      {
-        // JitCode finalization may release references on an executable
-        // allocator that is accessed when requesting interrupts.
-        JSRuntime::AutoLockForInterrupt lock(fop->runtime());
         return FinalizeTypedArenas<jit::JitCode>(fop, src, dest, thingKind, budget, keepArenas);
-      }
       default:
         MOZ_CRASH("Invalid alloc kind");
     }
 }
 
 static inline Chunk *
 AllocChunk(JSRuntime *rt)
 {
@@ -1058,17 +1053,17 @@ class js::gc::AutoMaybeStartBackgroundAl
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     void tryToStartBackgroundAllocation(JSRuntime *rt) {
         runtime = rt;
     }
 
     ~AutoMaybeStartBackgroundAllocation() {
-        if (runtime && !runtime->currentThreadOwnsInterruptLock())
+        if (runtime)
             runtime->gc.startBackgroundAllocTaskIfIdle();
     }
 };
 
 Chunk *
 GCRuntime::pickChunk(const AutoLockGC &lock,
                      AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
 {
@@ -2995,29 +2990,29 @@ js::MarkCompartmentActive(InterpreterFra
 void
 GCRuntime::requestMajorGC(JS::gcreason::Reason reason)
 {
     if (majorGCRequested)
         return;
 
     majorGCRequested = true;
     majorGCTriggerReason = reason;
-    rt->requestInterrupt(JSRuntime::RequestInterruptMainThread);
+    rt->requestInterrupt(JSRuntime::RequestInterruptUrgent);
 }
 
 void
 GCRuntime::requestMinorGC(JS::gcreason::Reason reason)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
     if (minorGCRequested)
         return;
 
     minorGCRequested = true;
     minorGCTriggerReason = reason;
-    rt->requestInterrupt(JSRuntime::RequestInterruptMainThread);
+    rt->requestInterrupt(JSRuntime::RequestInterruptUrgent);
 }
 
 bool
 GCRuntime::triggerGC(JS::gcreason::Reason reason)
 {
     /* Wait till end of parallel section to trigger GC. */
     if (InParallelSection()) {
         ForkJoinContext::current()->requestGC(reason);
@@ -3026,20 +3021,16 @@ GCRuntime::triggerGC(JS::gcreason::Reaso
 
     /*
      * Don't trigger GCs if this is being called off the main thread from
      * onTooMuchMalloc().
      */
     if (!CurrentThreadCanAccessRuntime(rt))
         return false;
 
-    /* Don't trigger GCs when allocating under the interrupt callback lock. */
-    if (rt->currentThreadOwnsInterruptLock())
-        return false;
-
     /* GC is already running. */
     if (rt->isHeapCollecting())
         return false;
 
     JS::PrepareForFullGC(rt);
     requestMajorGC(reason);
     return true;
 }
@@ -3089,20 +3080,16 @@ GCRuntime::triggerZoneGC(Zone *zone, JS:
         ForkJoinContext::current()->requestZoneGC(zone, reason);
         return true;
     }
 
     /* Zones in use by a thread with an exclusive context can't be collected. */
     if (zone->usedByExclusiveThread)
         return false;
 
-    /* Don't trigger GCs when allocating under the interrupt callback lock. */
-    if (rt->currentThreadOwnsInterruptLock())
-        return false;
-
     /* GC is already running. */
     if (rt->isHeapCollecting())
         return false;
 
 #ifdef JS_GC_ZEAL
     if (zealMode == ZealAllocValue) {
         triggerGC(reason);
         return true;
@@ -5339,20 +5326,18 @@ GCRuntime::endSweepPhase(bool lastGC)
          */
         if (isFull)
             SweepScriptData(rt);
 
         /* Clear out any small pools that we're hanging on to. */
         if (jit::ExecutableAllocator *execAlloc = rt->maybeExecAlloc())
             execAlloc->purge();
 
-        if (rt->jitRuntime() && rt->jitRuntime()->hasIonAlloc()) {
-            JSRuntime::AutoLockForInterrupt lock(rt);
+        if (rt->jitRuntime() && rt->jitRuntime()->hasIonAlloc())
             rt->jitRuntime()->ionAlloc(rt)->purge();
-        }
 
         /*
          * This removes compartments from rt->compartment, so we do it last to make
          * sure we don't miss sweeping any compartments.
          */
         if (!lastGC)
             sweepZones(&fop, lastGC);
     }
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -1662,19 +1662,17 @@ ForkJoinShared::setAbortFlagDueToInterru
 void
 ForkJoinShared::setAbortFlagAndRequestInterrupt(bool fatal)
 {
     AutoLockMonitor lock(*this);
 
     abort_ = true;
     fatal_ = fatal_ || fatal;
 
-    // Note: The ForkJoin trigger here avoids the expensive memory protection needed to
-    // interrupt Ion code compiled for sequential execution.
-    cx_->runtime()->requestInterrupt(JSRuntime::RequestInterruptAnyThreadForkJoin);
+    cx_->runtime()->requestInterrupt(JSRuntime::RequestInterruptCanWait);
 }
 
 void
 ForkJoinShared::requestGC(JS::gcreason::Reason reason)
 {
     AutoLockMonitor lock(*this);
 
     gcZone_ = nullptr;
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1060,17 +1060,17 @@ HelperThread::handleIonWorkload()
     FinishOffThreadIonCompile(ionBuilder);
     ionBuilder = nullptr;
     pause = false;
 
     // Ping the main thread so that the compiled code can be incorporated
     // at the next interrupt callback. Don't interrupt Ion code for this, as
     // this incorporation can be delayed indefinitely without affecting
     // performance as long as the main thread is actually executing Ion code.
-    rt->requestInterrupt(JSRuntime::RequestInterruptAnyThreadDontStopIon);
+    rt->requestInterrupt(JSRuntime::RequestInterruptCanWait);
 
     // Notify the main thread in case it is waiting for the compilation to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
 
     // When finishing Ion compilation jobs, we can start unpausing compilation
     // threads that were paused to restrict the number of active compilations.
     // Only unpause one at a time, to make sure we don't exceed the restriction.
     // Since threads are currently only paused for Ion compilations, this
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -22,16 +22,17 @@
 #include "jsatom.h"
 #include "jsdtoa.h"
 #include "jsgc.h"
 #include "jsmath.h"
 #include "jsnativestack.h"
 #include "jsobj.h"
 #include "jsscript.h"
 #include "jswatchpoint.h"
+#include "jswin.h"
 #include "jswrapper.h"
 
 #include "asmjs/AsmJSSignalHandlers.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/JitCompartment.h"
 #include "jit/mips/Simulator-mips.h"
 #include "jit/PcScriptCache.h"
 #include "js/MemoryMetrics.h"
@@ -135,28 +136,27 @@ JSRuntime::JSRuntime(JSRuntime *parentRu
 #endif
     ),
     mainThread(this),
     parentRuntime(parentRuntime),
     interrupt_(false),
     interruptPar_(false),
     handlingSignal(false),
     interruptCallback(nullptr),
-    interruptLock(nullptr),
-    interruptLockOwner(nullptr),
     exclusiveAccessLock(nullptr),
     exclusiveAccessOwner(nullptr),
     mainThreadHasExclusiveAccess(false),
     numExclusiveThreads(0),
     numCompartments(0),
     localeCallbacks(nullptr),
     defaultLocale(nullptr),
     defaultVersion_(JSVERSION_DEFAULT),
     futexAPI_(nullptr),
     ownerThread_(nullptr),
+    ownerThreadNative_(0),
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     execAlloc_(nullptr),
     jitRuntime_(nullptr),
     selfHostingGlobal_(nullptr),
     nativeStackBase(GetNativeStackBase()),
     cxCallback(nullptr),
     destroyCompartmentCallback(nullptr),
     destroyZoneCallback(nullptr),
@@ -251,19 +251,29 @@ SignalBasedTriggersDisabled()
   return !!getenv("JS_DISABLE_SLOW_SCRIPT_SIGNALS") || !!getenv("JS_NO_SIGNALS");
 }
 
 bool
 JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
 {
     ownerThread_ = PR_GetCurrentThread();
 
-    interruptLock = PR_NewLock();
-    if (!interruptLock)
-        return false;
+    // Get a platform-native handle for the owner thread, used by
+    // js::InterruptRunningJitCode to halt the runtime's main thread.
+#ifdef XP_WIN
+    size_t openFlags = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME;
+    HANDLE self = OpenThread(openFlags, false, GetCurrentThreadId());
+    if (!self)
+        MOZ_CRASH("Unable to open thread handle");
+    static_assert(sizeof(HANDLE) <= sizeof(ownerThreadNative_), "need bigger field");
+    ownerThreadNative_ = (size_t)self;
+#else
+    static_assert(sizeof(pthread_t) <= sizeof(ownerThreadNative_), "need bigger field");
+    ownerThreadNative_ = (size_t)pthread_self();
+#endif
 
     exclusiveAccessLock = PR_NewLock();
     if (!exclusiveAccessLock)
         return false;
 
     if (!mainThread.init())
         return false;
 
@@ -320,17 +330,17 @@ JSRuntime::init(uint32_t maxbytes, uint3
     simulatorRuntime_ = js::jit::CreateSimulatorRuntime();
     if (!simulatorRuntime_)
         return false;
 #endif
 
     jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
     jitSupportsSimd = js::jit::JitSupportsSimd();
 
-    signalHandlersInstalled_ = EnsureAsmJSSignalHandlersInstalled(this);
+    signalHandlersInstalled_ = EnsureSignalHandlersInstalled(this);
     canUseSignalHandlers_ = signalHandlersInstalled_ && !SignalBasedTriggersDisabled();
 
     if (!spsProfiler.init())
         return false;
 
     return true;
 }
 
@@ -386,20 +396,16 @@ JSRuntime::~JSRuntime()
     MOZ_ASSERT(!exclusiveAccessOwner);
     if (exclusiveAccessLock)
         PR_DestroyLock(exclusiveAccessLock);
 
     // Avoid bogus asserts during teardown.
     MOZ_ASSERT(!numExclusiveThreads);
     mainThreadHasExclusiveAccess = true;
 
-    MOZ_ASSERT(!interruptLockOwner);
-    if (interruptLock)
-        PR_DestroyLock(interruptLock);
-
     /*
      * Even though all objects in the compartment are dead, we may have keep
      * some filenames around because of gcKeepAtoms.
      */
     FreeScriptData(this);
 
 #ifdef DEBUG
     /* Don't hurt everyone in leaky ol' Mozilla with a fatal MOZ_ASSERT! */
@@ -439,16 +445,21 @@ JSRuntime::~JSRuntime()
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     js::jit::DestroySimulatorRuntime(simulatorRuntime_);
 #endif
 
     DebugOnly<size_t> oldCount = liveRuntimesCount--;
     MOZ_ASSERT(oldCount > 0);
 
     js::TlsPerThreadData.set(nullptr);
+
+#ifdef XP_WIN
+    if (ownerThreadNative_)
+        CloseHandle((HANDLE)ownerThreadNative_);
+#endif
 }
 
 void
 NewObjectCache::clearNurseryObjects(JSRuntime *rt)
 {
 #ifdef JSGC_GENERATIONAL
     for (unsigned i = 0; i < mozilla::ArrayLength(entries); ++i) {
         Entry &e = entries[i];
@@ -495,23 +506,19 @@ JSRuntime::addSizeOfIncludingThis(mozill
     rtSizes->compressedSourceSet += compressedSourceSet.sizeOfExcludingThis(mallocSizeOf);
 
     rtSizes->scriptData += scriptDataTable().sizeOfExcludingThis(mallocSizeOf);
     for (ScriptDataTable::Range r = scriptDataTable().all(); !r.empty(); r.popFront())
         rtSizes->scriptData += mallocSizeOf(r.front());
 
     if (execAlloc_)
         execAlloc_->addSizeOfCode(&rtSizes->code);
-    {
-        AutoLockForInterrupt lock(this);
-        if (jitRuntime()) {
-            if (jit::ExecutableAllocator *ionAlloc = jitRuntime()->ionAlloc(this))
-                ionAlloc->addSizeOfCode(&rtSizes->code);
-        }
-    }
+
+    if (jitRuntime() && jitRuntime()->ionAlloc(this))
+        jitRuntime()->ionAlloc(this)->addSizeOfCode(&rtSizes->code);
 
     rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf);
 #ifdef JSGC_GENERATIONAL
     rtSizes->gc.nurseryCommitted += gc.nursery.sizeOfHeapCommitted();
     rtSizes->gc.nurseryDecommitted += gc.nursery.sizeOfHeapDecommitted();
     rtSizes->gc.nurseryHugeSlots += gc.nursery.sizeOfHugeSlots(mallocSizeOf);
     gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc);
 #endif
@@ -606,21 +613,18 @@ PerThreadData::initJitStackLimitPar(uint
 
 void
 JSRuntime::requestInterrupt(InterruptMode mode)
 {
     interrupt_ = true;
     interruptPar_ = true;
     mainThread.jitStackLimit_ = UINTPTR_MAX;
 
-    if (canUseSignalHandlers()) {
-        AutoLockForInterrupt lock(this);
-        RequestInterruptForAsmJSCode(this, mode);
-        jit::RequestInterruptForIonCode(this, mode);
-    }
+    if (mode == JSRuntime::RequestInterruptUrgent)
+        InterruptRunningJitCode(this);
 }
 
 bool
 JSRuntime::handleInterrupt(JSContext *cx)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
     if (interrupt_ || mainThread.jitStackLimit_ == UINTPTR_MAX) {
         interrupt_ = false;
@@ -831,18 +835,16 @@ JSRuntime::assertCanLock(RuntimeLock whi
     // In the switch below, each case falls through to the one below it. None
     // of the runtime locks are reentrant, and when multiple locks are acquired
     // it must be done in the order below.
     switch (which) {
       case ExclusiveAccessLock:
         MOZ_ASSERT(exclusiveAccessOwner != PR_GetCurrentThread());
       case HelperThreadStateLock:
         MOZ_ASSERT(!HelperThreadState().isLocked());
-      case InterruptLock:
-        MOZ_ASSERT(!currentThreadOwnsInterruptLock());
       case GCLock:
         gc.assertCanLock();
         break;
       default:
         MOZ_CRASH();
     }
 }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -447,17 +447,16 @@ AtomStateOffsetToName(const JSAtomState 
 }
 
 // There are several coarse locks in the enum below. These may be either
 // per-runtime or per-process. When acquiring more than one of these locks,
 // the acquisition must be done in the order below to avoid deadlocks.
 enum RuntimeLock {
     ExclusiveAccessLock,
     HelperThreadStateLock,
-    InterruptLock,
     GCLock
 };
 
 #ifdef DEBUG
 void AssertCurrentThreadCanLock(RuntimeLock which);
 #else
 inline void AssertCurrentThreadCanLock(RuntimeLock which) {}
 #endif
@@ -536,24 +535,16 @@ class PerThreadData : public PerThreadDa
 
     // Information about the heap allocated backtrack stack used by RegExp JIT code.
     irregexp::RegExpStack regexpStack;
 
 #ifdef JS_TRACE_LOGGING
     TraceLogger         *traceLogger;
 #endif
 
-    /*
-     * asm.js maintains a stack of AsmJSModule activations (see AsmJS.h). This
-     * stack is used by JSRuntime::requestInterrupt to stop long-running asm.js
-     * without requiring dynamic polling operations in the generated
-     * code. Since requestInterrupt may run on a separate thread than the
-     * JSRuntime's owner thread all reads/writes must be synchronized (by
-     * rt->interruptLock).
-     */
   private:
     friend class js::Activation;
     friend class js::ActivationIterator;
     friend class js::jit::JitActivation;
     friend class js::AsmJSActivation;
 #ifdef DEBUG
     friend void js::AssertCurrentThreadCanLock(RuntimeLock which);
 #endif
@@ -561,21 +552,21 @@ class PerThreadData : public PerThreadDa
     /*
      * Points to the most recent activation running on the thread.
      * See Activation comment in vm/Stack.h.
      */
     js::Activation *activation_;
 
     /*
      * Points to the most recent profiling activation running on the
-     * thread.  Protected by rt->interruptLock.
+     * thread.
      */
     js::Activation * volatile profilingActivation_;
 
-    /* See AsmJSActivation comment. Protected by rt->interruptLock. */
+    /* See AsmJSActivation comment. */
     js::AsmJSActivation * volatile asmJSActivationStack_;
 
     /* Pointer to the current AutoFlushICache. */
     js::jit::AutoFlushICache *autoFlushICache_;
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     js::jit::Simulator *simulator_;
     uintptr_t simulatorStackLimit_;
@@ -709,20 +700,18 @@ struct JSRuntime : public JS::shadow::Ru
     JSRuntime *parentRuntime;
 
   private:
     mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
     mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptPar_;
   public:
 
     enum InterruptMode {
-        RequestInterruptMainThread,
-        RequestInterruptAnyThread,
-        RequestInterruptAnyThreadDontStopIon,
-        RequestInterruptAnyThreadForkJoin
+        RequestInterruptUrgent,
+        RequestInterruptCanWait
     };
 
     // 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
@@ -767,47 +756,16 @@ struct JSRuntime : public JS::shadow::Ru
 #ifdef DEBUG
     void assertCanLock(js::RuntimeLock which);
 #else
     void assertCanLock(js::RuntimeLock which) {}
 #endif
 
   private:
     /*
-     * Lock taken when triggering an interrupt from another thread.
-     * Protects all data that is touched in this process.
-     */
-    PRLock *interruptLock;
-    PRThread *interruptLockOwner;
-
-  public:
-    class AutoLockForInterrupt {
-        JSRuntime *rt;
-      public:
-        explicit AutoLockForInterrupt(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : rt(rt) {
-            MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-            rt->assertCanLock(js::InterruptLock);
-            PR_Lock(rt->interruptLock);
-            rt->interruptLockOwner = PR_GetCurrentThread();
-        }
-        ~AutoLockForInterrupt() {
-            MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
-            rt->interruptLockOwner = nullptr;
-            PR_Unlock(rt->interruptLock);
-        }
-
-        MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-    };
-
-    bool currentThreadOwnsInterruptLock() {
-        return interruptLockOwner == PR_GetCurrentThread();
-    }
-
-  private:
-    /*
      * Lock taken when using per-runtime or per-zone data that could otherwise
      * be accessed simultaneously by both the main thread and another thread
      * with an ExclusiveContext.
      *
      * Locking this only occurs if there is actually a thread other than the
      * main thread with an ExclusiveContext which could access such data.
      */
     PRLock *exclusiveAccessLock;
@@ -847,19 +805,24 @@ struct JSRuntime : public JS::shadow::Ru
     JSVersion defaultVersion_;
 
     /* Futex API, if installed */
     JS::PerRuntimeFutexAPI *futexAPI_;
 
   private:
     /* See comment for JS_AbortIfWrongThread in jsapi.h. */
     void *ownerThread_;
+    size_t ownerThreadNative_;
     friend bool js::CurrentThreadCanAccessRuntime(JSRuntime *rt);
   public:
 
+    size_t ownerThreadNative() const {
+        return ownerThreadNative_;
+    }
+
     /* Temporary arena pool used while compiling and decompiling. */
     static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024;
     js::LifoAlloc tempLifoAlloc;
 
   private:
     /*
      * Both of these allocators are used for regular expression code which is shared at the
      * thread-data level.
@@ -1084,26 +1047,25 @@ struct JSRuntime : public JS::shadow::Ru
     /* Client opaque pointers */
     void                *data;
 
 #ifdef XP_MACOSX
     js::AsmJSMachExceptionHandler asmJSMachExceptionHandler;
 #endif
 
   private:
-    // Whether asm.js signal handlers have been installed and can be used for
-    // performing interrupt checks in loops.
+    // Whether EnsureSignalHandlersInstalled succeeded in installing all the
+    // relevant handlers for this platform.
     bool signalHandlersInstalled_;
+
     // Whether we should use them or they have been disabled for making
     // debugging easier. If signal handlers aren't installed, it is set to false.
     bool canUseSignalHandlers_;
+
   public:
-    bool signalHandlersInstalled() const {
-        return signalHandlersInstalled_;
-    }
     bool canUseSignalHandlers() const {
         return canUseSignalHandlers_;
     }
     void setCanUseSignalHandlers(bool enable) {
         canUseSignalHandlers_ = signalHandlersInstalled_ && enable;
     }
 
   private:
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1553,21 +1553,17 @@ AsmJSActivation::AsmJSActivation(JSConte
         profiler_ = &cx->runtime()->spsProfiler;
         profiler_->enterAsmJS("asm.js code :0", this);
     }
 
     prevAsmJSForModule_ = module.activation();
     module.activation() = this;
 
     prevAsmJS_ = cx->mainThread().asmJSActivationStack_;
-
-    {
-        JSRuntime::AutoLockForInterrupt lock(cx->runtime());
-        cx->mainThread().asmJSActivationStack_ = this;
-    }
+    cx->mainThread().asmJSActivationStack_ = this;
 
     // Now that the AsmJSActivation is fully initialized, make it visible to
     // asynchronous profiling.
     registerProfiling();
 }
 
 AsmJSActivation::~AsmJSActivation()
 {
@@ -1580,17 +1576,16 @@ AsmJSActivation::~AsmJSActivation()
     MOZ_ASSERT(fp_ == nullptr);
 
     MOZ_ASSERT(module_.activation() == this);
     module_.activation() = prevAsmJSForModule_;
 
     JSContext *cx = cx_->asJSContext();
     MOZ_ASSERT(cx->mainThread().asmJSActivationStack_ == this);
 
-    JSRuntime::AutoLockForInterrupt lock(cx->runtime());
     cx->mainThread().asmJSActivationStack_ = prevAsmJS_;
 }
 
 InterpreterFrameIterator &
 InterpreterFrameIterator::operator++()
 {
     MOZ_ASSERT(!done());
     if (fp_ != activation_->entryFrame_) {
@@ -1604,25 +1599,23 @@ InterpreterFrameIterator::operator++()
     }
     return *this;
 }
 
 void
 Activation::registerProfiling()
 {
     MOZ_ASSERT(isProfiling());
-    JSRuntime::AutoLockForInterrupt lock(cx_->asJSContext()->runtime());
     cx_->perThreadData->profilingActivation_ = this;
 }
 
 void
 Activation::unregisterProfiling()
 {
     MOZ_ASSERT(isProfiling());
-    JSRuntime::AutoLockForInterrupt lock(cx_->asJSContext()->runtime());
     MOZ_ASSERT(cx_->perThreadData->profilingActivation_ == this);
     cx_->perThreadData->profilingActivation_ = prevProfiling_;
 }
 
 ActivationIterator::ActivationIterator(JSRuntime *rt)
   : jitTop_(rt->mainThread.jitTop),
     activation_(rt->mainThread.activation_)
 {