Title: [216808] trunk
Revision
216808
Author
[email protected]
Date
2017-05-12 18:30:13 -0700 (Fri, 12 May 2017)

Log Message

Use Mach exceptions instead of signals where possible
https://bugs.webkit.org/show_bug.cgi?id=171865

Reviewed by Mark Lam.

Source/_javascript_Core:

This patch adds some new JSC options. The first is an option that
enables or disables web assembly tier up. The second controls
whether or not we use mach exceptions (where available).

* API/tests/ExecutionTimeLimitTest.cpp:
(dispatchTermitateCallback):
(testExecutionTimeLimit):
* runtime/JSLock.cpp:
(JSC::JSLock::didAcquireLock):
* runtime/Options.cpp:
(JSC::overrideDefaults):
(JSC::Options::initialize):
* runtime/Options.h:
* runtime/VMTraps.cpp:
(JSC::SignalContext::SignalContext):
(JSC::SignalContext::adjustPCToPointToTrappingInstruction):
(JSC::installSignalHandler):
(JSC::VMTraps::SignalSender::send):
* tools/SigillCrashAnalyzer.cpp:
(JSC::SignalContext::SignalContext):
(JSC::SignalContext::dump):
(JSC::installCrashHandler):
* wasm/WasmBBQPlan.cpp:
(JSC::Wasm::BBQPlan::compileFunctions):
* wasm/WasmFaultSignalHandler.cpp:
(JSC::Wasm::trapHandler):
(JSC::Wasm::enableFastMemory):
* wasm/WasmMachineThreads.cpp:
(JSC::Wasm::resetInstructionCacheOnAllThreads):

Source/WTF:

This patch enables using mach exceptions on darwin. The way the
mach exception api works is that we create a mach port, which is
like a file descriptor.  We then wait for a message to arrive on
that port in a thread. When another thread raises an exception (say
due to a bad memory access) the OS sends our thread a message. The
payload of that message is the register file of the crashing
thread. We then call our custom handlers that change the state as
needed. In order to restart the thread we send a payload back to
the OS with an updated register file along with a success message
header.

This patch also makes thread messages work without signals by
simply suspending the thread, and then running the message at that
time.

You can read more about mach exceptions here:
http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/exception.ps
and the Mach interface Generator (MiG) here:
http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/mig.ps

* Configurations/WTF.xcconfig:
* WTF.xcodeproj/project.pbxproj:
* wtf/Platform.h:
* wtf/PlatformRegisters.h:
(WTF::registersFromUContext):
* wtf/StackBounds.h:
(WTF::StackBounds::StackBounds):
* wtf/ThreadHolder.cpp:
(WTF::ThreadHolder::~ThreadHolder):
* wtf/ThreadMessage.cpp:
(WTF::sendMessageUsingSignal):
(WTF::sendMessageUsingMach):
(WTF::deliverMessagesUsingMach):
(WTF::sendMessageScoped):
* wtf/ThreadMessage.h:
(WTF::sendMessage):
* wtf/Threading.h:
(WTF::Thread::machThread):
* wtf/mac/MachExceptions.defs: Copied from Source/WTF/wtf/ThreadMessage.h.
* wtf/threads/Signals.cpp:
(WTF::startMachExceptionHandlerThread):
(WTF::fromMachException):
(WTF::toMachMask):
(WTF::handleSignalsWithMach):
(WTF::setExceptionPorts):
(WTF::activeThreads):
(WTF::registerThreadForMachExceptionHandling):
(WTF::unregisterThreadForMachExceptionHandling):
(WTF::installSignalHandler):
(WTF::jscSignalHandler):
* wtf/threads/Signals.h:

Tools:

* TestWebKitAPI/Tests/WTF/ThreadMessages.cpp:
(runThreadMessageTest):
(TEST):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp (216807 => 216808)


--- trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -30,10 +30,18 @@
 #include "JSContextRefPrivate.h"
 #include "_javascript_Core.h"
 #include "Options.h"
+
 #include <chrono>
+#include <wtf/Atomics.h>
+#include <wtf/Condition.h>
 #include <wtf/CurrentTime.h>
+#include <wtf/Lock.h>
 #include <wtf/text/StringBuilder.h>
 
+#if HAVE(MACH_EXCEPTIONS)
+#include <dispatch/dispatch.h>
+#endif
+
 using namespace std::chrono;
 using JSC::Options;
 
@@ -77,6 +85,15 @@
     return true;
 }
 
+#if HAVE(MACH_EXCEPTIONS)
+bool dispatchTerminateCallbackCalled = false;
+static bool dispatchTermitateCallback(JSContextRef, void*)
+{
+    dispatchTerminateCallbackCalled = true;
+    return true;
+}
+#endif
+
 struct TierOptions {
     const char* tier;
     unsigned timeLimitAdjustmentMillis;
@@ -365,6 +382,65 @@
             }
         }
 
+#if HAVE(MACH_EXCEPTIONS)
+        /* Test script timeout from dispatch queue: */
+        timeLimit = (100 + tierAdjustmentMillis) / 1000.0;
+        JSContextGroupSetExecutionTimeLimit(contextGroup, timeLimit, dispatchTermitateCallback, 0);
+        {
+            unsigned timeAfterWatchdogShouldHaveFired = 300 + tierAdjustmentMillis;
+
+            StringBuilder scriptBuilder;
+            scriptBuilder.appendLiteral("function foo() { var startTime = currentCPUTime(); while (true) { for (var i = 0; i < 1000; i++); if (currentCPUTime() - startTime > ");
+            scriptBuilder.appendNumber(timeAfterWatchdogShouldHaveFired / 1000.0);
+            scriptBuilder.appendLiteral(") break; } } foo();");
+
+            JSStringRef script = JSStringCreateWithUTF8CString(scriptBuilder.toString().utf8().data());
+            exception = nullptr;
+            dispatchTerminateCallbackCalled = false;
+
+            // We have to do this since blocks can only capture things as const.
+            JSGlobalContextRef& contextRef = context;
+            JSStringRef& scriptRef = script;
+            JSValueRef& exceptionRef = exception;
+
+            Lock syncLock;
+            Lock& syncLockRef = syncLock;
+            Condition synchronize;
+            Condition& synchronizeRef = synchronize;
+            bool didSynchronize = false;
+            bool& didSynchronizeRef = didSynchronize;
+
+            std::chrono::microseconds startTime;
+            std::chrono::microseconds endTime;
+
+            std::chrono::microseconds& startTimeRef = startTime;
+            std::chrono::microseconds& endTimeRef = endTime;
+
+            dispatch_group_t group = dispatch_group_create();
+            dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
+                startTimeRef = currentCPUTime();
+                JSEvaluateScript(contextRef, scriptRef, nullptr, nullptr, 1, &exceptionRef);
+                endTimeRef = currentCPUTime();
+                auto locker = WTF::holdLock(syncLockRef);
+                didSynchronizeRef = true;
+                synchronizeRef.notifyAll();
+            });
+
+            auto locker = holdLock(syncLock);
+            synchronize.wait(syncLock, [&] { return didSynchronize; });
+
+            if (((endTime - startTime) < milliseconds(timeAfterWatchdogShouldHaveFired)) && dispatchTerminateCallbackCalled)
+                printf("PASS: %s script on dispatch queue timed out as expected.\n", tierOptions.tier);
+            else {
+                if ((endTime - startTime) >= milliseconds(timeAfterWatchdogShouldHaveFired))
+                    printf("FAIL: %s script on dispatch queue did not time out as expected.\n", tierOptions.tier);
+                if (!shouldTerminateCallbackWasCalled)
+                    printf("FAIL: %s script on dispatch queue timeout callback was not called.\n", tierOptions.tier);
+                failed = true;
+            }
+        }
+#endif
+
         JSGlobalContextRelease(context);
 
         Options::setOptions(savedOptionsBuilder.toString().ascii().data());

