Add signaling API
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Apr 2017 18:35:42 +0000 (18:35 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Apr 2017 18:35:42 +0000 (18:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=170976

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

Update various uses of sigaction to use the new signaling API.
Also switch VMTraps to use the thread message system instead of
rolling it's own.

* jit/ExecutableAllocator.h:
(JSC::isJITPC):
* runtime/VMTraps.cpp:
(JSC::installSignalHandler):
(JSC::VMTraps::VMTraps):
(JSC::VMTraps::SignalSender::send):
(JSC::handleSigusr1): Deleted.
(JSC::handleSigtrap): Deleted.
(JSC::installSignalHandlers): Deleted.
* runtime/VMTraps.h:
* tools/SigillCrashAnalyzer.cpp:
(JSC::installCrashHandler):
(JSC::handleCrash): Deleted.
* wasm/WasmFaultSignalHandler.cpp:
(JSC::Wasm::trapHandler):
(JSC::Wasm::enableFastMemory):

Source/WTF:

This patch adds a bunch of new functionality to WTF. First, it add
a new data structure of a lockless bag. The LocklessBag class can
be used as either a multi-writer multi-reader lockless bag or a
multi-writer single consumer lockless bag.

Next, this patch adds an abstraction around sigaction in WTF.
Basically, you can add a handler, which can be an arbitrary
lambda. Although it should still be safe for the signal you are
handling.  the signal handler manager will then do all the
appropriate handling of chaining:

In the SIGUSR case we always forward the signal to any other
non-default handler installed before us. We need to do this,
otherwise, since it's not possible to tell if a SIGUSR was
intended exlusively for our handlers. Signal handlers don't record
how many times they were sent only that there is at least one
unhandled signal.

In the faulting cases we require that every handle be able to
recognise a fault they installed, vs crashes. If none of our
handlers claim to have handled the fault we will forward the
fault. If a handler was installed before the first fault handler
we simply call that handler and rely on them to take the
appropriate action. If no handler was installed before our first
handler we restore the default handler and allow the fault to
happen again.

Finally, this patch adds a signal based messaging system.  This
system allows the user to run an arbitrary lambda from the SIGUSR2
signal handler of any target WTF::Thread. This is already in use
for the VMTraps API which allows us to kill rogue VMs by sending
the VM's running WTF::Thread a SIGUSR and requesting it jetison
all optimized code and throw an uncatchable exception from all
function entries/loop backedges.  In the future, we will also use
this API for Wasm to reset the instruction caches in currently
executing Wasm threads.

* WTF.xcodeproj/project.pbxproj:
* wtf/Atomics.h:
(WTF::Atomic::Atomic):
* wtf/LocklessBag.h: Added.
(WTF::LocklessBag::LocklessBag):
(WTF::LocklessBag::add):
(WTF::LocklessBag::iterate):
(WTF::LocklessBag::consumeAll):
* wtf/ThreadMessage.cpp: Added.
(WTF::ThreadMessageData::ThreadMessageData):
(WTF::initializeThreadMessages):
(WTF::sendMessageScoped):
* wtf/ThreadMessage.h: Added.
(WTF::sendMessage):
* wtf/Threading.cpp:
(WTF::initializeThreading):
* wtf/Threading.h:
(WTF::Thread::threadMessages):
* wtf/ThreadingPthreads.cpp:
(WTF::Thread::currentMayBeNull):
(WTF::Thread::current):
(WTF::Thread::signal):
* wtf/threads/Signals.cpp: Added.
(WTF::jscSignalHandler):
(WTF::installSignalHandler):
* wtf/threads/Signals.h: Added.
(WTF::toSystemSignal):
(WTF::fromSystemSignal):

Tools:

Add tests for ThreadMessages.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WTF/ThreadMessages.cpp: Added.
(runThreadMessageTest):
(TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@215620 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/jit/ExecutableAllocator.h
Source/JavaScriptCore/runtime/VMTraps.cpp
Source/JavaScriptCore/runtime/VMTraps.h
Source/JavaScriptCore/tools/SigillCrashAnalyzer.cpp
Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp
Source/WTF/ChangeLog
Source/WTF/WTF.xcodeproj/project.pbxproj
Source/WTF/wtf/Atomics.h
Source/WTF/wtf/CMakeLists.txt
Source/WTF/wtf/LocklessBag.h [new file with mode: 0644]
Source/WTF/wtf/ThreadMessage.cpp [new file with mode: 0644]
Source/WTF/wtf/ThreadMessage.h [new file with mode: 0644]
Source/WTF/wtf/Threading.cpp
Source/WTF/wtf/Threading.h
Source/WTF/wtf/ThreadingPthreads.cpp
Source/WTF/wtf/threads/Signals.cpp [new file with mode: 0644]
Source/WTF/wtf/threads/Signals.h [new file with mode: 0644]
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WTF/ThreadMessages.cpp [new file with mode: 0644]

index 2913f5f..e0449ee 100644 (file)
@@ -1,3 +1,31 @@
+2017-04-20  Keith Miller  <keith_miller@apple.com>
+
+        Add signaling API
+        https://bugs.webkit.org/show_bug.cgi?id=170976
+
+        Reviewed by Filip Pizlo.
+
+        Update various uses of sigaction to use the new signaling API.
+        Also switch VMTraps to use the thread message system instead of
+        rolling it's own.
+
+        * jit/ExecutableAllocator.h:
+        (JSC::isJITPC):
+        * runtime/VMTraps.cpp:
+        (JSC::installSignalHandler):
+        (JSC::VMTraps::VMTraps):
+        (JSC::VMTraps::SignalSender::send):
+        (JSC::handleSigusr1): Deleted.
+        (JSC::handleSigtrap): Deleted.
+        (JSC::installSignalHandlers): Deleted.
+        * runtime/VMTraps.h:
+        * tools/SigillCrashAnalyzer.cpp:
+        (JSC::installCrashHandler):
+        (JSC::handleCrash): Deleted.
+        * wasm/WasmFaultSignalHandler.cpp:
+        (JSC::Wasm::trapHandler):
+        (JSC::Wasm::enableFastMemory):
+
 2017-04-21  Michael Saboff  <msaboff@apple.com>
 
         X86-64 Assembler doesn't handle xchg with byte register src
index 6ee6fe2..10e1442 100644 (file)
@@ -81,6 +81,12 @@ static const double executablePoolReservationFraction = 0.25;
 extern JS_EXPORTDATA uintptr_t startOfFixedExecutableMemoryPool;
 extern JS_EXPORTDATA uintptr_t endOfFixedExecutableMemoryPool;
 
+inline bool isJITPC(void* pc)
+{
+    return reinterpret_cast<void*>(startOfFixedExecutableMemoryPool) <= pc
+        && pc < reinterpret_cast<void*>(endOfFixedExecutableMemoryPool);
+}
+
 typedef void (*JITWriteSeparateHeapsFunction)(off_t, const void*, size_t);
 extern JS_EXPORTDATA JITWriteSeparateHeapsFunction jitWriteSeparateHeapsFunction;
 
index e6285a3..905e6c9 100644 (file)
 #include "VMInspector.h"
 #include "Watchdog.h"
 #include <wtf/ProcessID.h>
-
-#if OS(DARWIN)
-#include <signal.h>
-#endif
+#include <wtf/ThreadMessage.h>
+#include <wtf/threads/Signals.h>
 
 namespace JSC {
 
@@ -54,9 +52,6 @@ ALWAYS_INLINE VM& VMTraps::vm() const
 
 #if ENABLE(SIGNAL_BASED_VM_TRAPS)
 
-struct sigaction originalSigusr1Action;
-struct sigaction originalSigtrapAction;
-
 struct SignalContext {
     SignalContext(mcontext_t& mcontext)
         : mcontext(mcontext)
@@ -132,87 +127,35 @@ static Expected<VMAndStackBounds, VMTraps::Error> findActiveVMAndStackBounds(Sig
     return VMAndStackBounds { activeVM, stackBounds };
 }
 
-static void handleSigusr1(int signalNumber, siginfo_t* info, void* uap)
+static void installSignalHandler()
 {
-    SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
-    auto activeVMAndStackBounds = findActiveVMAndStackBounds(context);
-    if (activeVMAndStackBounds) {
+    installSignalHandler(Signal::Trap, [] (int, siginfo_t*, void* uap) -> SignalAction {
+        SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
+
+        if (!isJITPC(context.trapPC))
+            return SignalAction::NotHandled;
+
+        // FIXME: This currently eats all traps including jit asserts we should make sure this
+        // always works. https://bugs.webkit.org/show_bug.cgi?id=171039
+        auto activeVMAndStackBounds = findActiveVMAndStackBounds(context);
+        if (!activeVMAndStackBounds)
+            return SignalAction::Handled; // Let the SignalSender try again later.
+
         VM* vm = activeVMAndStackBounds.value().vm;
         if (vm) {
-            StackBounds stackBounds = activeVMAndStackBounds.value().stackBounds;
             VMTraps& traps = vm->traps();
-            if (traps.needTrapHandling())
-                traps.tryInstallTrapBreakpoints(context, stackBounds);
+            if (!traps.needTrapHandling())
+                return SignalAction::Handled; // The polling code beat us to handling the trap already.
+
+            auto expectedSuccess = traps.tryJettisonCodeBlocksOnStack(context);
+            if (!expectedSuccess)
+                return SignalAction::Handled; // Let the SignalSender try again later.
+            if (expectedSuccess.value())
+                return SignalAction::Handled; // We've success jettison the codeBlocks.
         }
-    }
-
-    auto originalAction = originalSigusr1Action.sa_sigaction;
-    if (originalAction)
-        originalAction(signalNumber, info, uap);
-}
-
-static void handleSigtrap(int signalNumber, siginfo_t* info, void* uap)
-{
-    SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
-    auto activeVMAndStackBounds = findActiveVMAndStackBounds(context);
-    if (!activeVMAndStackBounds)
-        return; // Let the SignalSender try again later.
 
-    VM* vm = activeVMAndStackBounds.value().vm;
-    if (vm) {
-        VMTraps& traps = vm->traps();
-        if (!traps.needTrapHandling())
-            return; // The polling code beat us to handling the trap already.
-
-        auto expectedSuccess = traps.tryJettisonCodeBlocksOnStack(context);
-        if (!expectedSuccess)
-            return; // Let the SignalSender try again later.
-        if (expectedSuccess.value())
-            return; // We've success jettison the codeBlocks.
-    }
-
-    // If we get here, then this SIGTRAP is not due to a VMTrap. Let's do the default action.
-    auto originalAction = originalSigtrapAction.sa_sigaction;
-    if (originalAction) {
-        // It is always safe to just invoke the original handler using the sa_sigaction form
-        // without checking for the SA_SIGINFO flag. If the original handler is of the
-        // sa_handler form, it will just ignore the 2nd and 3rd arguments since sa_handler is a
-        // subset of sa_sigaction. This is what the man pages says the OS does anyway.
-        originalAction(signalNumber, info, uap);
-    }
-    
-    // Pre-emptively restore the default handler but we may roll it back below.
-    struct sigaction currentAction;
-    struct sigaction defaultAction;
-    defaultAction.sa_handler = SIG_DFL;
-    sigfillset(&defaultAction.sa_mask);
-    defaultAction.sa_flags = 0;
-    sigaction(SIGTRAP, &defaultAction, &currentAction);
-    
-    if (currentAction.sa_sigaction != handleSigtrap) {
-        // This means that there's a client handler installed after us. This also means
-        // that the client handler thinks it was able to recover from the SIGTRAP, and
-        // did not uninstall itself. We can't argue with this because the signal isn't
-        // known to be from a VMTraps signal. Hence, restore the client handler
-        // and keep going.
-        sigaction(SIGTRAP, &currentAction, nullptr);
-    }
-}
-
-static void installSignalHandlers()
-{
-    typedef void (* SigactionHandler)(int, siginfo_t *, void *);
-    struct sigaction action;
-
-    action.sa_sigaction = reinterpret_cast<SigactionHandler>(handleSigusr1);
-    sigfillset(&action.sa_mask);
-    action.sa_flags = SA_SIGINFO;
-    sigaction(SIGUSR1, &action, &originalSigusr1Action);
-
-    action.sa_sigaction = reinterpret_cast<SigactionHandler>(handleSigtrap);
-    sigfillset(&action.sa_mask);
-    action.sa_flags = SA_SIGINFO;
-    sigaction(SIGTRAP, &action, &originalSigtrapAction);
+        return SignalAction::Handled;
+    });
 }
 
 ALWAYS_INLINE static CallFrame* sanitizedTopCallFrame(CallFrame* topCallFrame)
@@ -400,7 +343,7 @@ VMTraps::VMTraps()
     if (!Options::usePollingTraps()) {
         static std::once_flag once;
         std::call_once(once, [] {
-            installSignalHandlers();
+            installSignalHandler();
         });
     }
 #endif
@@ -460,7 +403,19 @@ void VMTraps::SignalSender::send()
             VM& vm = *m_vm;
             auto optionalOwnerThread = vm.ownerThread();
             if (optionalOwnerThread) {
-                optionalOwnerThread.value()->signal(SIGUSR1);
+                sendMessage(*optionalOwnerThread.value().get(), [] (siginfo_t*, ucontext_t* ucontext) -> void {
+                    SignalContext context(ucontext->uc_mcontext);
+                    auto activeVMAndStackBounds = findActiveVMAndStackBounds(context);
+                    if (activeVMAndStackBounds) {
+                        VM* vm = activeVMAndStackBounds.value().vm;
+                        if (vm) {
+                            StackBounds stackBounds = activeVMAndStackBounds.value().stackBounds;
+                            VMTraps& traps = vm->traps();
+                            if (traps.needTrapHandling())
+                                traps.tryInstallTrapBreakpoints(context, stackBounds);
+                        }
+                    }
+                });
                 break;
             }
 
index fdba2af..5425b18 100644 (file)
@@ -42,7 +42,8 @@ class VMTraps {
 public:
     enum class Error {
         None,
-        LockUnavailable
+        LockUnavailable,
+        NotJITCode
     };
 
     enum EventType {
index 265c661..eb86ee4 100644 (file)
@@ -37,9 +37,7 @@
 #include "A64DOpcode.h"
 #endif
 
-#if HAVE(SIGNAL_H)
-#include <signal.h>
-#endif
+#include <wtf/threads/Signals.h>
 
 namespace JSC {
 
@@ -141,60 +139,19 @@ struct SignalContext {
     void* framePointer;
 };
 
-struct sigaction originalSigIllAction;
-
-static void handleCrash(int signalNumber, siginfo_t* info, void* uap)
-{
-    SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
-    SigillCrashAnalyzer& analyzer = SigillCrashAnalyzer::instance();
-    auto crashSource = analyzer.analyze(context);
-
-    auto originalAction = originalSigIllAction.sa_sigaction;
-    if (originalAction) {
-        // It is always safe to just invoke the original handler using the sa_sigaction form
-        // without checking for the SA_SIGINFO flag. If the original handler is of the
-        // sa_handler form, it will just ignore the 2nd and 3rd arguments since sa_handler is a
-        // subset of sa_sigaction. This is what the man pages says the OS does anyway.
-        originalAction(signalNumber, info, uap);
-    }
-
-    if (crashSource == SigillCrashAnalyzer::CrashSource::JavaScriptCore) {
-        // Restore the default handler so that we can get a core dump.
-        struct sigaction defaultAction;
-        defaultAction.sa_handler = SIG_DFL;
-        sigfillset(&defaultAction.sa_mask);
-        defaultAction.sa_flags = 0;
-        sigaction(SIGILL, &defaultAction, nullptr);
-    } else if (!originalAction) {
-        // Pre-emptively restore the default handler but we may roll it back below.
-        struct sigaction currentAction;
-        struct sigaction defaultAction;
-        defaultAction.sa_handler = SIG_DFL;
-        sigfillset(&defaultAction.sa_mask);
-        defaultAction.sa_flags = 0;
-        sigaction(SIGILL, &defaultAction, &currentAction);
-
-        if (currentAction.sa_sigaction != handleCrash) {
-            // This means that there's a client handler installed after us. This also means
-            // that the client handler thinks it was able to recover from the SIGILL, and
-            // did not uninstall itself. We can't argue with this because the crash isn't
-            // known to be from a JavaScriptCore source. Hence, restore the client handler
-            // and keep going.
-            sigaction(SIGILL, &currentAction, nullptr);
-        }
-    }
-}
-
 static void installCrashHandler()
 {
 #if CPU(X86_64) || CPU(ARM64)
-    struct sigaction action;
-    action.sa_sigaction = reinterpret_cast<void (*)(int, siginfo_t *, void *)>(handleCrash);
-    sigfillset(&action.sa_mask);
-    action.sa_flags = SA_SIGINFO;
-    sigaction(SIGILL, &action, &originalSigIllAction);
-#else
-    UNUSED_PARAM(handleCrash);
+    installSignalHandler(Signal::Ill, [] (int, siginfo_t*, void* uap) {
+        SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
+
+        if (!isJITPC(context.machinePC))
+            return SignalAction::NotHandled;
+
+        SigillCrashAnalyzer& analyzer = SigillCrashAnalyzer::instance();
+        analyzer.analyze(context);
+        return SignalAction::NotHandled;
+    });
 #endif
 }
 
index 898104b..2e0c99a 100644 (file)
@@ -35,9 +35,9 @@
 #include "WasmMemory.h"
 #include "WasmThunks.h"
 
-#include <signal.h>
 #include <wtf/Lock.h>
 #include <wtf/NeverDestroyed.h>
+#include <wtf/threads/Signals.h>
 
 namespace JSC { namespace Wasm {
 
@@ -50,11 +50,9 @@ static LazyNeverDestroyed<HashSet<std::tuple<void*, void*>>> codeLocations; // (
 
 #if ENABLE(WEBASSEMBLY_FAST_MEMORY)
 
-static struct sigaction oldSigBusHandler;
-static struct sigaction oldSigSegvHandler;
 static bool fastHandlerInstalled { false };
 
-static void trapHandler(int signal, siginfo_t* sigInfo, void* ucontext)
+static SignalAction trapHandler(int, siginfo_t* sigInfo, void* ucontext)
 {
     mcontext_t& context = static_cast<ucontext_t*>(ucontext)->uc_mcontext;
     void* faultingInstruction = MachineContext::instructionPointer(context);
@@ -63,9 +61,7 @@ static void trapHandler(int signal, siginfo_t* sigInfo, void* ucontext)
     dataLogLnIf(verbose, "JIT memory start: ", RawPointer(reinterpret_cast<void*>(startOfFixedExecutableMemoryPool)), " end: ", RawPointer(reinterpret_cast<void*>(endOfFixedExecutableMemoryPool)));
     // First we need to make sure we are in JIT code before we can aquire any locks. Otherwise,
     // we might have crashed in code that is already holding one of the locks we want to aquire.
-    if (reinterpret_cast<void*>(startOfFixedExecutableMemoryPool) <= faultingInstruction
-        && faultingInstruction < reinterpret_cast<void*>(endOfFixedExecutableMemoryPool)) {
-
+    if (isJITPC(faultingInstruction)) {
         bool faultedInActiveFastMemory = false;
         {
             void* faultingAddress = sigInfo->si_addr;
@@ -89,17 +85,12 @@ static void trapHandler(int signal, siginfo_t* sigInfo, void* ucontext)
                     dataLogLnIf(verbose, "found stub: ", RawPointer(exceptionStub.code().executableAddress()));
                     MachineContext::argumentPointer<1>(context) = reinterpret_cast<void*>(ExceptionType::OutOfBoundsMemoryAccess);
                     MachineContext::instructionPointer(context) = exceptionStub.code().executableAddress();
-                    return;
+                    return SignalAction::Handled;
                 }
             }
         }
     }
-
-    // Since we only use fast memory in processes we control, if we restore we will just fall back to the default handler.
-    if (signal == SIGBUS)
-        sigaction(signal, &oldSigBusHandler, nullptr);
-    else
-        sigaction(signal, &oldSigSegvHandler, nullptr);
+    return SignalAction::NotHandled;
 }
 
 #endif // ENABLE(WEBASSEMBLY_FAST_MEMORY)
@@ -133,22 +124,13 @@ void enableFastMemory()
             return;
 
 #if ENABLE(WEBASSEMBLY_FAST_MEMORY)
-        struct sigaction action;
-
-        action.sa_sigaction = trapHandler;
-        sigfillset(&action.sa_mask);
-        action.sa_flags = SA_SIGINFO;
-        
-        // Installing signal handlers fails when
-        // 1. specificied sig is incorrect (invalid values or signal numbers which cannot be handled), or
-        // 2. second or third parameter points incorrect pointers.
-        // Thus, we must not fail in the following attempts.
-        int ret = 0;
-        ret = sigaction(SIGBUS, &action, &oldSigBusHandler);
-        RELEASE_ASSERT(!ret);
-
-        ret = sigaction(SIGSEGV, &action, &oldSigSegvHandler);
-        RELEASE_ASSERT(!ret);
+        installSignalHandler(Signal::Bus, [] (int signal, siginfo_t* sigInfo, void* ucontext) {
+            return trapHandler(signal, sigInfo, ucontext);
+        });
+
+        installSignalHandler(Signal::SegV, [] (int signal, siginfo_t* sigInfo, void* ucontext) {
+            return trapHandler(signal, sigInfo, ucontext);
+        });
 
         codeLocations.construct();
         fastHandlerInstalled = true;
index 4ab9ee9..ba15314 100644 (file)
@@ -1,3 +1,76 @@
+2017-04-20  Keith Miller  <keith_miller@apple.com>
+
+        Add signaling API
+        https://bugs.webkit.org/show_bug.cgi?id=170976
+
+        Reviewed by Filip Pizlo.
+
+        This patch adds a bunch of new functionality to WTF. First, it add
+        a new data structure of a lockless bag. The LocklessBag class can
+        be used as either a multi-writer multi-reader lockless bag or a
+        multi-writer single consumer lockless bag.
+
+        Next, this patch adds an abstraction around sigaction in WTF.
+        Basically, you can add a handler, which can be an arbitrary
+        lambda. Although it should still be safe for the signal you are
+        handling.  the signal handler manager will then do all the
+        appropriate handling of chaining:
+
+        In the SIGUSR case we always forward the signal to any other
+        non-default handler installed before us. We need to do this,
+        otherwise, since it's not possible to tell if a SIGUSR was
+        intended exlusively for our handlers. Signal handlers don't record
+        how many times they were sent only that there is at least one
+        unhandled signal.
+
+        In the faulting cases we require that every handle be able to
+        recognise a fault they installed, vs crashes. If none of our
+        handlers claim to have handled the fault we will forward the
+        fault. If a handler was installed before the first fault handler
+        we simply call that handler and rely on them to take the
+        appropriate action. If no handler was installed before our first
+        handler we restore the default handler and allow the fault to
+        happen again.
+
+        Finally, this patch adds a signal based messaging system.  This
+        system allows the user to run an arbitrary lambda from the SIGUSR2
+        signal handler of any target WTF::Thread. This is already in use
+        for the VMTraps API which allows us to kill rogue VMs by sending
+        the VM's running WTF::Thread a SIGUSR and requesting it jetison
+        all optimized code and throw an uncatchable exception from all
+        function entries/loop backedges.  In the future, we will also use
+        this API for Wasm to reset the instruction caches in currently
+        executing Wasm threads.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/Atomics.h:
+        (WTF::Atomic::Atomic):
+        * wtf/LocklessBag.h: Added.
+        (WTF::LocklessBag::LocklessBag):
+        (WTF::LocklessBag::add):
+        (WTF::LocklessBag::iterate):
+        (WTF::LocklessBag::consumeAll):
+        * wtf/ThreadMessage.cpp: Added.
+        (WTF::ThreadMessageData::ThreadMessageData):
+        (WTF::initializeThreadMessages):
+        (WTF::sendMessageScoped):
+        * wtf/ThreadMessage.h: Added.
+        (WTF::sendMessage):
+        * wtf/Threading.cpp:
+        (WTF::initializeThreading):
+        * wtf/Threading.h:
+        (WTF::Thread::threadMessages):
+        * wtf/ThreadingPthreads.cpp:
+        (WTF::Thread::currentMayBeNull):
+        (WTF::Thread::current):
+        (WTF::Thread::signal):
+        * wtf/threads/Signals.cpp: Added.
+        (WTF::jscSignalHandler):
+        (WTF::installSignalHandler):
+        * wtf/threads/Signals.h: Added.
+        (WTF::toSystemSignal):
+        (WTF::fromSystemSignal):
+
 2017-04-21  Konstantin Tokarev  <annulen@yandex.ru>
 
         [cmake] WTF target should not have wtf and subdirectries in public interface
index 94c116b..fb7a901 100644 (file)
                515F79501CFC9F4A00CCED93 /* CrossThreadTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 515F794D1CFC9F4A00CCED93 /* CrossThreadTask.h */; };
                515F79561CFD3A6900CCED93 /* CrossThreadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 515F79551CFD3A6900CCED93 /* CrossThreadQueue.h */; };
                52183012C99E476A84EEBEA8 /* SymbolImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F72BBDB107FA424886178B9E /* SymbolImpl.cpp */; };
+               5311BD531EA71CAD00525281 /* Signals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5311BD511EA71CAD00525281 /* Signals.cpp */; };
+               5311BD561EA7E15A00525281 /* LocklessBag.h in Headers */ = {isa = PBXBuildFile; fileRef = 5311BD551EA7E15A00525281 /* LocklessBag.h */; };
+               5311BD581EA7E1A100525281 /* Signals.h in Headers */ = {isa = PBXBuildFile; fileRef = 5311BD571EA7E1A100525281 /* Signals.h */; };
+               5311BD5A1EA81A9600525281 /* ThreadMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 5311BD591EA81A9600525281 /* ThreadMessage.h */; };
+               5311BD5C1EA822F900525281 /* ThreadMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5311BD5B1EA822F900525281 /* ThreadMessage.cpp */; };
                539EB0631D55284200C82EF7 /* LEBDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 539EB0621D55284200C82EF7 /* LEBDecoder.h */; };
                53EC253E1E95AD30000831B9 /* PriorityQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EC253C1E95AD30000831B9 /* PriorityQueue.h */; };
                553071CA1C40427200384898 /* TinyLRUCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 553071C91C40427200384898 /* TinyLRUCache.h */; };
                515F794C1CFC9F4A00CCED93 /* CrossThreadCopier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrossThreadCopier.h; sourceTree = "<group>"; };
                515F794D1CFC9F4A00CCED93 /* CrossThreadTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrossThreadTask.h; sourceTree = "<group>"; };
                515F79551CFD3A6900CCED93 /* CrossThreadQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrossThreadQueue.h; sourceTree = "<group>"; };
+               5311BD511EA71CAD00525281 /* Signals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Signals.cpp; sourceTree = "<group>"; };
+               5311BD551EA7E15A00525281 /* LocklessBag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocklessBag.h; sourceTree = "<group>"; };
+               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>"; };
                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>"; };
                                0F60F32D1DFCBD1B00416D6C /* LockedPrintStream.cpp */,
                                0F60F32E1DFCBD1B00416D6C /* LockedPrintStream.h */,
                                A8A472C3151A825A004123FF /* Locker.h */,
