Title: [219653] trunk
Revision
219653
Author
utatane....@gmail.com
Date
2017-07-19 01:43:57 -0700 (Wed, 19 Jul 2017)

Log Message

[WTF] Implement WTF::ThreadGroup
https://bugs.webkit.org/show_bug.cgi?id=174081

Reviewed by Mark Lam.

Source/_javascript_Core:

Large part of MachineThreads are now removed and replaced with WTF::ThreadGroup.
And SamplingProfiler and others interact with WTF::Thread directly.

* API/tests/ExecutionTimeLimitTest.cpp:
* heap/MachineStackMarker.cpp:
(JSC::MachineThreads::MachineThreads):
(JSC::captureStack):
(JSC::MachineThreads::tryCopyOtherThreadStack):
(JSC::MachineThreads::tryCopyOtherThreadStacks):
(JSC::MachineThreads::gatherConservativeRoots):
(JSC::ActiveMachineThreadsManager::Locker::Locker): Deleted.
(JSC::ActiveMachineThreadsManager::add): Deleted.
(JSC::ActiveMachineThreadsManager::remove): Deleted.
(JSC::ActiveMachineThreadsManager::contains): Deleted.
(JSC::ActiveMachineThreadsManager::ActiveMachineThreadsManager): Deleted.
(JSC::activeMachineThreadsManager): Deleted.
(JSC::MachineThreads::~MachineThreads): Deleted.
(JSC::MachineThreads::addCurrentThread): Deleted.
(): Deleted.
(JSC::MachineThreads::removeThread): Deleted.
(JSC::MachineThreads::removeThreadIfFound): Deleted.
(JSC::MachineThreads::MachineThread::MachineThread): Deleted.
(JSC::MachineThreads::MachineThread::getRegisters): Deleted.
(JSC::MachineThreads::MachineThread::Registers::stackPointer): Deleted.
(JSC::MachineThreads::MachineThread::Registers::framePointer): Deleted.
(JSC::MachineThreads::MachineThread::Registers::instructionPointer): Deleted.
(JSC::MachineThreads::MachineThread::Registers::llintPC): Deleted.
(JSC::MachineThreads::MachineThread::captureStack): Deleted.
* heap/MachineStackMarker.h:
(JSC::MachineThreads::addCurrentThread):
(JSC::MachineThreads::getLock):
(JSC::MachineThreads::threads):
(JSC::MachineThreads::MachineThread::suspend): Deleted.
(JSC::MachineThreads::MachineThread::resume): Deleted.
(JSC::MachineThreads::MachineThread::threadID): Deleted.
(JSC::MachineThreads::MachineThread::stackBase): Deleted.
(JSC::MachineThreads::MachineThread::stackEnd): Deleted.
(JSC::MachineThreads::threadsListHead): Deleted.
* runtime/SamplingProfiler.cpp:
(JSC::FrameWalker::isValidFramePointer):
(JSC::SamplingProfiler::SamplingProfiler):
(JSC::SamplingProfiler::takeSample):
(JSC::SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread):
* runtime/SamplingProfiler.h:
* wasm/WasmMachineThreads.cpp:
(JSC::Wasm::resetInstructionCacheOnAllThreads):

Source/WebCore:

* page/ResourceUsageThread.h:

Source/WebKit:

* Shared/AsyncRequest.h:

Source/WTF:

This patch implements WTF::ThreadGroup. It implements core of JSC::MachineThreads with more reliable way.
JSC::MachineThreads was complicated because of managing dead threads. Each JSC::MachineThreads has its
own TLS with a registered destructor. And everytime a thread dies, the registered TLS destructor is called.
And this destructor will remove the current dying thread from JSC::MachineThreads.

However the above implementation is tricky. And each JSC::MachineThreads requires own TLS space, which is
not considered in WTF's Windows ThreadSpecific implementation. Current design works well since we only
have small number of MachineThreads right now.

Instead, we use more reliable way. After introducing WTF::Thread, WTF::Thread has WTF::Thread::didExit,
which is called when associated TLS (with WTF::Thread) is destroyed. We leverage this mechanism to remove
WTF::Thread from MachineThreads.

This patch introduces WTF::ThreadGroup. It is tightly integrated with WTF::Thread: WTF::Thread knows
ThreadGroups which includes this thread. And WTF::ThreadGroup of course knows WTF::Threads added to it.
WTF::Thread::didExit carefully remove itself from WTF::ThreadGroups.

The most important part of this patch is locking. WTF::Thread can die. And WTF::ThreadGroup can die.
If we take a design using two fine grain locks in WTF::Thread and WTF::ThreadGroup, we easily encounter
dead lock. Consider the following case.

1. When adding WTF::Thread (TH) to WTF::ThreadGroup (THG), we first hold a lock of THG, and hold a lock of TH (locking order is THG -> TH).
2. When TH dies, TH need to hold a lock of TH to iterate THGs. And we hold a lock of THG to unregister TH from it (locking order is TH -> THG).
3. When suspending and resuming THs in THG, we first hold a lock of THG. And then, we hold a lock of TH to suspend and resume it (locking order is THG -> TH).
4. When destroying THG, we need to hold a lock of TH to unregister THG from TH. We can hold a lock of THG before that (locking order is THG -> TH).

Then, it easily causes dead lock. We cannot swap the locking order of (2) since iterating THG requires a lock of TH.
To solve this problem, we use std::shared_ptr and std::weak_ptr.

1. When adding WTF::Thread (TH) to WTF::ThreadGroup (THG), we first hold THG, and hold a lock of TH. (THG -> TH)
2. When TH dies, TH first hold lock of TH. And we use std::weak_ptr<>::lock() to retain non-destructed ThreadGroups.
If some of ThreadGroups are dying, we just ignore them. It is ok because such a ThreadGroup will be destructed. So we do not need to unregister this thread from
such a ThreadGroup. Then, we have Vector<std::shared_ptr<ThreadGroup>>. So we unlock a lock of TH. To unregister a thread from thread group, we first hold a
lock of THG and then hold a lock of TH. Both lifetime is ensured: THG is retained by std::shared_ptr. And TH is itself. (TH), (THG -> TH).
3. When suspending and resuming THs in THG, we first hold a lock of THG. And then, we hold a lock of TH to suspend and resume it (THG -> TH).
4. When destroying THG, we hold a lock of THG. And hold a lock of TH. During holding THG's lock, registered thread never dies because (2) holds THG lock. (THG -> TH).

We also fix suspend and resume locking mechanism to avoid dead lock. We should hold the global lock when suspending and resuming.
If we use per-thread lock, the suspended thread can hold the lock of the other threads. It causes dead lock.

* WTF.xcodeproj/project.pbxproj:
* wtf/AutomaticThread.cpp:
* wtf/CMakeLists.txt:
* wtf/CrossThreadCopier.h:
* wtf/ParkingLot.h:
* wtf/ThreadGroup.cpp: Copied from Source/_javascript_Core/wasm/WasmMachineThreads.cpp.
(WTF::ThreadGroup::~ThreadGroup):
(WTF::ThreadGroup::add):
(WTF::ThreadGroup::addCurrentThread):
* wtf/ThreadGroup.h: Copied from Source/_javascript_Core/wasm/WasmMachineThreads.cpp.
(WTF::ThreadGroup::create):
(WTF::ThreadGroup::threads):
(WTF::ThreadGroup::getLock):
(WTF::ThreadGroup::weakFromThis):
* wtf/Threading.cpp:
(WTF::shouldRemoveThreadFromThreadGroup):
(WTF::Thread::didExit):
(WTF::Thread::addToThreadGroup):
(WTF::Thread::removeFromThreadGroup):
* wtf/Threading.h:
* wtf/ThreadingPthreads.cpp:
(WTF::Thread::resume):
(WTF::Thread::getRegisters):
* wtf/ThreadingWin.cpp:
(WTF::Thread::resume):
(WTF::Thread::getRegisters):

Tools:

Add WTF::ThreadGroup tests.

* TestWebKitAPI/CMakeLists.txt:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WTF/ThreadGroup.cpp: Added.
(TestWebKitAPI::testThreadGroup):
(TestWebKitAPI::TEST):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp (219652 => 219653)


--- trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -36,6 +36,7 @@
 #include <wtf/Condition.h>
 #include <wtf/CurrentTime.h>
 #include <wtf/Lock.h>
+#include <wtf/Threading.h>
 #include <wtf/text/StringBuilder.h>
 
 #if HAVE(MACH_EXCEPTIONS)

Modified: trunk/Source/_javascript_Core/ChangeLog (219652 => 219653)