Modified: trunk/Source/_javascript_Core/ChangeLog (216807 => 216808)


--- trunk/Source/_javascript_Core/ChangeLog	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-05-13 01:30:13 UTC (rev 216808)
@@ -1,3 +1,40 @@
+2017-05-12  Keith Miller  <[email protected]>
+
+        Use Mach exceptions instead of signals where possible
+        https://bugs.webkit.org/show_bug.cgi?id=171865
+
+        Reviewed by Mark Lam.
+
+        This patch adds some new JSC options. The first is an option that
+        enables or disables web assembly tier up. The second controls
+        whether or not we use mach exceptions (where available).
+
+        * API/tests/ExecutionTimeLimitTest.cpp:
+        (dispatchTermitateCallback):
+        (testExecutionTimeLimit):
+        * runtime/JSLock.cpp:
+        (JSC::JSLock::didAcquireLock):
+        * runtime/Options.cpp:
+        (JSC::overrideDefaults):
+        (JSC::Options::initialize):
+        * runtime/Options.h:
+        * runtime/VMTraps.cpp:
+        (JSC::SignalContext::SignalContext):
+        (JSC::SignalContext::adjustPCToPointToTrappingInstruction):
+        (JSC::installSignalHandler):
+        (JSC::VMTraps::SignalSender::send):
+        * tools/SigillCrashAnalyzer.cpp:
+        (JSC::SignalContext::SignalContext):
+        (JSC::SignalContext::dump):
+        (JSC::installCrashHandler):
+        * wasm/WasmBBQPlan.cpp:
+        (JSC::Wasm::BBQPlan::compileFunctions):
+        * wasm/WasmFaultSignalHandler.cpp:
+        (JSC::Wasm::trapHandler):
+        (JSC::Wasm::enableFastMemory):
+        * wasm/WasmMachineThreads.cpp:
+        (JSC::Wasm::resetInstructionCacheOnAllThreads):
+
 2017-05-12  Mark Lam  <[email protected]>
 
         WorkerRunLoop::Task::performTask() should check !scriptController->isTerminatingExecution().

Modified: trunk/Source/_javascript_Core/runtime/JSLock.cpp (216807 => 216808)


--- trunk/Source/_javascript_Core/runtime/JSLock.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/runtime/JSLock.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -30,6 +30,8 @@
 #include "SamplingProfiler.h"
 #include "WasmMachineThreads.h"
 #include <thread>
+#include <wtf/Threading.h>
+#include <wtf/threads/Signals.h>
 
 namespace JSC {
 
@@ -148,6 +150,10 @@
     Wasm::startTrackingCurrentThread();
 #endif
 
+#if HAVE(MACH_EXCEPTIONS)
+    registerThreadForMachExceptionHandling(&Thread::current());
+#endif
+
     m_vm->traps().notifyGrabAllLocks();
 
 #if ENABLE(SAMPLING_PROFILER)

Modified: trunk/Source/_javascript_Core/runtime/Options.cpp (216807 => 216808)


--- trunk/Source/_javascript_Core/runtime/Options.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/runtime/Options.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -43,6 +43,7 @@
 #include <wtf/StdLibExtras.h>
 #include <wtf/StringExtras.h>
 #include <wtf/text/StringBuilder.h>
+#include <wtf/threads/Signals.h>
 
 #if PLATFORM(COCOA)
 #include <crt_externs.h>
@@ -358,6 +359,10 @@
 #if !ENABLE(WEBASSEMBLY_FAST_MEMORY)
     Options::useWebAssemblyFastMemory() = false;
 #endif
+
+#if !HAVE(MACH_EXCEPTIONS)
+    Options::useMachForExceptions() = false;
+#endif
 }
 
 static void recomputeDependentOptions()
@@ -550,6 +555,11 @@
 
             dumpOptionsIfNeeded();
             ensureOptionsAreCoherent();
+
+#if HAVE(MACH_EXCEPTIONS)
+            if (Options::useMachForExceptions())
+                handleSignalsWithMach();
+#endif
         });
 }
 

Modified: trunk/Source/_javascript_Core/runtime/Options.h (216807 => 216808)


--- trunk/Source/_javascript_Core/runtime/Options.h	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/runtime/Options.h	2017-05-13 01:30:13 UTC (rev 216808)
@@ -422,6 +422,8 @@
     v(unsigned, watchdog, 0, Normal, "watchdog timeout (0 = Disabled, N = a timeout period of N milliseconds)") \
     v(bool, usePollingTraps, false, Normal, "use polling (instead of signalling) VM traps") \
     \
+    v(bool, useMachForExceptions, true, Normal, "Use mach exceptions rather than signals to handle faults and pass thread messages. (This does nothing on platforms without mach)") \
+    \
     v(bool, useICStats, false, Normal, nullptr) \
     \
     v(unsigned, prototypeHitCountForLLIntCaching, 2, Normal, "Number of prototype property hits before caching a prototype in the LLInt. A count of 0 means never cache.") \
@@ -445,6 +447,7 @@
     v(unsigned, webAssemblyBBQOptimizationLevel, 1, Normal, "B3 Optimization level for BBQ Web Assembly module compilations.") \
     v(unsigned, webAssemblyOMGOptimizationLevel, Options::defaultB3OptLevel(), Normal, "B3 Optimization level for OMG Web Assembly module compilations.") \
     \
+    v(bool, useBBQTierUpChecks, true, Normal, "Enables tier up checks for our BBQ code.") \
     v(unsigned, webAssemblyOMGTierUpCount, 5000, Normal, "The countdown before we tier up a function to OMG.") \
     v(unsigned, webAssemblyLoopDecrement, 15, Normal, "The amount the tier up countdown is decremented on each loop backedge.") \
     v(unsigned, webAssemblyFunctionEntryDecrement, 1, Normal, "The amount the tier up countdown is decremented on each function entry.") \

Modified: trunk/Source/_javascript_Core/runtime/VMTraps.cpp (216807 => 216808)