+                               5311BD551EA7E15A00525281 /* LocklessBag.h */,
                                513E170A1CD7D5BF00E3650B /* LoggingAccumulator.h */,
                                0F30BA8C1E78708E002CA847 /* LoggingHashID.h */,
                                0F30BA8D1E78708E002CA847 /* LoggingHashMap.h */,
                                E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */,
                                E3200AB61E9A536D003B59D2 /* ThreadHolder.h */,
                                A8A47330151A825B004123FF /* ThreadHolderPthreads.cpp */,
+                               5311BD5B1EA822F900525281 /* ThreadMessage.cpp */,
+                               5311BD591EA81A9600525281 /* ThreadMessage.h */,
                                A8A47332151A825B004123FF /* Threading.cpp */,
                                A8A47333151A825B004123FF /* Threading.h */,
                                A8A47335151A825B004123FF /* ThreadingPrimitives.h */,
                        children = (
                                A8A4733A151A825B004123FF /* BinarySemaphore.cpp */,
                                A8A4733B151A825B004123FF /* BinarySemaphore.h */,
+                               5311BD511EA71CAD00525281 /* Signals.cpp */,
+                               5311BD571EA7E1A100525281 /* Signals.h */,
                        );
                        path = threads;
                        sourceTree = "<group>";
                                DE5A09FC1BA36992003D4424 /* CommonCryptoSPI.h in Headers */,
                                0F8F2B91172E00FC007DBDA5 /* CompilationThread.h in Headers */,
                                A8A47398151A825B004123FF /* Compiler.h in Headers */,