--- trunk/Source/_javascript_Core/ChangeLog	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,57 @@
+2017-07-19  Yusuke Suzuki  <utatane....@gmail.com>
+
+        [WTF] Implement WTF::ThreadGroup
+        https://bugs.webkit.org/show_bug.cgi?id=174081
+
+        Reviewed by Mark Lam.
+
+        Large part of MachineThreads are now removed and replaced with WTF::ThreadGroup.
+        And SamplingProfiler and others interact with WTF::Thread directly.
+
+        * API/tests/ExecutionTimeLimitTest.cpp:
+        * heap/MachineStackMarker.cpp:
+        (JSC::MachineThreads::MachineThreads):
+        (JSC::captureStack):
+        (JSC::MachineThreads::tryCopyOtherThreadStack):
+        (JSC::MachineThreads::tryCopyOtherThreadStacks):
+        (JSC::MachineThreads::gatherConservativeRoots):
+        (JSC::ActiveMachineThreadsManager::Locker::Locker): Deleted.
+        (JSC::ActiveMachineThreadsManager::add): Deleted.
+        (JSC::ActiveMachineThreadsManager::remove): Deleted.
+        (JSC::ActiveMachineThreadsManager::contains): Deleted.
+        (JSC::ActiveMachineThreadsManager::ActiveMachineThreadsManager): Deleted.
+        (JSC::activeMachineThreadsManager): Deleted.
+        (JSC::MachineThreads::~MachineThreads): Deleted.
+        (JSC::MachineThreads::addCurrentThread): Deleted.
+        (): Deleted.
+        (JSC::MachineThreads::removeThread): Deleted.
+        (JSC::MachineThreads::removeThreadIfFound): Deleted.
+        (JSC::MachineThreads::MachineThread::MachineThread): Deleted.
+        (JSC::MachineThreads::MachineThread::getRegisters): Deleted.
+        (JSC::MachineThreads::MachineThread::Registers::stackPointer): Deleted.
+        (JSC::MachineThreads::MachineThread::Registers::framePointer): Deleted.
+        (JSC::MachineThreads::MachineThread::Registers::instructionPointer): Deleted.
+        (JSC::MachineThreads::MachineThread::Registers::llintPC): Deleted.
+        (JSC::MachineThreads::MachineThread::captureStack): Deleted.
+        * heap/MachineStackMarker.h:
+        (JSC::MachineThreads::addCurrentThread):
+        (JSC::MachineThreads::getLock):
+        (JSC::MachineThreads::threads):
+        (JSC::MachineThreads::MachineThread::suspend): Deleted.
+        (JSC::MachineThreads::MachineThread::resume): Deleted.
+        (JSC::MachineThreads::MachineThread::threadID): Deleted.
+        (JSC::MachineThreads::MachineThread::stackBase): Deleted.
+        (JSC::MachineThreads::MachineThread::stackEnd): Deleted.
+        (JSC::MachineThreads::threadsListHead): Deleted.
+        * runtime/SamplingProfiler.cpp:
+        (JSC::FrameWalker::isValidFramePointer):
+        (JSC::SamplingProfiler::SamplingProfiler):
+        (JSC::SamplingProfiler::takeSample):
+        (JSC::SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread):
+        * runtime/SamplingProfiler.h:
+        * wasm/WasmMachineThreads.cpp:
+        (JSC::Wasm::resetInstructionCacheOnAllThreads):
+
 2017-07-18  Andy Estes  <aes...@apple.com>
 
         [Xcode] Enable CLANG_WARN_RANGE_LOOP_ANALYSIS

Modified: trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp (219652 => 219653)


--- trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -23,16 +23,11 @@
 #include "MachineStackMarker.h"
 
 #include "ConservativeRoots.h"
-#include "GPRInfo.h"
-#include "Heap.h"
-#include "JSArray.h"
-#include "JSCInlines.h"
-#include "LLIntPCRanges.h"
-#include "MacroAssembler.h"
-#include "VM.h"
+#include "MachineContext.h"
 #include <setjmp.h>
 #include <stdlib.h>
-#include <wtf/MainThread.h>
+#include <wtf/BitVector.h>
+#include <wtf/PageBlock.h>
 #include <wtf/StdLibExtras.h>
 
 using namespace WTF;
@@ -39,190 +34,11 @@
 
 namespace JSC {
 
-class ActiveMachineThreadsManager;
-static ActiveMachineThreadsManager& activeMachineThreadsManager();
-
-class ActiveMachineThreadsManager {
-    WTF_MAKE_NONCOPYABLE(ActiveMachineThreadsManager);
-public:
-
-    class Locker {
-    public:
-        Locker(ActiveMachineThreadsManager& manager)
-            : m_locker(manager.m_lock)
-        {
-        }
-
-    private:
-        LockHolder m_locker;
-    };
-
-    void add(MachineThreads* machineThreads)
-    {
-        LockHolder managerLock(m_lock);
-        m_set.add(machineThreads);
-    }
-
-    void THREAD_SPECIFIC_CALL remove(MachineThreads* machineThreads)
-    {
-        LockHolder managerLock(m_lock);
-        auto recordedMachineThreads = m_set.take(machineThreads);
-        RELEASE_ASSERT(recordedMachineThreads == machineThreads);
-    }
-
-    bool contains(MachineThreads* machineThreads)
-    {
-        return m_set.contains(machineThreads);
-    }
-
-private:
-    typedef HashSet<MachineThreads*> MachineThreadsSet;
-
-    ActiveMachineThreadsManager() { }
-    
-    Lock m_lock;
-    MachineThreadsSet m_set;
-
-    friend ActiveMachineThreadsManager& activeMachineThreadsManager();
-};
-
-static ActiveMachineThreadsManager& activeMachineThreadsManager()
-{
-    static std::once_flag initializeManagerOnceFlag;
-    static ActiveMachineThreadsManager* manager = nullptr;
-
-    std::call_once(initializeManagerOnceFlag, [] {
-        manager = new ActiveMachineThreadsManager();
-    });
-    return *manager;
-}
-
-#if CPU(X86_64) && OS(DARWIN)
-#define FILL_CALLEE_SAVES_FOR_CRASH_INFO(number)     \
-    asm volatile(                                    \
-        "movq $0xc0defefe000000" number ", %%rbx;" \
-        "movq $0xc0defefe000000" number ", %%r12;" \
-        "movq $0xc0defefe000000" number ", %%r13;" \
-        "movq $0xc0defefe000000" number ", %%r14;" \
-        "movq $0xc0defefe000000" number ", %%r15;" \
-        :                                            \
-        :                                            \
-        : "%rbx", "%r12", "%r13", "%r14", "%r15"     \
-    );
-
-#define FILL_CALLER_SAVES_FOR_CRASH_INFO(number)     \
-    asm volatile(                                    \
-        "movq $0xc0defefe000000" number ", %%rax;" \
-        "movq $0xc0defefe000000" number ", %%rdi;" \
-        "movq $0xc0defefe000000" number ", %%rsi;" \
-        "movq $0xc0defefe000000" number ", %%rdx;" \
-        "movq $0xc0defefe000000" number ", %%rcx;" \
-        "movq $0xc0defefe000000" number ", %%r8;"  \
-        "movq $0xc0defefe000000" number ", %%r9;"  \
-        "movq $0xc0defefe000000" number ", %%r10;" \
-        "movq $0xc0defefe000000" number ", %%r11;" \
-        :                                            \
-        :                                            \
-        : "%rax", "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9", "%r10", "%r11" \
-    );
-#else
-#define FILL_CALLEE_SAVES_FOR_CRASH_INFO(number)
-#define FILL_CALLER_SAVES_FOR_CRASH_INFO(number)
-#endif
-
 MachineThreads::MachineThreads()
-    : m_registeredThreads()
-    , m_threadSpecificForMachineThreads(0)
+    : m_threadGroup(ThreadGroup::create())
 {
-    FILL_CALLEE_SAVES_FOR_CRASH_INFO("01");
-    threadSpecificKeyCreate(&m_threadSpecificForMachineThreads, removeThread);
-    FILL_CALLEE_SAVES_FOR_CRASH_INFO("02");
-    activeMachineThreadsManager().add(this);
-    FILL_CALLER_SAVES_FOR_CRASH_INFO("03");
 }
 
-MachineThreads::~MachineThreads()
-{
-    activeMachineThreadsManager().remove(this);
-    threadSpecificKeyDelete(m_threadSpecificForMachineThreads);
-
-    LockHolder registeredThreadsLock(m_registeredThreadsMutex);
-    for (MachineThread* current = m_registeredThreads.head(); current;) {
-        MachineThread* next = current->next();
-        delete current;
-        current = next;
-    }
-}
-
-void MachineThreads::addCurrentThread()
-{
-    if (threadSpecificGet(m_threadSpecificForMachineThreads)) {
-#ifndef NDEBUG
-        LockHolder lock(m_registeredThreadsMutex);
-        ASSERT(threadSpecificGet(m_threadSpecificForMachineThreads) == this);
-#endif
-        return;
-    }
-
-    MachineThread* thread = new MachineThread();
-    threadSpecificSet(m_threadSpecificForMachineThreads, this);
-
-    LockHolder lock(m_registeredThreadsMutex);
-
-    m_registeredThreads.append(thread);
-}
-
-auto MachineThreads::machineThreadForCurrentThread() -> MachineThread*
-{
-    LockHolder lock(m_registeredThreadsMutex);
-    ThreadIdentifier id = currentThread();
-    for (MachineThread* thread = m_registeredThreads.head(); thread; thread = thread->next()) {
-        if (thread->threadID() == id)
-            return thread;
-    }
-
-    RELEASE_ASSERT_NOT_REACHED();
-    return nullptr;
-}
-
-void THREAD_SPECIFIC_CALL MachineThreads::removeThread(void* p)
-{
-    auto& manager = activeMachineThreadsManager();
-    ActiveMachineThreadsManager::Locker lock(manager);
-    auto machineThreads = static_cast<MachineThreads*>(p);
-    if (manager.contains(machineThreads)) {
-        // There's a chance that the MachineThreads registry that this thread
-        // was registered with was already destructed, and another one happened
-        // to be instantiated at the same address. Hence, this thread may or
-        // may not be found in this MachineThreads registry. We only need to
-        // do a removal if this thread is found in it.
-
-#if OS(WINDOWS)
-        // On Windows the thread specific destructor is also called when the
-        // main thread is exiting. This may lead to the main thread waiting
-        // forever for the machine thread lock when exiting, if the sampling
-        // profiler thread was terminated by the system while holding the
-        // machine thread lock.
-        if (WTF::isMainThread())
-            return;
-#endif
-
-        machineThreads->removeThreadIfFound(currentThread());
-    }
-}
-
-void MachineThreads::removeThreadIfFound(ThreadIdentifier id)
-{
-    LockHolder lock(m_registeredThreadsMutex);
-    for (MachineThread* current = m_registeredThreads.head(); current; current = current->next()) {
-        if (current->threadID() == id) {
-            m_registeredThreads.remove(current);
-            delete current;
-            break;
-        }
-    }
-}
-
 SUPPRESS_ASAN
 void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, CurrentThreadState& currentThreadState)
 {
@@ -235,52 +51,6 @@
     conservativeRoots.add(currentThreadState.stackTop, currentThreadState.stackOrigin, jitStubRoutines, codeBlocks);
 }
 