--- trunk/Source/_javascript_Core/runtime/VMTraps.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/runtime/VMTraps.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -53,11 +53,11 @@
 #if ENABLE(SIGNAL_BASED_VM_TRAPS)
 
 struct SignalContext {
-    SignalContext(mcontext_t& mcontext)
-        : mcontext(mcontext)
-        , trapPC(MachineContext::instructionPointer(mcontext))
-        , stackPointer(MachineContext::stackPointer(mcontext))
-        , framePointer(MachineContext::framePointer(mcontext))
+    SignalContext(PlatformRegisters& registers)
+        : registers(registers)
+        , trapPC(MachineContext::instructionPointer(registers))
+        , stackPointer(MachineContext::stackPointer(registers))
+        , framePointer(MachineContext::framePointer(registers))
     {
 #if CPU(X86_64) || CPU(X86)
         // On X86_64, SIGTRAP reports the address after the trapping PC. So, dec by 1.
@@ -68,11 +68,11 @@
     void adjustPCToPointToTrappingInstruction()
     {
 #if CPU(X86_64) || CPU(X86)
-        MachineContext::instructionPointer(mcontext) = trapPC;
+        MachineContext::instructionPointer(registers) = trapPC;
 #endif
     }
 
-    mcontext_t& mcontext;
+    PlatformRegisters& registers;
     void* trapPC;
     void* stackPointer;
     void* framePointer;
@@ -130,8 +130,8 @@
 
 static void installSignalHandler()
 {
-    installSignalHandler(Signal::Trap, [] (int, siginfo_t*, void* uap) -> SignalAction {
-        SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
+    installSignalHandler(Signal::Trap, [] (Signal, SigInfo&, PlatformRegisters& registers) -> SignalAction {
+        SignalContext context(registers);
 
         if (!isJITPC(context.trapPC))
             return SignalAction::NotHandled;
@@ -404,8 +404,8 @@
             VM& vm = *m_vm;
             auto optionalOwnerThread = vm.ownerThread();
             if (optionalOwnerThread) {
-                sendMessage(*optionalOwnerThread.value().get(), [] (siginfo_t*, ucontext_t* ucontext) -> void {
-                    SignalContext context(ucontext->uc_mcontext);
+                sendMessage(*optionalOwnerThread.value().get(), [] (PlatformRegisters& registers) -> void {
+                    SignalContext context(registers);
                     auto activeVMAndStackBounds = findActiveVMAndStackBounds(context);
                     if (activeVMAndStackBounds) {
                         VM* vm = activeVMAndStackBounds.value().vm;

Modified: trunk/Source/_javascript_Core/tools/SigillCrashAnalyzer.cpp (216807 => 216808)


--- trunk/Source/_javascript_Core/tools/SigillCrashAnalyzer.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/tools/SigillCrashAnalyzer.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -78,11 +78,11 @@
 #endif // USE(OS_LOG)
 
 struct SignalContext {
-    SignalContext(mcontext_t& mcontext)
-        : mcontext(mcontext)
-        , machinePC(MachineContext::instructionPointer(mcontext))
-        , stackPointer(MachineContext::stackPointer(mcontext))
-        , framePointer(MachineContext::framePointer(mcontext))
+    SignalContext(PlatformRegisters& registers)
+        : registers(registers)
+        , machinePC(MachineContext::instructionPointer(registers))
+        , stackPointer(MachineContext::stackPointer(registers))
+        , framePointer(MachineContext::framePointer(registers))
     { }
 
     void dump()
@@ -112,7 +112,7 @@
         v(gs)
 
 #define DUMP_REGISTER(__reg) \
-        log("Register " #__reg ": %p", reinterpret_cast<void*>(mcontext->__ss.__##__reg));
+        log("Register " #__reg ": %p", reinterpret_cast<void*>(registers.__##__reg));
         FOR_EACH_REGISTER(DUMP_REGISTER)
 #undef FOR_EACH_REGISTER
 
@@ -120,20 +120,20 @@
         int i;
         for (i = 0; i < 28; i += 4) {
             log("x%d: %016llx x%d: %016llx x%d: %016llx x%d: %016llx",
-                i, mcontext->__ss.__x[i],
-                i+1, mcontext->__ss.__x[i+1],
-                i+2, mcontext->__ss.__x[i+2],
-                i+3, mcontext->__ss.__x[i+3]);
+                i, registers.__x[i],
+                i+1, registers.__x[i+1],
+                i+2, registers.__x[i+2],
+                i+3, registers.__x[i+3]);
         }
         ASSERT(i < 29);
         log("x%d: %016llx fp: %016llx lr: %016llx",
-            i, mcontext->__ss.__x[i], mcontext->__ss.__fp, mcontext->__ss.__lr);
+            i, registers.__x[i], registers.__fp, registers.__lr);
         log("sp: %016llx pc: %016llx cpsr: %08x",
-            mcontext->__ss.__sp, mcontext->__ss.__pc, mcontext->__ss.__cpsr);
+            registers.__sp, registers.__pc, registers.__cpsr);
 #endif
     }
 
-    mcontext_t& mcontext;
+    PlatformRegisters& registers;
     void* machinePC;
     void* stackPointer;
     void* framePointer;
@@ -142,8 +142,8 @@
 static void installCrashHandler()
 {
 #if CPU(X86_64) || CPU(ARM64)
-    installSignalHandler(Signal::Ill, [] (int, siginfo_t*, void* uap) {
-        SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
+    installSignalHandler(Signal::Ill, [] (Signal, SigInfo&, PlatformRegisters& registers) {
+        SignalContext context(registers);
 
         if (!isJITPC(context.machinePC))
             return SignalAction::NotHandled;

Modified: trunk/Source/_javascript_Core/wasm/WasmBBQPlan.cpp (216807 => 216808)


--- trunk/Source/_javascript_Core/wasm/WasmBBQPlan.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/wasm/WasmBBQPlan.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -242,7 +242,7 @@
         ASSERT(validateFunction(functionStart, functionLength, signature, m_moduleInformation.get()));
 
         m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>();
-        TierUpCount* tierUp = &m_tierUpCounts[functionIndex];
+        TierUpCount* tierUp = Options::useBBQTierUpChecks() ? &m_tierUpCounts[functionIndex] : nullptr;
         auto parseAndCompileResult = parseAndCompile(m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, CompilationMode::BBQMode, functionIndex, tierUp);
 
         if (UNLIKELY(!parseAndCompileResult)) {

Modified: trunk/Source/_javascript_Core/wasm/WasmFaultSignalHandler.cpp (216807 => 216808)


--- trunk/Source/_javascript_Core/wasm/WasmFaultSignalHandler.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/wasm/WasmFaultSignalHandler.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -52,9 +52,8 @@
 
 static bool fastHandlerInstalled { false };
 
-static SignalAction trapHandler(int, siginfo_t* sigInfo, void* ucontext)
+static SignalAction trapHandler(Signal, SigInfo& sigInfo, PlatformRegisters& context)
 {
-    mcontext_t& context = static_cast<ucontext_t*>(ucontext)->uc_mcontext;
     void* faultingInstruction = MachineContext::instructionPointer(context);
     dataLogLnIf(verbose, "starting handler for fault at: ", RawPointer(faultingInstruction));
 
@@ -64,7 +63,7 @@
     if (isJITPC(faultingInstruction)) {
         bool faultedInActiveFastMemory = false;
         {
-            void* faultingAddress = sigInfo->si_addr;
+            void* faultingAddress = sigInfo.faultingAddress;
             dataLogLnIf(verbose, "checking faulting address: ", RawPointer(faultingAddress), " is in an active fast memory");
             faultedInActiveFastMemory = Wasm::Memory::addressIsInActiveFastMemory(faultingAddress);
         }
@@ -124,11 +123,11 @@
             return;
 
 #if ENABLE(WEBASSEMBLY_FAST_MEMORY)
-        installSignalHandler(Signal::Bus, [] (int signal, siginfo_t* sigInfo, void* ucontext) {
+        installSignalHandler(Signal::Bus, [] (Signal signal, SigInfo& sigInfo, PlatformRegisters& ucontext) {
             return trapHandler(signal, sigInfo, ucontext);
         });
 
-        installSignalHandler(Signal::SegV, [] (int signal, siginfo_t* sigInfo, void* ucontext) {
+        installSignalHandler(Signal::SegV, [] (Signal signal, SigInfo& sigInfo, PlatformRegisters& ucontext) {
             return trapHandler(signal, sigInfo, ucontext);
         });
 

Modified: trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp (216807 => 216808)


--- trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -31,6 +31,7 @@
 #include "MachineStackMarker.h"
 #include <wtf/NeverDestroyed.h>
 #include <wtf/ThreadMessage.h>
+#include <wtf/threads/Signals.h>
 
 namespace JSC { namespace Wasm {
 
@@ -57,7 +58,7 @@
 
     const DoublyLinkedList<MachineThreads::MachineThread>& threads = wasmThreads().threadsListHead(locker);
     for (const auto* thread = threads.head(); thread; thread = thread->next()) {
-        sendMessage(thread->m_thread.get(), [] (siginfo_t*, ucontext_t*) {
+        sendMessage(thread->m_thread.get(), [] (const PlatformRegisters&) {
             // It's likely that the signal handler will already reset the instruction cache but we might as well be sure.
             WTF::crossModifyingCodeFence();
         });

Modified: trunk/Source/WTF/ChangeLog (216807 => 216808)


--- trunk/Source/WTF/ChangeLog	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/ChangeLog	2017-05-13 01:30:13 UTC (rev 216808)
@@ -1,3 +1,62 @@
+2017-05-12  Keith Miller  <[email protected]>
+
+        Use Mach exceptions instead of signals where possible
+        https://bugs.webkit.org/show_bug.cgi?id=171865
+
+        Reviewed by Mark Lam.
+
+        This patch enables using mach exceptions on darwin. The way the
+        mach exception api works is that we create a mach port, which is
+        like a file descriptor.  We then wait for a message to arrive on
+        that port in a thread. When another thread raises an exception (say
+        due to a bad memory access) the OS sends our thread a message. The
+        payload of that message is the register file of the crashing
+        thread. We then call our custom handlers that change the state as
+        needed. In order to restart the thread we send a payload back to
+        the OS with an updated register file along with a success message
+        header.
+
+        This patch also makes thread messages work without signals by
+        simply suspending the thread, and then running the message at that
+        time.
+
+        You can read more about mach exceptions here:
+        http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/exception.ps
+        and the Mach interface Generator (MiG) here:
+        http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/mig.ps
+
+        * Configurations/WTF.xcconfig:
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/Platform.h:
+        * wtf/PlatformRegisters.h:
+        (WTF::registersFromUContext):
+        * wtf/StackBounds.h:
+        (WTF::StackBounds::StackBounds):
+        * wtf/ThreadHolder.cpp:
+        (WTF::ThreadHolder::~ThreadHolder):
+        * wtf/ThreadMessage.cpp:
+        (WTF::sendMessageUsingSignal):
+        (WTF::sendMessageUsingMach):
+        (WTF::deliverMessagesUsingMach):
+        (WTF::sendMessageScoped):
+        * wtf/ThreadMessage.h:
+        (WTF::sendMessage):
+        * wtf/Threading.h:
+        (WTF::Thread::machThread):
+        * wtf/mac/MachExceptions.defs: Copied from Source/WTF/wtf/ThreadMessage.h.
+        * wtf/threads/Signals.cpp:
+        (WTF::startMachExceptionHandlerThread):
+        (WTF::fromMachException):
+        (WTF::toMachMask):
+        (WTF::handleSignalsWithMach):
+        (WTF::setExceptionPorts):
+        (WTF::activeThreads):
+        (WTF::registerThreadForMachExceptionHandling):
+        (WTF::unregisterThreadForMachExceptionHandling):
+        (WTF::installSignalHandler):
+        (WTF::jscSignalHandler):
+        * wtf/threads/Signals.h:
+
 2017-05-12  Ting-Wei Lan  <[email protected]>
 
         Include algorithm before using std::min

Modified: trunk/Source/WTF/Configurations/WTF.xcconfig (216807 => 216808)


--- trunk/Source/WTF/Configurations/WTF.xcconfig	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/Configurations/WTF.xcconfig	2017-05-13 01:30:13 UTC (rev 216808)
@@ -27,3 +27,6 @@
 
 GCC_SYMBOLS_PRIVATE_EXTERN = YES;
 STRIP_INSTALLED_PRODUCT = NO;
+
+EXCLUDED_SOURCE_FILE_NAMES_ = MachExceptions.defs
+EXCLUDED_SOURCE_FILE_NAMES[sdk=iphoneos*] = $(EXCLUDED_SOURCE_FILE_NAMES_$(USE_INTERNAL_SDK))

Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (216807 => 216808)


--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2017-05-13 01:30:13 UTC (rev 216808)
@@ -64,6 +64,7 @@
 		52183012C99E476A84EEBEA8 /* SymbolImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F72BBDB107FA424886178B9E /* SymbolImpl.cpp */; };
 		5311BD531EA71CAD00525281 /* Signals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5311BD511EA71CAD00525281 /* Signals.cpp */; };
 		5311BD5C1EA822F900525281 /* ThreadMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5311BD5B1EA822F900525281 /* ThreadMessage.cpp */; };
+		53534F2A1EC0E10E00141B2F /* MachExceptions.defs in Sources */ = {isa = PBXBuildFile; fileRef = 53534F291EC0E10E00141B2F /* MachExceptions.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; };
 		70A993FE1AD7151300FA615B /* SymbolRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70A993FC1AD7151300FA615B /* SymbolRegistry.cpp */; };
 		70ECA60D1B02426800449739 /* AtomicStringImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70ECA60A1B02426800449739 /* AtomicStringImpl.cpp */; };
 		7AFEC6B11EB22B5900DADE36 /* UUID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEC6B01EB22B5900DADE36 /* UUID.cpp */; };
@@ -299,6 +300,7 @@
 		5311BD571EA7E1A100525281 /* Signals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Signals.h; sourceTree = "<group>"; };
 		5311BD591EA81A9600525281 /* ThreadMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadMessage.h; sourceTree = "<group>"; };
 		5311BD5B1EA822F900525281 /* ThreadMessage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadMessage.cpp; sourceTree = "<group>"; };
+		53534F291EC0E10E00141B2F /* MachExceptions.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = MachExceptions.defs; sourceTree = "<group>"; };
 		539EB0621D55284200C82EF7 /* LEBDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LEBDecoder.h; sourceTree = "<group>"; };
 		53EC253C1E95AD30000831B9 /* PriorityQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PriorityQueue.h; sourceTree = "<group>"; };
 		553071C91C40427200384898 /* TinyLRUCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TinyLRUCache.h; sourceTree = "<group>"; };
@@ -1022,6 +1024,7 @@
 			children = (
 				1A428B8B1C8F89DD0051E9EB /* AppKitCompatibilityDeclarations.h */,
 				1ACADD821884480100D8B71D /* DeprecatedSymbolsUsedBySafari.mm */,
+				53534F291EC0E10E00141B2F /* MachExceptions.defs */,
 				A8A472C5151A825A004123FF /* MainThreadMac.mm */,
 			);
 			path = mac;
@@ -1346,6 +1349,7 @@
 				143F611F1565F0F900DB514A /* RAMSize.cpp in Sources */,
 				A3B725EC987446AD93F1A440 /* RandomDevice.cpp in Sources */,
 				A8A47414151A825B004123FF /* RandomNumber.cpp in Sources */,
+				53534F2A1EC0E10E00141B2F /* MachExceptions.defs in Sources */,
 				A8A4741A151A825B004123FF /* RefCountedLeakCounter.cpp in Sources */,
 				2CDED0F318115C85004DBA70 /* RunLoop.cpp in Sources */,
 				2CDED0EF18115C38004DBA70 /* RunLoopCF.cpp in Sources */,

Modified: trunk/Source/WTF/wtf/Platform.h (216807 => 216808)


--- trunk/Source/WTF/wtf/Platform.h	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/wtf/Platform.h	2017-05-13 01:30:13 UTC (rev 216808)
@@ -664,6 +664,10 @@
 #define HAVE_READLINE 1
 #define HAVE_SYS_TIMEB_H 1
 
+#if __has_include(<mach/mach_exc.defs>) && !(PLATFORM(WATCHOS) || PLATFORM(APPLETV))
+#define HAVE_MACH_EXCEPTIONS 1
+#endif
+
 #if !PLATFORM(GTK)
 #define USE_ACCELERATE 1
 #endif

Modified: trunk/Source/WTF/wtf/PlatformRegisters.h (216807 => 216808)


--- trunk/Source/WTF/wtf/PlatformRegisters.h	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/wtf/PlatformRegisters.h	2017-05-13 01:30:13 UTC (rev 216808)
@@ -26,9 +26,11 @@
 #pragma once
 
 #include <wtf/Platform.h>
+#include <wtf/StdLibExtras.h>
 
 #if OS(DARWIN)
 #include <mach/thread_act.h>
+#include <signal.h>
 #elif OS(WINDOWS)
 #include <windows.h>
 #else
@@ -55,6 +57,11 @@
 #error Unknown Architecture
 #endif
 
+inline PlatformRegisters& registersFromUContext(ucontext_t* ucontext)
+{
+    return ucontext->uc_mcontext->__ss;
+}
+
 #elif OS(WINDOWS)
 
 using PlatformRegisters = CONTEXT;
@@ -65,6 +72,11 @@
     mcontext_t machineContext;
 };
 
+inline PlatformRegisters& registersFromUContext(ucontext_t* ucontext)
+{
+    return *bitwise_cast<PlatformRegisters*>(&ucontext->uc_mcontext);
+}
+
 #else
 
 struct PlatformRegisters {

Modified: trunk/Source/WTF/wtf/StackBounds.h (216807 => 216808)


--- trunk/Source/WTF/wtf/StackBounds.h	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/wtf/StackBounds.h	2017-05-13 01:30:13 UTC (rev 216808)
@@ -54,7 +54,6 @@
         : m_origin(origin)
         , m_bound(end)
     {
-        checkConsistency();
     }
 
     void* origin() const

Modified: trunk/Source/WTF/wtf/ThreadHolder.cpp (216807 => 216808)


--- trunk/Source/WTF/wtf/ThreadHolder.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/wtf/ThreadHolder.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -34,6 +34,8 @@
 
 #include "Threading.h"
 
+#include <wtf/threads/Signals.h>
+
 namespace WTF {
 
 ThreadSpecificKey ThreadHolder::m_key = InvalidThreadSpecificKey;
@@ -40,6 +42,9 @@
 
 ThreadHolder::~ThreadHolder()
 {
+#if HAVE(MACH_EXCEPTIONS)
+    unregisterThreadForMachExceptionHandling(&m_thread.get());
+#endif
     m_thread->didExit();
 }
 

Modified: trunk/Source/WTF/wtf/ThreadMessage.cpp (216807 => 216808)


--- trunk/Source/WTF/wtf/ThreadMessage.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/wtf/ThreadMessage.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -36,6 +36,9 @@
 #include <wtf/Locker.h>
 #include <wtf/threads/Signals.h>
 
+#if HAVE(MACH_EXCEPTIONS)
+#include <mach/thread_act.h>
+#endif
 
 namespace WTF {
 
@@ -83,12 +86,12 @@
 }
 
 SUPPRESS_ASAN
-MessageStatus sendMessageScoped(Thread& thread, const ThreadMessage& message)
+static MessageStatus sendMessageUsingSignal(Thread& thread, const ThreadMessage& message)
 {
     constexpr Signal signal = Signal::Usr;
     static std::once_flag once;
     std::call_once(once, [] {
-        installSignalHandler(signal, [] (int, siginfo_t* info, void* uap) {
+        installSignalHandler(signal, [] (Signal, SigInfo&, PlatformRegisters& registers) {
             Thread* thread = Thread::currentMayBeNull();
 
             if (!thread) {
@@ -98,7 +101,7 @@
 
             // Node should be deleted in the sender thread. Deleting Nodes in signal handler causes dead lock.
             thread->threadMessages().consumeAllWithNode([&] (ThreadMessageData* data, Node* node) {
-                data->message(info, static_cast<ucontext_t*>(uap));
+                data->message(registers);
                 // By setting ran variable, (1) the sender acknowledges the completion and
                 // (2) gets the Node to be deleted.
                 data->ran.store(node);
@@ -156,6 +159,62 @@
     RELEASE_ASSERT_NOT_REACHED();
 }
 
+#if HAVE(MACH_EXCEPTIONS)
+static MessageStatus sendMessageUsingMach(Thread& thread, const ThreadMessage& message)
+{
+    static StaticLock messageLock;
+    auto lockholder = holdLock(messageLock);
+
+    auto result = thread.suspend();
+    if (!result)
+        return MessageStatus::ThreadExited;
+
+#if CPU(X86)
+    unsigned userCount = sizeof(PlatformRegisters) / sizeof(int);
+    thread_state_flavor_t flavor = i386_THREAD_STATE;
+#elif CPU(X86_64)
+    unsigned userCount = x86_THREAD_STATE64_COUNT;
+    thread_state_flavor_t flavor = x86_THREAD_STATE64;
+#elif CPU(ARM)
+    unsigned userCount = ARM_THREAD_STATE_COUNT;
+    thread_state_flavor_t flavor = ARM_THREAD_STATE;
+#elif CPU(ARM64)
+    unsigned userCount = ARM_THREAD_STATE64_COUNT;
+    thread_state_flavor_t flavor = ARM_THREAD_STATE64;
+#else
+#error Unknown Architecture
+#endif
+
+    PlatformRegisters registers;
+    thread_state_t state = reinterpret_cast<thread_state_t>(&registers);
+    thread_get_state(thread.machThread(), flavor, state, &userCount);
+
+    message(registers);
+
+    thread_set_state(thread.machThread(), flavor, state, userCount);
+
+    thread.resume();
+    return MessageStatus::MessageRan;
+}
+
+static bool useMach = false;
+void deliverMessagesUsingMach()
+{
+    useMach = true;
+}
+
+#endif // HAVE(MACH_EXCEPTIONS)
+
+MessageStatus sendMessageScoped(Thread& thread, const ThreadMessage& message)
+{
+#if HAVE(MACH_EXCEPTIONS)
+    if (useMach)
+        return sendMessageUsingMach(thread, message);
+#endif
+    return sendMessageUsingSignal(thread, message);
+}
+
+
 } // namespace WTF
 
 #endif // USE(PTHREADS)

Modified: trunk/Source/WTF/wtf/ThreadMessage.h (216807 => 216808)


--- trunk/Source/WTF/wtf/ThreadMessage.h	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/wtf/ThreadMessage.h	2017-05-13 01:30:13 UTC (rev 216808)
@@ -36,7 +36,7 @@
 void initializeThreadMessages();
 
 class ThreadMessageData;
-using ThreadMessage = ScopedLambda<void(siginfo_t*, ucontext_t*)>;
+using ThreadMessage = ScopedLambda<void(PlatformRegisters&)>;
 
 enum class MessageStatus {
     MessageRan,
@@ -51,10 +51,14 @@
 template<typename Functor>
 MessageStatus sendMessage(Thread& targetThread, const Functor& func)
 {
-    auto lambda = scopedLambdaRef<void(siginfo_t*, ucontext_t*)>(func);
+    auto lambda = scopedLambdaRef<void(PlatformRegisters&)>(func);
     return sendMessageScoped(targetThread, lambda);
 }
 
+#if HAVE(MACH_EXCEPTIONS)
+void deliverMessagesUsingMach();
+#endif
+
 } // namespace WTF
 
 using WTF::sendMessage;

Modified: trunk/Source/WTF/wtf/Threading.h (216807 => 216808)


--- trunk/Source/WTF/wtf/Threading.h	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/wtf/Threading.h	2017-05-13 01:30:13 UTC (rev 216808)
@@ -138,7 +138,11 @@
 
 #if USE(PTHREADS)
     LocklessBag<ThreadMessageData*>& threadMessages() { return m_threadMessages; }
+
+#if OS(DARWIN)
+    mach_port_t machThread() { return m_platformThread; }
 #endif
+#endif
 
 protected:
     Thread();

Copied: trunk/Source/WTF/wtf/mac/MachExceptions.defs (from rev 216807, trunk/Source/WTF/wtf/ThreadMessage.h) (0 => 216808)


--- trunk/Source/WTF/wtf/mac/MachExceptions.defs	                        (rev 0)
+++ trunk/Source/WTF/wtf/mac/MachExceptions.defs	2017-05-13 01:30:13 UTC (rev 216808)
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <mach/mach_exc.defs>

Modified: trunk/Source/WTF/wtf/threads/Signals.cpp (216807 => 216808)


--- trunk/Source/WTF/wtf/threads/Signals.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/wtf/threads/Signals.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -28,22 +28,268 @@
 
 #if USE(PTHREADS)
 
+#if HAVE(MACH_EXCEPTIONS)
+extern "C" {
+#include "MachExceptionsServer.h"
+};
+#endif
+
 #include <cstdio>
 #include <mutex>
 #include <signal.h>
+
+#if HAVE(MACH_EXCEPTIONS)
+#include <mach/mach.h>
+#include <mach/thread_act.h>
+#endif
+
 #include <wtf/Atomics.h>
 #include <wtf/DataLog.h>
+#include <wtf/HashSet.h>
 #include <wtf/LocklessBag.h>
 #include <wtf/NeverDestroyed.h>
+#include <wtf/ThreadMessage.h>
+#include <wtf/Threading.h>
 
+
 namespace WTF {
 
+    
 static LazyNeverDestroyed<LocklessBag<SignalHandler>> handlers[static_cast<size_t>(Signal::NumberOfSignals)] = { };
+static std::once_flag initializeOnceFlags[static_cast<size_t>(Signal::NumberOfSignals)];
 static struct sigaction oldActions[static_cast<size_t>(Signal::NumberOfSignals)];
-static std::once_flag initializeOnceFlags[static_cast<size_t>(Signal::NumberOfSignals)];
 
-static void jscSignalHandler(int sig, siginfo_t* info, void* mcontext)
+#if HAVE(MACH_EXCEPTIONS)
+// You can read more about mach exceptions here:
+// http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/exception.ps
+// and the Mach interface Generator (MiG) here:
+// http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/mig.ps
+
+static mach_port_t exceptionPort;
+static constexpr size_t maxMessageSize = 1 * KB;
+
+static void startMachExceptionHandlerThread()
 {
+    static std::once_flag once;
+    std::call_once(once, [] {
+        if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exceptionPort) != KERN_SUCCESS)
+            CRASH();
+
+        if (mach_port_insert_right(mach_task_self(), exceptionPort, exceptionPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)
+            CRASH();
+
+        // FIXME: This should use a dispatch queue.
+        // See: https://bugs.webkit.org/show_bug.cgi?id=172003
+        (void)Thread::create(
+            "WTF Mach Exception Thread", [] () {
+                union Message {
+                    mach_msg_header_t header;
+                    char data[maxMessageSize];
+                };
+                Message messageHeaderIn;
+                Message messageHeaderOut;
+
+                while (true) {
+                    kern_return_t messageResult = mach_msg(&messageHeaderIn.header, MACH_RCV_MSG, 0, maxMessageSize, exceptionPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+                    if (messageResult == KERN_SUCCESS) {
+                        if (!mach_exc_server(&messageHeaderIn.header, &messageHeaderOut.header))
+                            CRASH();
+
+                        messageResult = mach_msg(&messageHeaderOut.header, MACH_SEND_MSG, messageHeaderOut.header.msgh_size, 0, messageHeaderOut.header.msgh_local_port, 0, MACH_PORT_NULL);
+                        RELEASE_ASSERT(messageResult == KERN_SUCCESS);
+                    } else {
+                        dataLogLn("Failed to receive mach message due to ", mach_error_string(messageResult));
+                        RELEASE_ASSERT_NOT_REACHED();
+                    }
+                }
+        }).leakRef();
+    });
+}
+
+static Signal fromMachException(exception_type_t type)
+{
+    switch (type) {
+    case EXC_BAD_ACCESS: return Signal::SegV;
+    case EXC_BAD_INSTRUCTION: return Signal::Ill;
+    case EXC_BREAKPOINT: return Signal::Trap;
+    default: break;
+    }
+    return Signal::Unknown;
+}
+
+static exception_mask_t toMachMask(Signal signal)
+{
+    switch (signal) {
+    case Signal::SegV: return EXC_MASK_BAD_ACCESS;
+    case Signal::Ill: return EXC_MASK_BAD_INSTRUCTION;
+    case Signal::Trap: return EXC_MASK_BREAKPOINT;
+    default: break;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+extern "C" {
+
+// We need to implement stubs for catch_mach_exception_raise and catch_mach_exception_raise_state_identity.
+// The MiG generated file will fail to link otherwise, even though we don't use the functions. Only the
+// catch_mach_exception_raise_state function should be called because we pass EXCEPTION_STATE to
+// thread_set_exception_ports.
+kern_return_t catch_mach_exception_raise(mach_port_t, mach_port_t, mach_port_t, exception_type_t, mach_exception_data_t, mach_msg_type_number_t)
+{
+    dataLogLn("We should not have called catch_exception_raise(), please file a bug at bugs.webkit.org");
+    return KERN_FAILURE;
+}
+
+kern_return_t catch_mach_exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t, exception_type_t, mach_exception_data_t, mach_msg_type_number_t, int*, thread_state_t, mach_msg_type_number_t, thread_state_t,  mach_msg_type_number_t*)
+{
+    dataLogLn("We should not have called catch_mach_exception_raise_state_identity, please file a bug at bugs.webkit.org");
+    return KERN_FAILURE;
+}
+
+kern_return_t catch_mach_exception_raise_state(
+    mach_port_t port,
+    exception_type_t exceptionType,
+    const mach_exception_data_t exceptionData,
+    mach_msg_type_number_t dataCount,
+    int* stateFlavor,
+    const thread_state_t inState,
+    mach_msg_type_number_t inStateCount,
+    thread_state_t outState,
+    mach_msg_type_number_t* outStateCount)
+{
+    RELEASE_ASSERT(port == exceptionPort);
+    Signal signal = fromMachException(exceptionType);
+    RELEASE_ASSERT(signal != Signal::Unknown);
+
+    memcpy(outState, inState, inStateCount * sizeof(inState[0]));
+    *outStateCount = inStateCount;
+
+#if CPU(X86_64)
+    RELEASE_ASSERT(*stateFlavor == x86_THREAD_STATE);
+    PlatformRegisters& registers = reinterpret_cast<x86_thread_state_t*>(outState)->uts.ts64;
+#elif CPU(X86)
+    RELEASE_ASSERT(*stateFlavor == x86_THREAD_STATE);
+    PlatformRegisters& registers = reinterpret_cast<x86_thread_state_t*>(outState)->uts.ts32;
+#elif CPU(ARM64)
+    RELEASE_ASSERT(*stateFlavor == ARM_THREAD_STATE);
+    PlatformRegisters& registers = reinterpret_cast<arm_unified_thread_state*>(outState)->ts_64;
+#elif CPU(ARM)
+    RELEASE_ASSERT(*stateFlavor == ARM_THREAD_STATE);
+    PlatformRegisters& registers = reinterpret_cast<arm_unified_thread_state*>(outState)->ts_32;
+#endif
+
+    SigInfo info;
+    if (signal == Signal::SegV) {
+        ASSERT_UNUSED(dataCount, dataCount == 2);
+        info.faultingAddress = reinterpret_cast<void*>(exceptionData[1]);
+    }
+
+    bool didHandle = false;
+    handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
+        SignalAction handlerResult = handler(signal, info, registers);
+        didHandle |= handlerResult == SignalAction::Handled;
+    });
+
+    if (didHandle)
+        return KERN_SUCCESS;
+    return KERN_FAILURE;
+}
+
+};
+
+static bool useMach { false };
+void handleSignalsWithMach()
+{
+    deliverMessagesUsingMach();
+    useMach = true;
+}
+
+
+static StaticLock threadLock;
+exception_mask_t activeExceptions { 0 };
+
+inline void setExceptionPorts(const AbstractLocker&, Thread* thread)
+{
+    kern_return_t result = thread_set_exception_ports(thread->machThread(), activeExceptions, exceptionPort, EXCEPTION_STATE | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE);
+    if (result != KERN_SUCCESS) {
+        dataLogLn("thread set port failed due to ", mach_error_string(result));
+        CRASH();
+    }
+}
+
+inline HashSet<Thread*>& activeThreads(const AbstractLocker&)
+{
+    static NeverDestroyed<HashSet<Thread*>> activeThreads;
+    return activeThreads;
+}
+
+void registerThreadForMachExceptionHandling(Thread* thread)
+{
+    auto locker = holdLock(threadLock);
+    auto result = activeThreads(locker).add(thread);
+
+    if (result.isNewEntry)
+        setExceptionPorts(locker, thread);
+}
+
+void unregisterThreadForMachExceptionHandling(Thread* thread)
+{
+    auto locker = holdLock(threadLock);
+    activeThreads(locker).remove(thread);
+}
+
+#else
+static constexpr bool useMach = false;
+#endif // HAVE(MACH_EXCEPTIONS)
+
+static void jscSignalHandler(int, siginfo_t*, void*);
+
+void installSignalHandler(Signal signal, SignalHandler&& handler)
+{
+    ASSERT(signal < Signal::Unknown);
+#if HAVE(MACH_EXCEPTIONS)
+    // Since mach only has EXC_BAD_ACCESS, which covers both SegV and Bus, we arbitarily choose to make
+    // mach EXC_BAD_ACCESSes map to SegV.
+    // FIXME: We should just use a single Signal::BadAccess value instead of SegV/Bus.
+    // See: https://bugs.webkit.org/show_bug.cgi?id=172063
+    if (signal == Signal::Bus && useMach)
+        return;
+    ASSERT(!useMach || signal != Signal::Usr);
+
+    if (useMach)
+        startMachExceptionHandlerThread();
+#endif
+
+    std::call_once(initializeOnceFlags[static_cast<size_t>(signal)], [&] {
+        handlers[static_cast<size_t>(signal)].construct();
+
+        if (!useMach) {
+            struct sigaction action;
+            action.sa_sigaction = jscSignalHandler;
+            auto result = sigfillset(&action.sa_mask);
+            RELEASE_ASSERT(!result);
+            action.sa_flags = SA_SIGINFO;
+            result = sigaction(toSystemSignal(signal), &action, &oldActions[static_cast<size_t>(signal)]);
+            RELEASE_ASSERT(!result);
+        }
+
+    });
+
+    handlers[static_cast<size_t>(signal)]->add(WTFMove(handler));
+
+#if HAVE(MACH_EXCEPTIONS)
+    auto locker = holdLock(threadLock);
+    if (useMach) {
+        activeExceptions |= toMachMask(signal);
+
+        for (Thread* thread : activeThreads(locker))
+            setExceptionPorts(locker, thread);
+    }
+#endif
+}
+
+void jscSignalHandler(int sig, siginfo_t* info, void* ucontext)
+{
     Signal signal = fromSystemSignal(sig);
 
     auto restoreDefault = [&] {
@@ -62,10 +308,16 @@
         return;
     }
 
+    SigInfo sigInfo;
+    if (signal == Signal::SegV || signal == Signal::Bus)
+        sigInfo.faultingAddress = info->si_addr;
+
+    PlatformRegisters& registers = registersFromUContext(reinterpret_cast<ucontext_t*>(ucontext));
+
     bool didHandle = false;
     bool restoreDefaultHandler = false;
     handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
-        switch (handler(sig, info, mcontext)) {
+        switch (handler(signal, sigInfo, registers)) {
         case SignalAction::Handled:
             didHandle = true;
             break;
@@ -85,13 +337,13 @@
     struct sigaction& oldAction = oldActions[static_cast<size_t>(signal)];
     if (signal == Signal::Usr) {
         if (oldAction.sa_sigaction)
-            oldAction.sa_sigaction(sig, info, mcontext);
+            oldAction.sa_sigaction(sig, info, ucontext);
         return;
     }
 
     if (!didHandle) {
         if (oldAction.sa_sigaction) {
-            oldAction.sa_sigaction(sig, info, mcontext);
+            oldAction.sa_sigaction(sig, info, ucontext);
             return;
         }
 
@@ -100,24 +352,6 @@
     }
 }
 
-void installSignalHandler(Signal signal, SignalHandler&& handler)
-{
-    ASSERT(signal < Signal::Unknown);
-    std::call_once(initializeOnceFlags[static_cast<size_t>(signal)], [&] {
-        handlers[static_cast<size_t>(signal)].construct();
-
-        struct sigaction action;
-        action.sa_sigaction = jscSignalHandler;
-        auto result = sigfillset(&action.sa_mask);
-        RELEASE_ASSERT(!result);
-        action.sa_flags = SA_SIGINFO;
-        result = sigaction(toSystemSignal(signal), &action, &oldActions[static_cast<size_t>(signal)]);
-        RELEASE_ASSERT(!result);
-    });
-
-    handlers[static_cast<size_t>(signal)]->add(WTFMove(handler));
-}
-
 } // namespace WTF
 
 #endif // USE(PTHREADS)

Modified: trunk/Source/WTF/wtf/threads/Signals.h (216807 => 216808)


--- trunk/Source/WTF/wtf/threads/Signals.h	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Source/WTF/wtf/threads/Signals.h	2017-05-13 01:30:13 UTC (rev 216808)
@@ -29,6 +29,7 @@
 
 #include <signal.h>
 #include <wtf/Function.h>
+#include <wtf/PlatformRegisters.h>
 
 namespace WTF {
 
@@ -79,8 +80,12 @@
     ForceDefault
 };
 
-using SignalHandler = Function<SignalAction(int, siginfo_t*, void*)>;
+struct SigInfo {
+    void* faultingAddress { 0 };
+};
 
+using SignalHandler = Function<SignalAction(Signal, SigInfo&, PlatformRegisters&)>;
+
 // Call this method whenever you want to install a signal handler. It's ok to call this function lazily.
 // Note: Your signal handler will be called every time the handler for the desired signal is called.
 // Thus it is your responsibility to discern if the signal fired was yours.
@@ -87,9 +92,25 @@
 // This function is currently a one way street i.e. once installed, a signal handler cannot be uninstalled.
 void installSignalHandler(Signal, SignalHandler&&);
 
+
+#if HAVE(MACH_EXCEPTIONS)
+class Thread;
+void registerThreadForMachExceptionHandling(Thread*);
+void unregisterThreadForMachExceptionHandling(Thread*);
+
+void handleSignalsWithMach();
+#endif // HAVE(MACH_EXCEPTIONS)
+
 } // namespace WTF
 
+#if HAVE(MACH_EXCEPTIONS)
+using WTF::registerThreadForMachExceptionHandling;
+using WTF::unregisterThreadForMachExceptionHandling;
+using WTF::handleSignalsWithMach;
+#endif // HAVE(MACH_EXCEPTIONS)
+
 using WTF::Signal;
+using WTF::SigInfo;
 using WTF::toSystemSignal;
 using WTF::fromSystemSignal;
 using WTF::SignalAction;

Modified: trunk/Tools/ChangeLog (216807 => 216808)


--- trunk/Tools/ChangeLog	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Tools/ChangeLog	2017-05-13 01:30:13 UTC (rev 216808)
@@ -1,3 +1,14 @@
+2017-05-12  Keith Miller  <[email protected]>
+
+        Use Mach exceptions instead of signals where possible
+        https://bugs.webkit.org/show_bug.cgi?id=171865
+
+        Reviewed by Mark Lam.
+
+        * TestWebKitAPI/Tests/WTF/ThreadMessages.cpp:
+        (runThreadMessageTest):
+        (TEST):
+
 2017-05-12  Jeremy Jones  <[email protected]>
 
         Add objc version of WK2 UIPageClient setHasVideoInPictureInPicture and rename it to hasVideoInPictureInPictureDidChange.

Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadMessages.cpp (216807 => 216808)


--- trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadMessages.cpp	2017-05-13 01:00:37 UTC (rev 216807)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadMessages.cpp	2017-05-13 01:30:13 UTC (rev 216808)
@@ -50,7 +50,7 @@
     for (unsigned senderID = 0; senderID < numSenders; ++senderID) {
         senderThreads[senderID] = Thread::create("ThreadMessage sender", [senderID, numMessages, receiverThread, &messagesRun, &handlersRun] () {
             for (unsigned i = 0; i < numMessages; ++i) {
-                auto result = sendMessage(*receiverThread.get(), [senderID, &handlersRun] (siginfo_t*, ucontext_t*) {
+                auto result = sendMessage(*receiverThread.get(), [senderID, &handlersRun] (PlatformRegisters&) {
                     handlersRun[senderID]++;
                 });
                 EXPECT_TRUE(result == WTF::MessageStatus::MessageRan);
@@ -95,7 +95,7 @@
 TEST(ThreadMessage, SignalsWorkOnExit)
 {
     static bool handlerRan = false;
-    installSignalHandler(Signal::Usr, [] (int, siginfo_t*, void*) -> SignalAction {
+    installSignalHandler(Signal::Usr, [] (Signal, SigInfo&, PlatformRegisters&) -> SignalAction {
         dataLogLn("here");
         handlerRan = true;
         return SignalAction::Handled;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to