+                               5311BD561EA7E15A00525281 /* LocklessBag.h in Headers */,
                                0FDB698E1B7C643A000C1078 /* Condition.h in Headers */,
                                A8A4748C151A8264004123FF /* config.h in Headers */,
                                0F8F2B9C172F2596007DBDA5 /* ConversionMode.h in Headers */,
                                A70DA0841799F04D00529A9B /* Insertion.h in Headers */,
                                26147B0A15DDCCDC00DDB907 /* IntegerToStringConversion.h in Headers */,
                                7CDD7FF8186D291E007433CD /* IteratorAdaptors.h in Headers */,
+                               5311BD581EA7E1A100525281 /* Signals.h in Headers */,
                                7CDD7FFA186D2A54007433CD /* IteratorRange.h in Headers */,
                                93AC91A818942FC400244939 /* LChar.h in Headers */,
                                539EB0631D55284200C82EF7 /* LEBDecoder.h in Headers */,
                                A748745417A0BDAE00FA04CB /* StringHashDumpContext.h in Headers */,
                                A8A47441151A825B004123FF /* StringImpl.h in Headers */,
                                A8A47442151A825B004123FF /* StringOperators.h in Headers */,
+                               5311BD5A1EA81A9600525281 /* ThreadMessage.h in Headers */,
                                0FDDBFA81666DFA300C55FEF /* StringPrintStream.h in Headers */,
                                1A6EB1E0187D0BD30030126F /* StringView.h in Headers */,
                                A8A473B8151A825B004123FF /* strtod.h in Headers */,
                                FEDACD3D1630F83F00C69634 /* StackStats.cpp in Sources */,
                                A8A4743C151A825B004123FF /* StringBuilder.cpp in Sources */,
                                A5BA15FB182435A600A82E69 /* StringCF.cpp in Sources */,