-MachineThreads::MachineThread::MachineThread()
-    : m_thread(WTF::Thread::current())
-{
-}
-
-size_t MachineThreads::MachineThread::getRegisters(MachineThread::Registers& registers)
-{
-    WTF::PlatformRegisters& regs = registers.regs;
-    return m_thread->getRegisters(regs);
-}
-
-void* MachineThreads::MachineThread::Registers::stackPointer() const
-{
-    return MachineContext::stackPointer(regs);
-}
-
-#if ENABLE(SAMPLING_PROFILER)
-void* MachineThreads::MachineThread::Registers::framePointer() const
-{
-#if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)
-    return MachineContext::framePointer(regs);
-#else
-#error Need a way to get the frame pointer for another thread on this platform
-#endif
-}
-
-void* MachineThreads::MachineThread::Registers::instructionPointer() const
-{
-#if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)
-    return MachineContext::instructionPointer(regs);
-#else
-#error Need a way to get the instruction pointer for another thread on this platform
-#endif
-}
-
-void* MachineThreads::MachineThread::Registers::llintPC() const
-{
-    // LLInt uses regT4 as PC.
-#if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)
-    return MachineContext::llintInstructionPointer(regs);
-#else
-#error Need a way to get the LLIntPC for another thread on this platform
-#endif
-}
-#endif // ENABLE(SAMPLING_PROFILER)
-
 static inline int osRedZoneAdjustment()
 {
     int redZoneAdjustment = 0;
@@ -296,9 +66,9 @@
     return redZoneAdjustment;
 }
 
-std::pair<void*, size_t> MachineThreads::MachineThread::captureStack(void* stackTop)
+static std::pair<void*, size_t> captureStack(Thread& thread, void* stackTop)
 {
-    char* begin = reinterpret_cast_ptr<char*>(stackBase());
+    char* begin = reinterpret_cast_ptr<char*>(thread.stack().origin());
     char* end = bitwise_cast<char*>(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackTop)));
     ASSERT(begin >= end);
 
@@ -305,8 +75,8 @@
     char* endWithRedZone = end + osRedZoneAdjustment();
     ASSERT(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(endWithRedZone)) == reinterpret_cast<uintptr_t>(endWithRedZone));
 
-    if (endWithRedZone < stackEnd())
-        endWithRedZone = reinterpret_cast_ptr<char*>(stackEnd());
+    if (endWithRedZone < thread.stack().end())
+        endWithRedZone = reinterpret_cast_ptr<char*>(thread.stack().end());
 
     std::swap(begin, endWithRedZone);
     return std::make_pair(begin, endWithRedZone - begin);
@@ -339,20 +109,20 @@
 // significant performance loss as tryCopyOtherThreadStack is only called as part of an O(heapsize)
 // operation. As the heap is generally much larger than the stack the performance hit is minimal.
 // See: https://bugs.webkit.org/show_bug.cgi?id=146297
-void MachineThreads::tryCopyOtherThreadStack(MachineThread* thread, void* buffer, size_t capacity, size_t* size)
+void MachineThreads::tryCopyOtherThreadStack(Thread& thread, void* buffer, size_t capacity, size_t* size)
 {
-    MachineThread::Registers registers;
-    size_t registersSize = thread->getRegisters(registers);
+    PlatformRegisters registers;
+    size_t registersSize = thread.getRegisters(registers);
 
     // This is a workaround for <rdar://problem/27607384>. libdispatch recycles work
     // queue threads without running pthread exit destructors. This can cause us to scan a
     // thread during work queue initialization, when the stack pointer is null.
-    if (UNLIKELY(!registers.stackPointer())) {
+    if (UNLIKELY(!MachineContext::stackPointer(registers))) {
         *size = 0;
         return;
     }
 
-    std::pair<void*, size_t> stack = thread->captureStack(registers.stackPointer());
+    std::pair<void*, size_t> stack = captureStack(thread, MachineContext::stackPointer(registers));
 
     bool canCopy = *size + registersSize + stack.second <= capacity;
 
@@ -365,7 +135,7 @@
     *size += stack.second;
 }
 
-bool MachineThreads::tryCopyOtherThreadStacks(const AbstractLocker&, void* buffer, size_t capacity, size_t* size)
+bool MachineThreads::tryCopyOtherThreadStacks(const AbstractLocker& locker, void* buffer, size_t capacity, size_t* size)
 {
     // Prevent two VMs from suspending each other's threads at the same time,
     // which can cause deadlock: <rdar://problem/20300842>.
@@ -374,60 +144,49 @@
 
     *size = 0;
 
-    ThreadIdentifier id = currentThread();
-    int numberOfThreads = 0; // Using 0 to denote that we haven't counted the number of threads yet.
-    int index = 1;
-    DoublyLinkedList<MachineThread> threadsToBeDeleted;
+    Thread& currentThread = Thread::current();
+    const auto& threads = m_threadGroup->threads(locker);
+    BitVector isSuspended(threads.size());
 
-    for (MachineThread* thread = m_registeredThreads.head(); thread; index++) {
-        if (thread->threadID() != id) {
-            auto result = thread->suspend();
+    {
+        unsigned index = 0;
+        for (auto& thread : threads) {
+            if (thread.get() != currentThread) {
+                auto result = thread->suspend();
+                if (result)
+                    isSuspended.set(index);
+                else {
 #if OS(DARWIN)
-            if (!result) {
-                if (!numberOfThreads)
-                    numberOfThreads = m_registeredThreads.size();
-
-                ASSERT(result.error() != KERN_SUCCESS);
-
-                WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
-                    "_javascript_ garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] id %u.",
-                    result.error(), index, numberOfThreads, thread, thread->threadID());
-
-                // Put the invalid thread on the threadsToBeDeleted list.
-                // We can't just delete it here because we have suspended other
-                // threads, and they may still be holding the C heap lock which
-                // we need for deleting the invalid thread. Hence, we need to
-                // defer the deletion till after we have resumed all threads.
-                MachineThread* nextThread = thread->next();
-                m_registeredThreads.remove(thread);
-                threadsToBeDeleted.append(thread);
-                thread = nextThread;
-                continue;
+                    // These threads will be removed from the ThreadGroup. Thus, we do not do anything here except for reporting.
+                    ASSERT(result.error() != KERN_SUCCESS);
+                    WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
+                        "_javascript_ garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] id %u.",
+                        result.error(), index, threads.size(), thread.ptr(), thread->id());
+#endif
+                }
             }
-#else
-            UNUSED_PARAM(numberOfThreads);
-            ASSERT_UNUSED(result, result);
-#endif
+            ++index;
         }
-        thread = thread->next();
     }
 
-    for (MachineThread* thread = m_registeredThreads.head(); thread; thread = thread->next()) {
-        if (thread->threadID() != id)
-            tryCopyOtherThreadStack(thread, buffer, capacity, size);
+    {
+        unsigned index = 0;
+        for (auto& thread : threads) {
+            if (isSuspended.get(index))
+                tryCopyOtherThreadStack(thread.get(), buffer, capacity, size);
+            ++index;
+        }
     }
 
-    for (MachineThread* thread = m_registeredThreads.head(); thread; thread = thread->next()) {
-        if (thread->threadID() != id)
-            thread->resume();
+    {
+        unsigned index = 0;
+        for (auto& thread : threads) {
+            if (isSuspended.get(index))
+                thread->resume();
+            ++index;
+        }
     }
 
-    for (MachineThread* thread = threadsToBeDeleted.head(); thread; ) {
-        MachineThread* nextThread = thread->next();
-        delete thread;
-        thread = nextThread;
-    }
-    
     return *size <= capacity;
 }
 
@@ -448,8 +207,8 @@
     size_t size;
     size_t capacity = 0;
     void* buffer = nullptr;
-    LockHolder lock(m_registeredThreadsMutex);
-    while (!tryCopyOtherThreadStacks(lock, buffer, capacity, &size))
+    auto locker = holdLock(m_threadGroup->getLock());
+    while (!tryCopyOtherThreadStacks(locker, buffer, capacity, &size))
         growBuffer(size, &buffer, &capacity);
 
     if (!buffer)

Modified: trunk/Source/_javascript_Core/heap/MachineStackMarker.h (219652 => 219653)


--- trunk/Source/_javascript_Core/heap/MachineStackMarker.h	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/heap/MachineStackMarker.h	2017-07-19 08:43:57 UTC (rev 219653)
@@ -21,12 +21,10 @@
 
 #pragma once
 
-#include "MachineContext.h"
 #include "RegisterState.h"
-#include <wtf/DoublyLinkedList.h>
 #include <wtf/Lock.h>
 #include <wtf/ScopedLambda.h>
-#include <wtf/ThreadSpecific.h>
+#include <wtf/ThreadGroup.h>
 
 namespace JSC {
 
@@ -45,58 +43,22 @@
     WTF_MAKE_NONCOPYABLE(MachineThreads);
 public:
     MachineThreads();
-    ~MachineThreads();
 
     void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, CurrentThreadState*);
 
-    JS_EXPORT_PRIVATE void addCurrentThread(); // Only needs to be called by clients that can use the same heap from multiple threads.
+    // Only needs to be called by clients that can use the same heap from multiple threads.
+    void addCurrentThread() { m_threadGroup->addCurrentThread(); }
 
-    class MachineThread : public DoublyLinkedListNode<MachineThread> {
-        WTF_MAKE_FAST_ALLOCATED;
-    public:
-        MachineThread();
+    std::mutex& getLock() { return m_threadGroup->getLock(); }
+    const ListHashSet<Ref<Thread>>& threads(const AbstractLocker& locker) const { return m_threadGroup->threads(locker); }
 
-        struct Registers {
-            void* stackPointer() const;
-#if ENABLE(SAMPLING_PROFILER)
-            void* framePointer() const;
-            void* instructionPointer() const;
-            void* llintPC() const;
-#endif // ENABLE(SAMPLING_PROFILER)
-            PlatformRegisters regs;
-        };
-
-        Expected<void, Thread::PlatformSuspendError> suspend() { return m_thread->suspend(); }
-        void resume() { m_thread->resume(); }
-        size_t getRegisters(Registers& regs);
-        std::pair<void*, size_t> captureStack(void* stackTop);
-
-        WTF::ThreadIdentifier threadID() const { return m_thread->id(); }
-        void* stackBase() const { return m_thread->stack().origin(); }
-        void* stackEnd() const { return m_thread->stack().end(); }
-
-        Ref<WTF::Thread> m_thread;
-        MachineThread* m_next { nullptr };
-        MachineThread* m_prev { nullptr };
-    };
-
-    Lock& getLock() { return m_registeredThreadsMutex; }
-    const DoublyLinkedList<MachineThread>& threadsListHead(const AbstractLocker&) const { ASSERT(m_registeredThreadsMutex.isLocked()); return m_registeredThreads; }
-    MachineThread* machineThreadForCurrentThread();
-
 private:
     void gatherFromCurrentThread(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, CurrentThreadState&);
 
-    void tryCopyOtherThreadStack(MachineThread*, void*, size_t capacity, size_t*);
+    void tryCopyOtherThreadStack(Thread&, void*, size_t capacity, size_t*);
     bool tryCopyOtherThreadStacks(const AbstractLocker&, void*, size_t capacity, size_t*);
 
-    static void THREAD_SPECIFIC_CALL removeThread(void*);
-
-    void removeThreadIfFound(ThreadIdentifier);
-
-    Lock m_registeredThreadsMutex;
-    DoublyLinkedList<MachineThread> m_registeredThreads;
-    WTF::ThreadSpecificKey m_threadSpecificForMachineThreads;
+    std::shared_ptr<ThreadGroup> m_threadGroup;
 };
 
 #define DECLARE_AND_COMPUTE_CURRENT_THREAD_STATE(stateName) \

Modified: trunk/Source/_javascript_Core/runtime/SamplingProfiler.cpp (219652 => 219653)


--- trunk/Source/_javascript_Core/runtime/SamplingProfiler.cpp	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/runtime/SamplingProfiler.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -39,6 +39,7 @@
 #include "JSCInlines.h"
 #include "JSFunction.h"
 #include "LLIntPCRanges.h"
+#include "MachineContext.h"
 #include "MarkedBlock.h"
 #include "MarkedBlockSet.h"
 #include "MarkedSpaceInlines.h"
@@ -165,10 +166,9 @@
     bool isValidFramePointer(void* exec)
     {
         uint8_t* fpCast = bitwise_cast<uint8_t*>(exec);
-        const auto& threadList = m_vm.heap.machineThreads().threadsListHead(m_machineThreadsLocker);
-        for (MachineThreads::MachineThread* thread = threadList.head(); thread; thread = thread->next()) {
-            uint8_t* stackBase = static_cast<uint8_t*>(thread->stackBase());
-            uint8_t* stackLimit = static_cast<uint8_t*>(thread->stackEnd());
+        for (auto& thread : m_vm.heap.machineThreads().threads(m_machineThreadsLocker)) {
+            uint8_t* stackBase = static_cast<uint8_t*>(thread->stack().origin());
+            uint8_t* stackLimit = static_cast<uint8_t*>(thread->stack().end());
             RELEASE_ASSERT(stackBase);
             RELEASE_ASSERT(stackLimit);
             if (fpCast <= stackBase && fpCast >= stackLimit)
@@ -278,7 +278,6 @@
     , m_weakRandom()
     , m_stopwatch(WTFMove(stopwatch))
     , m_timingInterval(std::chrono::microseconds(Options::sampleInterval()))
-    , m_jscExecutionThread(nullptr)
     , m_isPaused(false)
     , m_isShutDown(false)
 {
@@ -338,7 +337,7 @@
     if (m_vm.entryScope) {
         double nowTime = m_stopwatch->elapsedTime();
 
-        LockHolder machineThreadsLocker(m_vm.heap.machineThreads().getLock());
+        auto machineThreadsLocker = holdLock(m_vm.heap.machineThreads().getLock());
         LockHolder codeBlockSetLocker(m_vm.heap.codeBlockSet().getLock());
         LockHolder executableAllocatorLocker(ExecutableAllocator::singleton().getLock());
 
@@ -352,12 +351,12 @@
             bool topFrameIsLLInt = false;
             void* llintPC;
             {
-                MachineThreads::MachineThread::Registers registers;
+                PlatformRegisters registers;
                 m_jscExecutionThread->getRegisters(registers);
-                machineFrame = registers.framePointer();
+                machineFrame = MachineContext::framePointer(registers);
                 callFrame = static_cast<ExecState*>(machineFrame);
-                machinePC = registers.instructionPointer();
-                llintPC = registers.llintPC();
+                machinePC = MachineContext::instructionPointer(registers);
+                llintPC = MachineContext::llintInstructionPointer(registers);
             }
             // FIXME: Lets have a way of detecting when we're parsing code.
             // https://bugs.webkit.org/show_bug.cgi?id=152761
@@ -678,7 +677,7 @@
 void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread(const AbstractLocker&)
 {
     ASSERT(m_lock.isLocked());
-    m_jscExecutionThread = m_vm.heap.machineThreads().machineThreadForCurrentThread();
+    m_jscExecutionThread = &Thread::current();
 }
 
 void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread()

Modified: trunk/Source/_javascript_Core/runtime/SamplingProfiler.h (219652 => 219653)


--- trunk/Source/_javascript_Core/runtime/SamplingProfiler.h	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/runtime/SamplingProfiler.h	2017-07-19 08:43:57 UTC (rev 219653)
@@ -196,7 +196,7 @@
     double m_lastTime;
     Lock m_lock;
     RefPtr<Thread> m_thread;
-    MachineThreads::MachineThread* m_jscExecutionThread;
+    RefPtr<Thread> m_jscExecutionThread;
     bool m_isPaused;
     bool m_isShutDown;
     bool m_needsReportAtExit { false };

Modified: trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp (219652 => 219653)


--- trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -55,10 +55,8 @@
 void resetInstructionCacheOnAllThreads()
 {
     auto locker = holdLock(wasmThreads().getLock());
-
-    const DoublyLinkedList<MachineThreads::MachineThread>& threads = wasmThreads().threadsListHead(locker);
-    for (const auto* thread = threads.head(); thread; thread = thread->next()) {
-        sendMessage(thread->m_thread.get(), [] (const PlatformRegisters&) {
+    for (auto& thread : wasmThreads().threads(locker)) {
+        sendMessage(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 (219652 => 219653)


--- trunk/Source/WTF/ChangeLog	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/ChangeLog	2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,77 @@
+2017-07-19  Yusuke Suzuki  <utatane....@gmail.com>
+
+        [WTF] Implement WTF::ThreadGroup
+        https://bugs.webkit.org/show_bug.cgi?id=174081
+
+        Reviewed by Mark Lam.
+
+        This patch implements WTF::ThreadGroup. It implements core of JSC::MachineThreads with more reliable way.
+        JSC::MachineThreads was complicated because of managing dead threads. Each JSC::MachineThreads has its
+        own TLS with a registered destructor. And everytime a thread dies, the registered TLS destructor is called.
+        And this destructor will remove the current dying thread from JSC::MachineThreads.
+
+        However the above implementation is tricky. And each JSC::MachineThreads requires own TLS space, which is
+        not considered in WTF's Windows ThreadSpecific implementation. Current design works well since we only
+        have small number of MachineThreads right now.
+
+        Instead, we use more reliable way. After introducing WTF::Thread, WTF::Thread has WTF::Thread::didExit,
+        which is called when associated TLS (with WTF::Thread) is destroyed. We leverage this mechanism to remove
+        WTF::Thread from MachineThreads.
+
+        This patch introduces WTF::ThreadGroup. It is tightly integrated with WTF::Thread: WTF::Thread knows
+        ThreadGroups which includes this thread. And WTF::ThreadGroup of course knows WTF::Threads added to it.
+        WTF::Thread::didExit carefully remove itself from WTF::ThreadGroups.
+
+        The most important part of this patch is locking. WTF::Thread can die. And WTF::ThreadGroup can die.
+        If we take a design using two fine grain locks in WTF::Thread and WTF::ThreadGroup, we easily encounter
+        dead lock. Consider the following case.
+
+        1. When adding WTF::Thread (TH) to WTF::ThreadGroup (THG), we first hold a lock of THG, and hold a lock of TH (locking order is THG -> TH).
+        2. When TH dies, TH need to hold a lock of TH to iterate THGs. And we hold a lock of THG to unregister TH from it (locking order is TH -> THG).
+        3. When suspending and resuming THs in THG, we first hold a lock of THG. And then, we hold a lock of TH to suspend and resume it (locking order is THG -> TH).
+        4. When destroying THG, we need to hold a lock of TH to unregister THG from TH. We can hold a lock of THG before that (locking order is THG -> TH).
+
+        Then, it easily causes dead lock. We cannot swap the locking order of (2) since iterating THG requires a lock of TH.
+        To solve this problem, we use std::shared_ptr and std::weak_ptr.
+
+        1. When adding WTF::Thread (TH) to WTF::ThreadGroup (THG), we first hold THG, and hold a lock of TH. (THG -> TH)
+        2. When TH dies, TH first hold lock of TH. And we use std::weak_ptr<>::lock() to retain non-destructed ThreadGroups.
+        If some of ThreadGroups are dying, we just ignore them. It is ok because such a ThreadGroup will be destructed. So we do not need to unregister this thread from
+        such a ThreadGroup. Then, we have Vector<std::shared_ptr<ThreadGroup>>. So we unlock a lock of TH. To unregister a thread from thread group, we first hold a
+        lock of THG and then hold a lock of TH. Both lifetime is ensured: THG is retained by std::shared_ptr. And TH is itself. (TH), (THG -> TH).
+        3. When suspending and resuming THs in THG, we first hold a lock of THG. And then, we hold a lock of TH to suspend and resume it (THG -> TH).
+        4. When destroying THG, we hold a lock of THG. And hold a lock of TH. During holding THG's lock, registered thread never dies because (2) holds THG lock. (THG -> TH).
+
+        We also fix suspend and resume locking mechanism to avoid dead lock. We should hold the global lock when suspending and resuming.
+        If we use per-thread lock, the suspended thread can hold the lock of the other threads. It causes dead lock.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/AutomaticThread.cpp:
+        * wtf/CMakeLists.txt:
+        * wtf/CrossThreadCopier.h:
+        * wtf/ParkingLot.h:
+        * wtf/ThreadGroup.cpp: Copied from Source/_javascript_Core/wasm/WasmMachineThreads.cpp.
+        (WTF::ThreadGroup::~ThreadGroup):
+        (WTF::ThreadGroup::add):
+        (WTF::ThreadGroup::addCurrentThread):
+        * wtf/ThreadGroup.h: Copied from Source/_javascript_Core/wasm/WasmMachineThreads.cpp.
+        (WTF::ThreadGroup::create):
+        (WTF::ThreadGroup::threads):
+        (WTF::ThreadGroup::getLock):
+        (WTF::ThreadGroup::weakFromThis):
+        * wtf/Threading.cpp:
+        (WTF::shouldRemoveThreadFromThreadGroup):
+        (WTF::Thread::didExit):
+        (WTF::Thread::addToThreadGroup):
+        (WTF::Thread::removeFromThreadGroup):
+        * wtf/Threading.h:
+        * wtf/ThreadingPthreads.cpp:
+        (WTF::Thread::resume):
+        (WTF::Thread::getRegisters):
+        * wtf/ThreadingWin.cpp:
+        (WTF::Thread::resume):
+        (WTF::Thread::getRegisters):
+
 2017-07-18  Andy Estes  <aes...@apple.com>
 
         [Xcode] Enable CLANG_WARN_RANGE_LOOP_ANALYSIS

Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (219652 => 219653)


--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2017-07-19 08:43:57 UTC (rev 219653)
@@ -131,6 +131,7 @@
 		DCEE22011CEA7551000C2396 /* BlockObjCExceptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = DCEE21FD1CEA7551000C2396 /* BlockObjCExceptions.mm */; };
 		DCEE22031CEA7551000C2396 /* PlatformUserPreferredLanguagesMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = DCEE21FF1CEA7551000C2396 /* PlatformUserPreferredLanguagesMac.mm */; };
 		E15556F518A0CC18006F48FB /* CryptographicUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E15556F318A0CC18006F48FB /* CryptographicUtilities.cpp */; };
+		E311FB171F0A568B003C08DE /* ThreadGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E311FB151F0A568B003C08DE /* ThreadGroup.cpp */; };
 		E3200AB81E9A536D003B59D2 /* ThreadHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */; };
 		E38C41251EB4E04C0042957D /* CPUTimeCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = E38C41241EB4E04C0042957D /* CPUTimeCocoa.mm */; };
 		E38C41281EB4E0680042957D /* CPUTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38C41261EB4E0680042957D /* CPUTime.cpp */; };
@@ -539,6 +540,8 @@
 		DE5A09FB1BA36992003D4424 /* CommonCryptoSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonCryptoSPI.h; sourceTree = "<group>"; };
 		E15556F318A0CC18006F48FB /* CryptographicUtilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptographicUtilities.cpp; sourceTree = "<group>"; };
 		E15556F418A0CC18006F48FB /* CryptographicUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptographicUtilities.h; sourceTree = "<group>"; };
+		E311FB151F0A568B003C08DE /* ThreadGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadGroup.cpp; sourceTree = "<group>"; };
+		E311FB161F0A568B003C08DE /* ThreadGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadGroup.h; sourceTree = "<group>"; };
 		E3200AB41E9A536D003B59D2 /* PlatformRegisters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformRegisters.h; sourceTree = "<group>"; };
 		E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadHolder.cpp; sourceTree = "<group>"; };
 		E3200AB61E9A536D003B59D2 /* ThreadHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadHolder.h; sourceTree = "<group>"; };
@@ -956,6 +959,8 @@
 				E3E158251EADA53C004A079D /* SystemFree.h */,
 				0FB317C31C488001007E395A /* SystemTracing.h */,
 				A8A4732F151A825B004123FF /* ThreadFunctionInvocation.h */,
+				E311FB151F0A568B003C08DE /* ThreadGroup.cpp */,
+				E311FB161F0A568B003C08DE /* ThreadGroup.h */,
 				E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */,
 				E3200AB61E9A536D003B59D2 /* ThreadHolder.h */,
 				A8A47330151A825B004123FF /* ThreadHolderPthreads.cpp */,
@@ -1388,6 +1393,7 @@
 				A8A47448151A825B004123FF /* ThreadHolderPthreads.cpp in Sources */,
 				A8A4744A151A825B004123FF /* Threading.cpp in Sources */,
 				A8A4744E151A825B004123FF /* ThreadingPthreads.cpp in Sources */,
+				E311FB171F0A568B003C08DE /* ThreadGroup.cpp in Sources */,
 				5311BD5C1EA822F900525281 /* ThreadMessage.cpp in Sources */,
 				0F66B2901DC97BAB004A1D3F /* TimeWithDynamicClockType.cpp in Sources */,
 				1C181C8F1D307AB800F5FA16 /* UTextProvider.cpp in Sources */,

Modified: trunk/Source/WTF/wtf/AutomaticThread.cpp (219652 => 219653)


--- trunk/Source/WTF/wtf/AutomaticThread.cpp	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/AutomaticThread.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -27,6 +27,7 @@
 #include "AutomaticThread.h"
 
 #include "DataLog.h"
+#include "Threading.h"
 
 namespace WTF {
 

Modified: trunk/Source/WTF/wtf/CMakeLists.txt (219652 => 219653)


--- trunk/Source/WTF/wtf/CMakeLists.txt	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/CMakeLists.txt	2017-07-19 08:43:57 UTC (rev 219653)
@@ -132,6 +132,7 @@
     StringPrintStream.h
     SystemFree.h
     SystemTracing.h
+    ThreadGroup.h
     ThreadHolder.cpp
     ThreadMessage.h
     ThreadSafeRefCounted.h
@@ -246,6 +247,7 @@
     StackStats.cpp
     StackTrace.cpp
     StringPrintStream.cpp
+    ThreadGroup.cpp
     ThreadMessage.cpp
     Threading.cpp
     TimeWithDynamicClockType.cpp

Modified: trunk/Source/WTF/wtf/CrossThreadCopier.h (219652 => 219653)


--- trunk/Source/WTF/wtf/CrossThreadCopier.h	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/CrossThreadCopier.h	2017-07-19 08:43:57 UTC (rev 219653)
@@ -35,6 +35,7 @@
 #include <wtf/Forward.h>
 #include <wtf/HashSet.h>
 #include <wtf/RefPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
 #include <wtf/text/WTFString.h>
 
 namespace WTF {

Modified: trunk/Source/WTF/wtf/ParkingLot.h (219652 => 219653)


--- trunk/Source/WTF/wtf/ParkingLot.h	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/ParkingLot.h	2017-07-19 08:43:57 UTC (rev 219653)
@@ -28,11 +28,12 @@
 
 #include <wtf/Atomics.h>
 #include <wtf/ScopedLambda.h>
-#include <wtf/Threading.h>
 #include <wtf/TimeWithDynamicClockType.h>
 
 namespace WTF {
 
+class Thread;
+
 class ParkingLot {
     ParkingLot() = delete;
     ParkingLot(const ParkingLot&) = delete;

Copied: trunk/Source/WTF/wtf/ThreadGroup.cpp (from rev 219652, trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp) (0 => 219653)


--- trunk/Source/WTF/wtf/ThreadGroup.cpp	                        (rev 0)
+++ trunk/Source/WTF/wtf/ThreadGroup.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 Yusuke Suzuki <utatane....@gmail.com>.
+ *
+ * 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 "ThreadGroup.h"
+
+#include <wtf/NeverDestroyed.h>
+
+namespace WTF {
+
+ThreadGroup::~ThreadGroup()
+{
+    std::lock_guard<std::mutex> locker(m_lock);
+    for (auto& thread : m_threads)
+        thread->removeFromThreadGroup(locker, *this);
+}
+
+bool ThreadGroup::add(Thread& thread)
+{
+    std::lock_guard<std::mutex> locker(m_lock);
+    return thread.addToThreadGroup(locker, *this);
+}
+
+void ThreadGroup::addCurrentThread()
+{
+    bool isAdded = add(Thread::current());
+    ASSERT_UNUSED(isAdded, isAdded);
+}
+
+} // namespace WTF

Copied: trunk/Source/WTF/wtf/ThreadGroup.h (from rev 219652, trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp) (0 => 219653)


--- trunk/Source/WTF/wtf/ThreadGroup.h	                        (rev 0)
+++ trunk/Source/WTF/wtf/ThreadGroup.h	2017-07-19 08:43:57 UTC (rev 219653)
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 Yusuke Suzuki <utatane....@gmail.com>.
+ *
+ * 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 <memory>
+#include <wtf/ListHashSet.h>
+#include <wtf/Lock.h>
+#include <wtf/Threading.h>
+
+namespace WTF {
+
+class ThreadGroup : public std::enable_shared_from_this<ThreadGroup> {
+public:
+    friend class Thread;
+
+    static std::shared_ptr<ThreadGroup> create()
+    {
+        return std::make_shared<ThreadGroup>();
+    }
+
+    WTF_EXPORT_PRIVATE bool add(Thread&);
+    WTF_EXPORT_PRIVATE void addCurrentThread();
+
+    const ListHashSet<Ref<Thread>>& threads(const AbstractLocker&) const { return m_threads; }
+
+    std::mutex& getLock() { return m_lock; }
+
+    WTF_EXPORT_PRIVATE ~ThreadGroup();
+
+    ThreadGroup() = default;
+
+private:
+    std::weak_ptr<ThreadGroup> weakFromThis()
+    {
+        return shared_from_this();
+    }
+
+    // We use std::mutex since it can be used when deallocating TLS.
+    std::mutex m_lock;
+    ListHashSet<Ref<Thread>> m_threads;
+};
+
+}
+
+using WTF::ThreadGroup;

Modified: trunk/Source/WTF/wtf/Threading.cpp (219652 => 219653)


--- trunk/Source/WTF/wtf/Threading.cpp	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/Threading.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -26,8 +26,6 @@
 #include "config.h"
 #include "Threading.h"
 
-#include "dtoa.h"
-#include "dtoa/cached-powers.h"
 #include <algorithm>
 #include <cmath>
 #include <cstring>
@@ -34,6 +32,7 @@
 #include <wtf/DateMath.h>
 #include <wtf/PrintStream.h>
 #include <wtf/RandomNumberSeed.h>
+#include <wtf/ThreadGroup.h>
 #include <wtf/ThreadHolder.h>
 #include <wtf/ThreadMessage.h>
 #include <wtf/ThreadingPrimitives.h>
@@ -130,12 +129,70 @@
     m_stack = StackBounds::currentThreadStackBounds();
 }
 
+static bool shouldRemoveThreadFromThreadGroup()
+{
+#if OS(WINDOWS)
+    // On Windows the thread specific destructor is also called when the
+    // main thread is exiting. This may lead to the main thread waiting
+    // forever for the thread group lock when exiting, if the sampling
+    // profiler thread was terminated by the system while holding the
+    // thread group lock.
+    if (WTF::isMainThread())
+        return false;
+#endif
+    return true;
+}
+
 void Thread::didExit()
 {
+    if (shouldRemoveThreadFromThreadGroup()) {
+        Vector<std::shared_ptr<ThreadGroup>> threadGroups;
+        {
+            std::lock_guard<std::mutex> locker(m_mutex);
+            for (auto& threadGroup : m_threadGroups) {
+                // If ThreadGroup is just being destroyed,
+                // we do not need to perform unregistering.
+                if (auto retained = threadGroup.lock())
+                    threadGroups.append(WTFMove(retained));
+            }
+            m_isShuttingDown = true;
+        }
+        for (auto& threadGroup : threadGroups) {
+            std::lock_guard<std::mutex> threadGroupLocker(threadGroup->getLock());
+            std::lock_guard<std::mutex> locker(m_mutex);
+            threadGroup->m_threads.remove(*this);
+        }
+    }
+    // We would like to say "thread is exited" after unregistering threads from thread groups.
+    // So we need to separate m_isShuttingDown from m_didExit.
     std::lock_guard<std::mutex> locker(m_mutex);
     m_didExit = true;
 }
 
+bool Thread::addToThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup& threadGroup)
+{
+    UNUSED_PARAM(threadGroupLocker);
+    std::lock_guard<std::mutex> locker(m_mutex);
+    if (m_isShuttingDown)
+        return false;
+    if (threadGroup.m_threads.add(*this).isNewEntry)
+        m_threadGroups.append(threadGroup.weakFromThis());
+    return true;
+}
+
+void Thread::removeFromThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup& threadGroup)
+{
+    UNUSED_PARAM(threadGroupLocker);
+    std::lock_guard<std::mutex> locker(m_mutex);
+    if (m_isShuttingDown)
+        return;
+    m_threadGroups.removeFirstMatching([&] (auto weakPtr) {
+        if (auto sharedPtr = weakPtr.lock())
+            return sharedPtr.get() == &threadGroup;
+        return false;
+    });
+}
+
 void Thread::setCurrentThreadIsUserInteractive(int relativePriority)
 {
 #if HAVE(QOS_CLASSES)

Modified: trunk/Source/WTF/wtf/Threading.h (219652 => 219653)


--- trunk/Source/WTF/wtf/Threading.h	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/Threading.h	2017-07-19 08:43:57 UTC (rev 219653)
@@ -40,6 +40,7 @@
 #include <wtf/RefPtr.h>
 #include <wtf/StackBounds.h>
 #include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/Vector.h>
 
 #if USE(PTHREADS) && !OS(DARWIN)
 #include <semaphore.h>
@@ -48,16 +49,19 @@
 
 namespace WTF {
 
+class AbstractLocker;
 class ThreadMessageData;
 
 using ThreadIdentifier = uint32_t;
 typedef void (*ThreadFunction)(void* argument);
 
+class ThreadGroup;
 class ThreadHolder;
 class PrintStream;
 
 class Thread : public ThreadSafeRefCounted<Thread> {
 public:
+    friend class ThreadGroup;
     friend class ThreadHolder;
 
     WTF_EXPORT_PRIVATE ~Thread();
@@ -175,11 +179,17 @@
     void didJoin() { m_joinableState = Joined; }
     bool hasExited() { return m_didExit; }
 
+    // These functions are only called from ThreadGroup.
+    bool addToThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup&);
+    void removeFromThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup&);
+
     // WordLock & Lock rely on ThreadSpecific. But Thread object can be destroyed even after ThreadSpecific things are destroyed.
     std::mutex m_mutex;
     ThreadIdentifier m_id { 0 };
     JoinableState m_joinableState { Joinable };
     StackBounds m_stack { StackBounds::emptyBounds() };
+    Vector<std::weak_ptr<ThreadGroup>> m_threadGroups;
+    bool m_isShuttingDown { false };
     bool m_didExit { false };
 #if USE(PTHREADS)
     pthread_t m_handle;

Modified: trunk/Source/WTF/wtf/ThreadingPthreads.cpp (219652 => 219653)


--- trunk/Source/WTF/wtf/ThreadingPthreads.cpp	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/ThreadingPthreads.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -40,6 +40,7 @@
 #include <wtf/RawPointer.h>
 #include <wtf/StdLibExtras.h>
 #include <wtf/ThreadFunctionInvocation.h>
+#include <wtf/ThreadGroup.h>
 #include <wtf/ThreadHolder.h>
 #include <wtf/ThreadingPrimitives.h>
 #include <wtf/WordLock.h>
@@ -73,6 +74,8 @@
 
 namespace WTF {
 
+static StaticLock globalSuspendLock;
+
 Thread::Thread()
 {
 #if !OS(DARWIN)
@@ -92,7 +95,6 @@
 // We use SIGUSR1 to suspend and resume machine threads in _javascript_Core.
 static constexpr const int SigThreadSuspendResume = SIGUSR1;
 static std::atomic<Thread*> targetThread { nullptr };
-static StaticWordLock globalSuspendLock;
 
 #if COMPILER(GCC)
 #pragma GCC diagnostic push
@@ -329,7 +331,18 @@
 auto Thread::suspend() -> Expected<void, PlatformSuspendError>
 {
     RELEASE_ASSERT_WITH_MESSAGE(id() != currentThread(), "We do not support suspending the current thread itself.");
-    std::lock_guard<std::mutex> locker(m_mutex);
+    // During suspend, suspend or resume should not be executed from the other threads.
+    // We use global lock instead of per thread lock.
+    // Consider the following case, there are threads A and B.
+    // And A attempt to suspend B and B attempt to suspend A.
+    // A and B send signals. And later, signals are delivered to A and B.
+    // In that case, both will be suspended.
+    //
+    // And it is important to use a global lock to suspend and resume. Let's consider using per-thread lock.
+    // Your issuing thread (A) attempts to suspend the target thread (B). Then, you will suspend the thread (C) additionally.
+    // This case frequently happens if you stop threads to perform stack scanning. But thread (B) may hold the lock of thread (C).
+    // In that case, dead lock happens. Using global lock here avoids this dead lock.
+    LockHolder locker(globalSuspendLock);
 #if OS(DARWIN)
     kern_return_t result = thread_suspend(m_platformThread);
     if (result != KERN_SUCCESS)
@@ -336,58 +349,46 @@
         return makeUnexpected(result);
     return { };
 #else
-    {
-        // During suspend, suspend or resume should not be executed from the other threads.
-        // We use global lock instead of per thread lock.
-        // Consider the following case, there are threads A and B.
-        // And A attempt to suspend B and B attempt to suspend A.
-        // A and B send signals. And later, signals are delivered to A and B.
-        // In that case, both will be suspended.
-        WordLockHolder locker(globalSuspendLock);
-        if (!m_suspendCount) {
-            // Ideally, we would like to use pthread_sigqueue. It allows us to pass the argument to the signal handler.
-            // But it can be used in a few platforms, like Linux.
-            // Instead, we use Thread* stored in the thread local storage to pass it to the signal handler.
-            targetThread.store(this);
-            int result = pthread_kill(m_handle, SigThreadSuspendResume);
-            if (result)
-                return makeUnexpected(result);
-            sem_wait(&m_semaphoreForSuspendResume);
-            // Release barrier ensures that this operation is always executed after all the above processing is done.
-            m_suspended.store(true, std::memory_order_release);
-        }
-        ++m_suspendCount;
-        return { };
+    if (!m_suspendCount) {
+        // Ideally, we would like to use pthread_sigqueue. It allows us to pass the argument to the signal handler.
+        // But it can be used in a few platforms, like Linux.
+        // Instead, we use Thread* stored in the thread local storage to pass it to the signal handler.
+        targetThread.store(this);
+        int result = pthread_kill(m_handle, SigThreadSuspendResume);
+        if (result)
+            return makeUnexpected(result);
+        sem_wait(&m_semaphoreForSuspendResume);
+        // Release barrier ensures that this operation is always executed after all the above processing is done.
+        m_suspended.store(true, std::memory_order_release);
     }
+    ++m_suspendCount;
+    return { };
 #endif
 }
 
 void Thread::resume()
 {
-    std::lock_guard<std::mutex> locker(m_mutex);
+    // During resume, suspend or resume should not be executed from the other threads.
+    LockHolder locker(globalSuspendLock);
 #if OS(DARWIN)
     thread_resume(m_platformThread);
 #else
-    {
-        // During resume, suspend or resume should not be executed from the other threads.
-        WordLockHolder locker(globalSuspendLock);
-        if (m_suspendCount == 1) {
-            // When allowing SigThreadSuspendResume interrupt in the signal handler by sigsuspend and SigThreadSuspendResume is actually issued,
-            // the signal handler itself will be called once again.
-            // There are several ways to distinguish the handler invocation for suspend and resume.
-            // 1. Use different signal numbers. And check the signal number in the handler.
-            // 2. Use some arguments to distinguish suspend and resume in the handler. If pthread_sigqueue can be used, we can take this.
-            // 3. Use thread local storage with atomic variables in the signal handler.
-            // In this implementaiton, we take (3). suspended flag is used to distinguish it.
-            targetThread.store(this);
-            if (pthread_kill(m_handle, SigThreadSuspendResume) == ESRCH)
-                return;
-            sem_wait(&m_semaphoreForSuspendResume);
-            // Release barrier ensures that this operation is always executed after all the above processing is done.
-            m_suspended.store(false, std::memory_order_release);
-        }
-        --m_suspendCount;
+    if (m_suspendCount == 1) {
+        // When allowing SigThreadSuspendResume interrupt in the signal handler by sigsuspend and SigThreadSuspendResume is actually issued,
+        // the signal handler itself will be called once again.
+        // There are several ways to distinguish the handler invocation for suspend and resume.
+        // 1. Use different signal numbers. And check the signal number in the handler.
+        // 2. Use some arguments to distinguish suspend and resume in the handler. If pthread_sigqueue can be used, we can take this.
+        // 3. Use thread local storage with atomic variables in the signal handler.
+        // In this implementaiton, we take (3). suspended flag is used to distinguish it.
+        targetThread.store(this);
+        if (pthread_kill(m_handle, SigThreadSuspendResume) == ESRCH)
+            return;
+        sem_wait(&m_semaphoreForSuspendResume);
+        // Release barrier ensures that this operation is always executed after all the above processing is done.
+        m_suspended.store(false, std::memory_order_release);
     }
+    --m_suspendCount;
 #endif
 }
 
@@ -426,7 +427,7 @@
 
 size_t Thread::getRegisters(PlatformRegisters& registers)
 {
-    std::lock_guard<std::mutex> locker(m_mutex);
+    LockHolder locker(globalSuspendLock);
 #if OS(DARWIN)
     auto metadata = threadStateMetadata();
     kern_return_t result = thread_get_state(m_platformThread, metadata.flavor, (thread_state_t)&registers, &metadata.userCount);

Modified: trunk/Source/WTF/wtf/ThreadingWin.cpp (219652 => 219653)


--- trunk/Source/WTF/wtf/ThreadingWin.cpp	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/ThreadingWin.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -91,6 +91,7 @@
 #include <process.h>
 #include <windows.h>
 #include <wtf/CurrentTime.h>
+#include <wtf/Lock.h>
 #include <wtf/MainThread.h>
 #include <wtf/MathExtras.h>
 #include <wtf/NeverDestroyed.h>
@@ -104,6 +105,8 @@
 
 namespace WTF {
 
+static StaticLock globalSuspendLock;
+
 Thread::Thread()
 {
 }
@@ -243,7 +246,7 @@
 auto Thread::suspend() -> Expected<void, PlatformSuspendError>
 {
     RELEASE_ASSERT_WITH_MESSAGE(id() != currentThread(), "We do not support suspending the current thread itself.");
-    std::lock_guard<std::mutex> locker(m_mutex);
+    LockHolder locker(globalSuspendLock);
     DWORD result = SuspendThread(m_handle);
     if (result != (DWORD)-1)
         return { };
@@ -253,13 +256,13 @@
 // During resume, suspend or resume should not be executed from the other threads.
 void Thread::resume()
 {
-    std::lock_guard<std::mutex> locker(m_mutex);
+    LockHolder locker(globalSuspendLock);
     ResumeThread(m_handle);
 }
 
 size_t Thread::getRegisters(PlatformRegisters& registers)
 {
-    std::lock_guard<std::mutex> locker(m_mutex);
+    LockHolder locker(globalSuspendLock);
     registers.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
     GetThreadContext(m_handle, &registers);
     return sizeof(CONTEXT);

Modified: trunk/Source/WebCore/ChangeLog (219652 => 219653)


--- trunk/Source/WebCore/ChangeLog	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WebCore/ChangeLog	2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,12 @@
+2017-07-19  Yusuke Suzuki  <utatane....@gmail.com>
+
+        [WTF] Implement WTF::ThreadGroup
+        https://bugs.webkit.org/show_bug.cgi?id=174081
+
+        Reviewed by Mark Lam.
+
+        * page/ResourceUsageThread.h:
+
 2017-07-18  Andy Estes  <aes...@apple.com>
 
         [Xcode] Enable CLANG_WARN_RANGE_LOOP_ANALYSIS

Modified: trunk/Source/WebCore/page/ResourceUsageThread.h (219652 => 219653)


--- trunk/Source/WebCore/page/ResourceUsageThread.h	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WebCore/page/ResourceUsageThread.h	2017-07-19 08:43:57 UTC (rev 219653)
@@ -35,6 +35,7 @@
 #include <wtf/Lock.h>
 #include <wtf/NeverDestroyed.h>
 #include <wtf/Noncopyable.h>
+#include <wtf/Threading.h>
 
 namespace JSC {
 class VM;

Modified: trunk/Source/WebKit/ChangeLog (219652 => 219653)


--- trunk/Source/WebKit/ChangeLog	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WebKit/ChangeLog	2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,12 @@
+2017-07-19  Yusuke Suzuki  <utatane....@gmail.com>
+
+        [WTF] Implement WTF::ThreadGroup
+        https://bugs.webkit.org/show_bug.cgi?id=174081
+
+        Reviewed by Mark Lam.
+
+        * Shared/AsyncRequest.h:
+
 2017-07-18  Carlos Garcia Campos  <cgar...@igalia.com>
 
         WebDriver: handle invalid selector errors

Modified: trunk/Source/WebKit/Shared/AsyncRequest.h (219652 => 219653)


--- trunk/Source/WebKit/Shared/AsyncRequest.h	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WebKit/Shared/AsyncRequest.h	2017-07-19 08:43:57 UTC (rev 219653)
@@ -27,6 +27,7 @@
 #ifndef AsyncRequest_h
 #define AsyncRequest_h
 
+#include <wtf/Function.h>
 #include <wtf/HashMap.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>

Modified: trunk/Tools/ChangeLog (219652 => 219653)


--- trunk/Tools/ChangeLog	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Tools/ChangeLog	2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,18 @@
+2017-07-19  Yusuke Suzuki  <utatane....@gmail.com>
+
+        [WTF] Implement WTF::ThreadGroup
+        https://bugs.webkit.org/show_bug.cgi?id=174081
+
+        Reviewed by Mark Lam.
+
+        Add WTF::ThreadGroup tests.
+
+        * TestWebKitAPI/CMakeLists.txt:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WTF/ThreadGroup.cpp: Added.
+        (TestWebKitAPI::testThreadGroup):
+        (TestWebKitAPI::TEST):
+
 2017-07-18  Andy Estes  <aes...@apple.com>
 
         [Xcode] Enable CLANG_WARN_RANGE_LOOP_ANALYSIS

Modified: trunk/Tools/TestWebKitAPI/CMakeLists.txt (219652 => 219653)


--- trunk/Tools/TestWebKitAPI/CMakeLists.txt	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Tools/TestWebKitAPI/CMakeLists.txt	2017-07-19 08:43:57 UTC (rev 219653)
@@ -81,6 +81,7 @@
     ${TESTWEBKITAPI_DIR}/Tests/WTF/StringOperators.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/StringView.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/TextBreakIterator.cpp
+    ${TESTWEBKITAPI_DIR}/Tests/WTF/ThreadGroup.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/Time.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/UniqueRef.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/Variant.cpp

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (219652 => 219653)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-07-19 08:43:57 UTC (rev 219653)
@@ -632,6 +632,7 @@
 		D34E08761E4E42E1005FF14A /* WKWebViewGetContents.mm in Sources */ = {isa = PBXBuildFile; fileRef = D3BE5E341E4CE85E00FD563A /* WKWebViewGetContents.mm */; };
 		E1220DCA155B28AA0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E1220DC9155B287D0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html */; };
 		E194E1BD177E53C7009C4D4E /* StopLoadingFromDidReceiveResponse.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */; };
+		E3DEA8111F0A589000CBC2E8 /* ThreadGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */; };
 		ECA680CE1E68CC0900731D20 /* StringUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = ECA680CD1E68CC0900731D20 /* StringUtilities.mm */; };
 		F407FE391F1D0DFC0017CF25 /* enormous.svg in Copy Resources */ = {isa = PBXBuildFile; fileRef = F407FE381F1D0DE60017CF25 /* enormous.svg */; };
 		F415086D1DA040C50044BE9B /* play-audio-on-click.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F415086C1DA040C10044BE9B /* play-audio-on-click.html */; };
@@ -1616,6 +1617,7 @@
 		E194E1BA177E5145009C4D4E /* StopLoadingFromDidReceiveResponse.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StopLoadingFromDidReceiveResponse.mm; sourceTree = "<group>"; };
 		E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = StopLoadingFromDidReceiveResponse.html; sourceTree = "<group>"; };
 		E19DB9781B32137C00DB38D4 /* NavigatorLanguage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NavigatorLanguage.mm; sourceTree = "<group>"; };
+		E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadGroup.cpp; sourceTree = "<group>"; };
 		E40019301ACE9B5C001B0A2A /* BloomFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BloomFilter.cpp; sourceTree = "<group>"; };
 		E490296714E2E3A4002BEDD1 /* TypingStyleCrash.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TypingStyleCrash.mm; sourceTree = "<group>"; };
 		E4A757D3178AEA5B00B5D7A4 /* Deque.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Deque.cpp; sourceTree = "<group>"; };
@@ -2404,6 +2406,7 @@
 				7C74D42D188228F300E5ED57 /* StringView.cpp */,
 				5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */,
 				9329AA281DE3F81E003ABD07 /* TextBreakIterator.cpp */,
+				E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */,
 				5311BD5D1EA9490D00525281 /* ThreadMessages.cpp */,
 				0F2C20B71DCD544800542D9E /* Time.cpp */,
 				5C5E633D1D0B67940085A025 /* UniqueRef.cpp */,
@@ -2904,6 +2907,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				E3DEA8111F0A589000CBC2E8 /* ThreadGroup.cpp in Sources */,
 				7C83DE991D0A590C00FEBCF3 /* AtomicString.cpp in Sources */,
 				1ADAD1501D77A9F600212586 /* BlockPtr.mm in Sources */,
 				7C83DE9C1D0A590C00FEBCF3 /* BloomFilter.cpp in Sources */,

Added: trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp (0 => 219653)


--- trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp	2017-07-19 08:43:57 UTC (rev 219653)
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 Yusuke Suzuki <utatane....@gmail.com>.
+ *
+ * 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 <wtf/Condition.h>
+#include <wtf/ThreadGroup.h>
+#include <wtf/Vector.h>
+
+namespace TestWebKitAPI {
+
+enum class Mode { Add, AddCurrentThread };
+static void testThreadGroup(Mode mode)
+{
+    std::shared_ptr<ThreadGroup> threadGroup = ThreadGroup::create();
+    unsigned numberOfThreads = 16;
+    unsigned waitingThreads = 0;
+    bool restarting = false;
+    Lock lock;
+    Condition condition;
+    Condition restartCondition;
+    Vector<RefPtr<Thread>> threads;
+
+    {
+        auto locker = holdLock(lock);
+        for (unsigned i = 0; i < numberOfThreads; ++i) {
+            RefPtr<Thread> thread = Thread::create("ThreadGroupWorker", [&] {
+                auto locker = holdLock(lock);
+                if (mode == Mode::AddCurrentThread)
+                    threadGroup->addCurrentThread();
+                ++waitingThreads;
+                condition.notifyOne();
+                restartCondition.wait(lock, [&] {
+                    return restarting;
+                });
+            });
+            if (mode == Mode::Add)
+                EXPECT_EQ(threadGroup->add(*thread), true);
+            threads.append(thread);
+        }
+
+        condition.wait(lock, [&] {
+            return waitingThreads == numberOfThreads;
+        });
+    }
+
+    {
+        auto threadGroupLocker = holdLock(threadGroup->getLock());
+        EXPECT_EQ(threads.size(), numberOfThreads);
+        EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), numberOfThreads);
+        {
+            auto locker = holdLock(lock);
+            restarting = true;
+            restartCondition.notifyAll();
+        }
+
+        // While holding ThreadGroup lock, threads do not exit.
+        WTF::sleep(0.1);
+        EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), numberOfThreads);
+    }
+    {
+        for (auto& thread : threads)
+            thread->waitForCompletion();
+
+        auto threadGroupLocker = holdLock(threadGroup->getLock());
+        EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 0u);
+    }
+}
+
+TEST(WTF, ThreadGroupAdd)
+{
+    testThreadGroup(Mode::Add);
+}
+
+TEST(WTF, ThreadGroupAddCurrentThread)
+{
+    testThreadGroup(Mode::AddCurrentThread);
+}
+
+TEST(WTF, ThreadGroupDoNotAddDeadThread)
+{
+    std::shared_ptr<ThreadGroup> threadGroup = ThreadGroup::create();
+    RefPtr<Thread> thread = Thread::create("ThreadGroupWorker", [&] { });
+    thread->waitForCompletion();
+    EXPECT_EQ(threadGroup->add(*thread), false);
+
+    auto threadGroupLocker = holdLock(threadGroup->getLock());
+    EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 0u);
+}
+
+TEST(WTF, ThreadGroupAddDuplicateThreads)
+{
+    bool restarting = false;
+    Lock lock;
+    Condition restartCondition;
+    std::shared_ptr<ThreadGroup> threadGroup = ThreadGroup::create();
+    RefPtr<Thread> thread = Thread::create("ThreadGroupWorker", [&] {
+        auto locker = holdLock(lock);
+        restartCondition.wait(lock, [&] {
+            return restarting;
+        });
+    });
+    EXPECT_EQ(threadGroup->add(*thread), true);
+    EXPECT_EQ(threadGroup->add(*thread), true);
+
+    {
+        auto threadGroupLocker = holdLock(threadGroup->getLock());
+        EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 1u);
+    }
+
+    {
+        auto locker = holdLock(lock);
+        restarting = true;
+        restartCondition.notifyAll();
+    }
+    thread->waitForCompletion();
+    {
+        auto threadGroupLocker = holdLock(threadGroup->getLock());
+        EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 0u);
+    }
+}
+
+} // namespace TestWebKitAPI
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to