+                               5311BD5C1EA822F900525281 /* ThreadMessage.cpp in Sources */,
                                A8A47440151A825B004123FF /* StringImpl.cpp in Sources */,
                                A5BA15FC182435A600A82E69 /* StringImplCF.cpp in Sources */,
+                               5311BD531EA71CAD00525281 /* Signals.cpp in Sources */,
                                A5BA15F51824348000A82E69 /* StringImplMac.mm in Sources */,
                                A5BA15F3182433A900A82E69 /* StringMac.mm in Sources */,
                                0FDDBFA71666DFA300C55FEF /* StringPrintStream.cpp in Sources */,
index 772e6fe..9907b32 100644 (file)
@@ -174,6 +174,12 @@ struct Atomic {
         return transaction(func, std::memory_order_relaxed, abortLikelihood);
     }
 
+    Atomic() = default;
+    constexpr Atomic(T initial)
+        : value(std::forward<T>(initial))
+    {
+    }
+
     std::atomic<T> value;
 };
 
index f88a572..742e655 100644 (file)
@@ -62,6 +62,7 @@ set(WTF_HEADERS
     LockAlgorithm.h
     LockedPrintStream.h
     Locker.h
+    LocklessBag.h
     LoggingHashID.h
     LoggingHashMap.h
     LoggingHashSet.h
@@ -130,6 +131,7 @@ set(WTF_HEADERS
     StringPrintStream.h
     SystemTracing.h
     ThreadHolder.cpp
+    ThreadMessage.h
     ThreadSafeRefCounted.h
     ThreadSpecific.h
     Threading.h
@@ -185,6 +187,7 @@ set(WTF_HEADERS
     text/icu/UTextProviderUTF16.h
 
     threads/BinarySemaphore.h
+    threads/Signals.h
 
     unicode/CharacterNames.h
     unicode/Collator.h
@@ -239,6 +242,7 @@ set(WTF_SOURCES
     StackBounds.cpp
     StackStats.cpp
     StringPrintStream.cpp
+    ThreadMessage.cpp
     Threading.cpp
     TimeWithDynamicClockType.cpp
     WTFThreadData.cpp
@@ -279,6 +283,7 @@ set(WTF_SOURCES
     text/icu/UTextProviderUTF16.cpp
 
     threads/BinarySemaphore.cpp
+    threads/Signals.cpp
 
     unicode/UTF8.cpp
 )
diff --git a/Source/WTF/wtf/LocklessBag.h b/Source/WTF/wtf/LocklessBag.h
new file mode 100644 (file)
index 0000000..19cac03
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "Atomics.h"
+
+namespace WTF {
+
+// This a simple single consumer, multiple producer Bag data structure.
+
+template<typename T>
+class LocklessBag {
+    WTF_MAKE_NONCOPYABLE(LocklessBag);
+
+    struct Node {
+        WTF_MAKE_FAST_ALLOCATED;
+    public:
+        T data;
+        Node* next;
+    };
+public:
+    LocklessBag()
+        : m_head(nullptr)
+    {
+    }
+
+    enum PushResult { Empty, NonEmpty };
+    PushResult add(T&& element)
+    {
+        Node* newNode = new Node();
+        newNode->data = std::forward<T>(element);
+
+        Node* oldHead;
+        m_head.transaction([&] (Node*& head) {
+            oldHead = head;
+            newNode->next = head;
+            head = newNode;
+            return true;
+        });
+
+        return oldHead == nullptr ? Empty : NonEmpty;
+    }
+
+    // CONSUMER FUNCTIONS: Everything below here is only safe to call from the consumer thread.
+
+    // This function is actually safe to call from more than one thread, but ONLY if no thread can call consumeAll.
+    template<typename Functor>
+    void iterate(const Functor& func)
+    {
+        Node* node = m_head.load();
+        while (node) {
+            const T& data = node->data;
+            func(data);
+            node = node->next;
+        }
+    }
+
+    template<typename Functor>
+    void consumeAll(const Functor& func)
+    {
+        Node* node = m_head.exchange(nullptr);
+        while (node) {
+            func(WTFMove(node->data));
+            Node* oldNode = node;
+            node = node->next;
+            delete oldNode;
+        }
+    }
+
+private:
+    Atomic<Node*> m_head;
+};
+    
+} // namespace WTF
diff --git a/Source/WTF/wtf/ThreadMessage.cpp b/Source/WTF/wtf/ThreadMessage.cpp
new file mode 100644 (file)
index 0000000..f2f8a7b
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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 "config.h"
+#include "ThreadMessage.h"
+
+#if USE(PTHREADS)
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <wtf/DataLog.h>
+#include <wtf/Lock.h>
+#include <wtf/Locker.h>
+#include <wtf/threads/Signals.h>
+
+
+namespace WTF {
+
+class ThreadMessageData {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    ThreadMessageData(const ThreadMessage& m)
+        : ran(false)
+        , message(m)
+    {
+    }
+
+    Atomic<bool> ran;
+    const ThreadMessage& message;
+};
+
+enum FileDescriptor {
+    Read,
+    Write,
+    NumberOfFileDescriptors,
+};
+
+static int fileDescriptors[NumberOfFileDescriptors];
+static const char* const magicByte = "d";
+
+
+void initializeThreadMessages()
+{
+    int result = pipe(fileDescriptors);
+    RELEASE_ASSERT(!result);
+
+    int flags = fcntl(fileDescriptors[Write], F_GETFL);
+    result = fcntl(fileDescriptors[Write], F_SETFL, flags | O_NONBLOCK | O_APPEND);
+    flags = fcntl(fileDescriptors[Write], F_GETFL);
+    RELEASE_ASSERT(result != -1);
+    RELEASE_ASSERT((flags & O_NONBLOCK) && (flags & O_APPEND));
+
+    flags = fcntl(fileDescriptors[Read], F_GETFL);
+    result = fcntl(fileDescriptors[Read], F_SETFL, flags & ~O_NONBLOCK);
+    flags = fcntl(fileDescriptors[Read], F_GETFL);
+    RELEASE_ASSERT(result != -1);
+    RELEASE_ASSERT(!(flags & O_NONBLOCK));
+}
+
+SUPPRESS_ASAN
+MessageStatus sendMessageScoped(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) {
+            Thread* thread = Thread::currentMayBeNull();
+
+            if (!thread) {
+                dataLogLn("We somehow got a message on a thread that didn't have a WTF::Thread initialized, we probably deadlocked, halp.");
+                return SignalAction::NotHandled;
+            }
+
+            thread->threadMessages().consumeAll([&] (ThreadMessageData* data) {
+                data->message(info, static_cast<ucontext_t*>(uap));
+                data->ran.store(true);
+            });
+
+            while (write(fileDescriptors[Write], magicByte, 1) == -1)
+                ASSERT(errno == EAGAIN);
+
+            return SignalAction::Handled;
+        });
+    });
+
+
+    // Since we are guarenteed not to return until we get a response from the other thread this is ok.
+    ThreadMessageData data(message);
+
+    thread.threadMessages().add(&data);
+    bool result = thread.signal(toSystemSignal(signal));
+    if (!result)
+        return MessageStatus::ThreadExited;
+    RELEASE_ASSERT(result);
+
+    static StaticLock readLock;
+    while (true) {
+        LockHolder locker(readLock);
+        constexpr size_t bufferSize = 16;
+        char buffer[bufferSize];
+
+        // It's always safe to clear the pipe because only one thread can ever block trying to read
+        // from the pipe. Thus, each byte we clear from the pipe actually just corresponds to some task
+        // that has already finished. We actively want to ensure that the pipe does not overfill because
+        // otherwise our writers might spin trying to write.
+        auto clearPipe = [&] {
+            int flags = fcntl(fileDescriptors[Read], F_GETFL);
+            ASSERT(!(flags & O_NONBLOCK));
+            fcntl(fileDescriptors[Read], F_SETFL, flags | O_NONBLOCK);
+
+            while (read(fileDescriptors[Read], buffer, bufferSize) != -1) { }
+            ASSERT(errno == EAGAIN);
+
+            fcntl(fileDescriptors[Read], F_SETFL, flags);
+        };
+
+        if (data.ran.load()) {
+            clearPipe();
+            return MessageStatus::MessageRan;
+        }
+
+        read(fileDescriptors[Read], buffer, 1);
+        ASSERT(buffer[0] == magicByte[0]);
+        clearPipe();
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+
+#endif // USE(PTHREADS)
diff --git a/Source/WTF/wtf/ThreadMessage.h b/Source/WTF/wtf/ThreadMessage.h
new file mode 100644 (file)
index 0000000..9ae53f2
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <wtf/Ref.h>
+#include <wtf/ScopedLambda.h>
+#include <wtf/Threading.h>
+
+#if USE(PTHREADS)
+
+namespace WTF {
+
+void initializeThreadMessages();
+
+class ThreadMessageData;
+using ThreadMessage = ScopedLambda<void(siginfo_t*, ucontext_t*)>;
+
+enum class MessageStatus {
+    MessageRan,
+    ThreadExited,
+};
+
+// This method allows us to send a message which will be run in a signal handler on the desired thread.
+// There are several caveates to this method however, This function uses signals so your message should
+// be sync signal safe.
+MessageStatus sendMessageScoped(Thread&, const ThreadMessage&);
+
+template<typename Functor>
+MessageStatus sendMessage(Thread& targetThread, const Functor& func)
+{
+    auto lambda = scopedLambdaRef<void(siginfo_t*, ucontext_t*)>(func);
+    return sendMessageScoped(targetThread, lambda);
+}
+
+} // namespace WTF
+
+using WTF::sendMessage;
+
+#endif // USE(PTHREADS)
index fef5811..15a5c68 100644 (file)
@@ -34,6 +34,7 @@
 #include <wtf/DateMath.h>
 #include <wtf/RandomNumberSeed.h>
 #include <wtf/ThreadHolder.h>
+#include <wtf/ThreadMessage.h>
 #include <wtf/WTFThreadData.h>
 #include <wtf/text/StringView.h>
 
@@ -178,6 +179,9 @@ void initializeThreading()
         wtfThreadData();
         initializeDates();
         Thread::initializePlatformThreading();
+#if USE(PTHREADS)
+        initializeThreadMessages();
+#endif
     });
 }
 
index bd1f8f3..c20a65e 100644 (file)
@@ -42,6 +42,7 @@
 #include <wtf/Atomics.h>
 #include <wtf/Expected.h>
 #include <wtf/Locker.h>
+#include <wtf/LocklessBag.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/PlatformRegisters.h>
 #include <wtf/PrintStream.h>
@@ -56,6 +57,8 @@
 
 namespace WTF {
 
+class ThreadMessageData;
+
 using ThreadIdentifier = uint32_t;
 typedef void (*ThreadFunction)(void* argument);
 
@@ -75,6 +78,7 @@ public:
 
     // Returns Thread object.
     WTF_EXPORT_PRIVATE static Thread& current();
+    WTF_EXPORT_PRIVATE static Thread* currentMayBeNull();
 
     // Returns ThreadIdentifier directly. It is useful if the user only cares about identity
     // of threads. At that time, users should know that holding this ThreadIdentifier does not ensure
@@ -132,7 +136,11 @@ public:
 
     static void initializePlatformThreading();
 
-private:
+#if USE(PTHREADS)
+    LocklessBag<ThreadMessageData*>& threadMessages() { return m_threadMessages; }
+#endif
+
+protected:
     Thread();
 
     // Internal platform-specific Thread::create implementation.
@@ -177,6 +185,8 @@ private:
     bool m_didExit { false };
 #if USE(PTHREADS)
     pthread_t m_handle;
+
+    LocklessBag<ThreadMessageData*> m_threadMessages;
 #if OS(DARWIN)
     mach_port_t m_platformThread;
 #else
index 3ee9a5f..1a5d99f 100644 (file)
@@ -264,11 +264,18 @@ void Thread::detach()
         didBecomeDetached();
 }
 
-Thread& Thread::current()
+Thread* Thread::currentMayBeNull()
 {
     ThreadHolder* data = ThreadHolder::current();
     if (data)
-        return data->thread();
+        return &data->thread();
+    return nullptr;
+}
+
+Thread& Thread::current()
+{
+    if (Thread* current = currentMayBeNull())
+        return *current;
 
     // Not a WTF-created thread, ThreadIdentifier is not established yet.
     RefPtr<Thread> thread = adoptRef(new Thread());
@@ -286,6 +293,8 @@ ThreadIdentifier Thread::currentID()
 bool Thread::signal(int signalNumber)
 {
     std::unique_lock<std::mutex> locker(m_mutex);
+    if (hasExited())
+        return false;
     int errNo = pthread_kill(m_handle, signalNumber);
     return !errNo; // A 0 errNo means success.
 }
diff --git a/Source/WTF/wtf/threads/Signals.cpp b/Source/WTF/wtf/threads/Signals.cpp
new file mode 100644 (file)
index 0000000..b8c53dc
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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 "config.h"
+#include "Signals.h"
+
+#if USE(PTHREADS)
+
+#include <mutex>
+#include <signal.h>
+#include <wtf/Atomics.h>
+#include <wtf/DataLog.h>
+#include <wtf/LocklessBag.h>
+
+namespace WTF {
+
+static LocklessBag<SignalHandler> handlers[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)
+{
+    Signal signal = fromSystemSignal(sig);
+
+    auto restoreDefault = [&] {
+        struct sigaction defaultAction;
+        defaultAction.sa_handler = SIG_DFL;
+        sigfillset(&defaultAction.sa_mask);
+        defaultAction.sa_flags = 0;
+        auto result = sigaction(sig, &defaultAction, nullptr);
+        dataLogLnIf(!result, "Unable to restore the default handler while proccessing signal ", sig, " the process is probably deadlocked.");
+    };
+
+    // This shouldn't happen but we might as well be careful.
+    if (signal == Signal::Unknown) {
+        dataLogLn("We somehow got called for an unknown signal ", sig, ", halp.");
+        restoreDefault();
+        return;
+    }
+
+    bool didHandle = false;
+    bool restoreDefaultHandler = false;
+    handlers[static_cast<size_t>(signal)].iterate([&] (const SignalHandler& handler) {
+        switch (handler(sig, info, mcontext)) {
+        case SignalAction::Handled:
+            didHandle = true;
+            break;
+        case SignalAction::ForceDefault:
+            restoreDefaultHandler = true;
+            break;
+        default:
+            break;
+        }
+    });
+
+    if (restoreDefaultHandler) {
+        restoreDefault();
+        return;
+    }
+
+    struct sigaction& oldAction = oldActions[static_cast<size_t>(signal)];
+    if (signal == Signal::Usr) {
+        if (oldAction.sa_sigaction)
+            oldAction.sa_sigaction(sig, info, mcontext);
+        return;
+    }
+
+    if (!didHandle) {
+        if (oldAction.sa_sigaction) {
+            oldAction.sa_sigaction(sig, info, mcontext);
+            return;
+        }
+
+        restoreDefault();
+        return;
+    }
+}
+
+void installSignalHandler(Signal signal, SignalHandler&& handler)
+{
+    ASSERT(signal < Signal::Unknown);
+    handlers[static_cast<size_t>(signal)].add(WTFMove(handler));
+
+    std::call_once(initializeOnceFlags[static_cast<size_t>(signal)], [&] {
+        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);
+    });
+}
+
+} // namespace WTF
+
+#endif // USE(PTHREADS)
diff --git a/Source/WTF/wtf/threads/Signals.h b/Source/WTF/wtf/threads/Signals.h
new file mode 100644 (file)
index 0000000..03a1d2e
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#if USE(PTHREADS)
+
+#include <signal.h>
+#include <wtf/Function.h>
+
+namespace WTF {
+
+enum class Signal {
+    // Usr will always chain to any non-default handler install before us. Since there is no way to know
+    // if a signal was intended exclusively for us.
+    Usr,
+
+    // These signals will only chain if we don't have a handler that can process them. If there is nothing
+    // to chain to we restore the default handler and crash.
+    Trap,
+    Ill,
+    SegV,
+    Bus,
+    NumberOfSignals,
+    Unknown = NumberOfSignals
+};
+
+inline int toSystemSignal(Signal signal)
+{
+    switch (signal) {
+    case Signal::SegV: return SIGSEGV;
+    case Signal::Bus: return SIGBUS;
+    case Signal::Ill: return SIGILL;
+    case Signal::Usr: return SIGUSR2;
+    case Signal::Trap: return SIGTRAP;
+    default: break;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+inline Signal fromSystemSignal(int signal)
+{
+    switch (signal) {
+    case SIGSEGV: return Signal::SegV;
+    case SIGBUS: return Signal::Bus;
+    case SIGILL: return Signal::Ill;
+    case SIGUSR2: return Signal::Usr;
+    case SIGTRAP: return Signal::Trap;
+    default: return Signal::Unknown;
+    }
+}
+
+enum class SignalAction {
+    Handled,
+    NotHandled,
+    ForceDefault
+};
+
+using SignalHandler = Function<SignalAction(int, siginfo_t*, void*)>;
+
+// 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.
+// This function is currently a one way street i.e. once installed, a signal handler cannot be uninstalled.
+void installSignalHandler(Signal, SignalHandler&&);
+
+} // namespace WTF
+
+using WTF::Signal;
+using WTF::toSystemSignal;
+using WTF::fromSystemSignal;
+using WTF::SignalAction;
+using WTF::installSignalHandler;
+
+#endif // USE(PTHREADS)
index 178ede4..a5ff7ed 100644 (file)
@@ -1,3 +1,17 @@
+2017-04-20  Keith Miller  <keith_miller@apple.com>
+
+        Add signaling API
+        https://bugs.webkit.org/show_bug.cgi?id=170976
+
+        Reviewed by Filip Pizlo.
+
+        Add tests for ThreadMessages.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WTF/ThreadMessages.cpp: Added.
+        (runThreadMessageTest):
+        (TEST):
+
 2017-04-20  Conrad Shultz  <conrad_shultz@apple.com>
 
         commit-log-editor should respect the git editor if one is set
index 0d85b2b..6378b6d 100644 (file)
                52B8CF9815868D9100281053 /* SetDocumentURI.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 52B8CF9415868CF000281053 /* SetDocumentURI.html */; };
                52D673EE1AFB127300FA19FE /* WKPageCopySessionStateWithFiltering.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52D673EC1AFB126800FA19FE /* WKPageCopySessionStateWithFiltering.cpp */; };
                52E5CE4914D21EAB003B2BD8 /* ParentFrame_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52E5CE4814D21EAB003B2BD8 /* ParentFrame_Bundle.cpp */; };
+               5311BD5E1EA9490E00525281 /* ThreadMessages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5311BD5D1EA9490D00525281 /* ThreadMessages.cpp */; };
                531C1D8E1DF8EF72006E979F /* LEBDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 531C1D8D1DF8EF72006E979F /* LEBDecoder.cpp */; };
                536770341CC8022800D425B1 /* WebScriptObjectDescription.mm in Sources */ = {isa = PBXBuildFile; fileRef = 536770331CC8022800D425B1 /* WebScriptObjectDescription.mm */; };
                536770361CC81B6100D425B1 /* WebScriptObjectDescription.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 536770351CC812F900D425B1 /* WebScriptObjectDescription.html */; };
                52D673EC1AFB126800FA19FE /* WKPageCopySessionStateWithFiltering.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKPageCopySessionStateWithFiltering.cpp; sourceTree = "<group>"; };
                52E5CE4514D21E9D003B2BD8 /* ParentFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParentFrame.cpp; sourceTree = "<group>"; };
                52E5CE4814D21EAB003B2BD8 /* ParentFrame_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParentFrame_Bundle.cpp; sourceTree = "<group>"; };
+               5311BD5D1EA9490D00525281 /* ThreadMessages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadMessages.cpp; sourceTree = "<group>"; };
                531C1D8D1DF8EF72006E979F /* LEBDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LEBDecoder.cpp; sourceTree = "<group>"; };
                536770331CC8022800D425B1 /* WebScriptObjectDescription.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebScriptObjectDescription.mm; sourceTree = "<group>"; };
                536770351CC812F900D425B1 /* WebScriptObjectDescription.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = WebScriptObjectDescription.html; sourceTree = "<group>"; };
                                7C74D42D188228F300E5ED57 /* StringView.cpp */,
                                5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */,
                                9329AA281DE3F81E003ABD07 /* TextBreakIterator.cpp */,
+                               5311BD5D1EA9490D00525281 /* ThreadMessages.cpp */,
                                0F2C20B71DCD544800542D9E /* Time.cpp */,
                                5C5E633D1D0B67940085A025 /* UniqueRef.cpp */,
                                7CD0D5AA1D5534DE000CC9E1 /* Variant.cpp */,
                                7C83DEE01D0A590C00FEBCF3 /* IntegerToStringConversion.cpp in Sources */,
                                531C1D8E1DF8EF72006E979F /* LEBDecoder.cpp in Sources */,
                                7C83DEE81D0A590C00FEBCF3 /* ListHashSet.cpp in Sources */,
+                               5311BD5E1EA9490E00525281 /* ThreadMessages.cpp in Sources */,
                                7C83DF1D1D0A590C00FEBCF3 /* Lock.cpp in Sources */,
                                7C83DEED1D0A590C00FEBCF3 /* MathExtras.cpp in Sources */,
                                7C83DEEF1D0A590C00FEBCF3 /* MD5.cpp in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WTF/ThreadMessages.cpp b/Tools/TestWebKitAPI/Tests/WTF/ThreadMessages.cpp
new file mode 100644 (file)
index 0000000..09a2773
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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. AND ITS CONTRIBUTORS ``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 ITS 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 "config.h"
+
+#include <type_traits>
+#include <wtf/DataLog.h>
+#include <wtf/HashSet.h>
+#include <wtf/Ref.h>
+#include <wtf/ThreadMessage.h>
+#include <wtf/Vector.h>
+#include <wtf/threads/Signals.h>
+
+static void runThreadMessageTest(unsigned numSenders, unsigned numMessages)
+{
+    bool receiverShouldKeepRunning = true;
+    dataLogLn("starting");
+    RefPtr<Thread> receiverThread = Thread::create("ThreadMessage receiver", [&receiverShouldKeepRunning] () {
+        while (receiverShouldKeepRunning) { }
+    });
+    ASSERT_TRUE(receiverThread);
+
+    Vector<RefPtr<Thread>> senderThreads(numSenders);
+    Vector<unsigned> messagesRun(numSenders);
+    Vector<unsigned> handlersRun(numSenders);
+    messagesRun.fill(0);
+    handlersRun.fill(0);
+
+    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*) {
+                    handlersRun[senderID]++;
+                });
+                EXPECT_TRUE(result == WTF::MessageStatus::MessageRan);
+                messagesRun[senderID]++;
+            }
+        });
+        ASSERT_TRUE(senderThreads[senderID]);
+    }
+
+    for (unsigned i = 0; i < numSenders; ++i)
+        senderThreads[i]->waitForCompletion();
+
+    receiverShouldKeepRunning = false;
+    receiverThread->waitForCompletion();
+
+    for (unsigned i = 0; i < numSenders; ++i) {
+        EXPECT_EQ(numMessages, messagesRun[i]);
+        EXPECT_EQ(numMessages, handlersRun[i]);
+    }
+}
+
+TEST(ThreadMessage, Basic)
+{
+    runThreadMessageTest(1, 1);
+    runThreadMessageTest(1, 100);
+}
+
+TEST(ThreadMessage, MultipleSenders)
+{
+    runThreadMessageTest(10, 1);
+    runThreadMessageTest(10, 100);
+    runThreadMessageTest(10, 10000);
+}
+
+class ReflectedThread : public Thread {
+public:
+    using Thread::m_mutex;
+    using Thread::m_handle;
+    using Thread::hasExited;
+};
+
+TEST(ThreadMessage, SignalsWorkOnExit)
+{
+    static bool handlerRan = false;
+    installSignalHandler(Signal::Usr, [] (int, siginfo_t*, void*) -> SignalAction {
+        dataLogLn("here");
+        handlerRan = true;
+        return SignalAction::Handled;
+    });
+
+    bool receiverShouldKeepRunning = true;
+    RefPtr<Thread> receiverThread = (Thread::create("ThreadMessage receiver",
+        [&receiverShouldKeepRunning] () {
+            while (receiverShouldKeepRunning) { }
+    }));
+    ASSERT_TRUE(receiverThread);
+
+    bool signalFired;
+    {
+        std::unique_lock<std::mutex> locker(static_cast<ReflectedThread*>(receiverThread.get())->m_mutex);
+        receiverShouldKeepRunning = false;
+        EXPECT_FALSE(static_cast<ReflectedThread*>(receiverThread.get())->hasExited());
+        sleep(1);
+        signalFired = !pthread_kill(static_cast<ReflectedThread*>(receiverThread.get())->m_handle, toSystemSignal(Signal::Usr));
+    }
+
+    receiverThread->waitForCompletion();
+    EXPECT_TRUE(handlerRan || !signalFired);
+}
+