Title: [219238] trunk
Revision
219238
Author
utatane....@gmail.com
Date
2017-07-06 21:56:23 -0700 (Thu, 06 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/WebKit2:

* Shared/AsyncRequest.h:
* UIProcess/Storage/ResourceLoadStatisticsStore.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 introduce one global lock ThreadGroup::destructionMutex (GL).

1. When adding WTF::Thread (TH) to WTF::ThreadGroup (THG), we first hold GL, and hold a lock of THG. Do not hold a
lock of TH. TH's thread groups information is guarded by GL instead of a lock of TH (locking order is GL -> THG).
2. When TH dies, TH need to hold GL to iterate THGs. And we hold a lock of THG to unregister TH from it (locking order is GL -> 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 GL to unregister THG from TH. We can hold a lock of THG after that (locking order is GL -> THG).

We still have a lock of THG. By separating GL and THG's lock, we can hold a lock of THG while completely unrelated TH is destructed which takes a lock of GL and unrelated THG.

* 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::destructionMutex):
(WTF::ThreadGroup::~ThreadGroup):
(WTF::ThreadGroup::add):
(WTF::ThreadGroup::addCurrentThread):
(WTF::ThreadGroup::removeCurrentThread):
* wtf/ThreadGroup.h: Copied from Source/_javascript_Core/wasm/WasmMachineThreads.cpp.
(WTF::ThreadGroup::create):
(WTF::ThreadGroup::threads):
(WTF::ThreadGroup::getLock):
* wtf/Threading.cpp:
(WTF::Thread::didExit):
(WTF::Thread::addToThreadGroup):
(WTF::Thread::removeFromThreadGroup):
* wtf/Threading.h:
(WTF::Thread::canAddToThreadGroup):

Tools:

Add WTF::ThreadGroup tests.

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

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp (219237 => 219238)


--- trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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 (219237 => 219238)


--- trunk/Source/_javascript_Core/ChangeLog	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-07-07 04:56:23 UTC (rev 219238)
@@ -1,3 +1,57 @@
+2017-07-05  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-06  Saam Barati  <sbar...@apple.com>
 
         We are missing places where we invalidate the for-in context

Modified: trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp (219237 => 219238)


--- trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp	2017-07-07 04:56:23 UTC (rev 219238)
@@ -23,17 +23,10 @@
 #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/NeverDestroyed.h>
+#include <wtf/BitVector.h>
 #include <wtf/StdLibExtras.h>
 
 using namespace WTF;
@@ -40,190 +33,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)
 {
@@ -236,52 +50,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;
@@ -297,9 +65,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);
 
@@ -306,8 +74,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);
@@ -340,20 +108,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;
 
@@ -366,7 +134,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>.
@@ -375,60 +143,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 != 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, 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, 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;
 }
 
@@ -449,8 +206,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 (219237 => 219238)


--- trunk/Source/_javascript_Core/heap/MachineStackMarker.h	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/_javascript_Core/heap/MachineStackMarker.h	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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<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;
+    Ref<ThreadGroup> m_threadGroup;
 };
 
 #define DECLARE_AND_COMPUTE_CURRENT_THREAD_STATE(stateName) \

Modified: trunk/Source/_javascript_Core/runtime/SamplingProfiler.cpp (219237 => 219238)


--- trunk/Source/_javascript_Core/runtime/SamplingProfiler.cpp	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/_javascript_Core/runtime/SamplingProfiler.cpp	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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 (219237 => 219238)


--- trunk/Source/_javascript_Core/runtime/SamplingProfiler.h	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/_javascript_Core/runtime/SamplingProfiler.h	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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 (219237 => 219238)


--- trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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, [] (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 (219237 => 219238)


--- trunk/Source/WTF/ChangeLog	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WTF/ChangeLog	2017-07-07 04:56:23 UTC (rev 219238)
@@ -1,3 +1,69 @@
+2017-07-05  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 introduce one global lock ThreadGroup::destructionMutex (GL).
+
+        1. When adding WTF::Thread (TH) to WTF::ThreadGroup (THG), we first hold GL, and hold a lock of THG. Do not hold a
+        lock of TH. TH's thread groups information is guarded by GL instead of a lock of TH (locking order is GL -> THG).
+        2. When TH dies, TH need to hold GL to iterate THGs. And we hold a lock of THG to unregister TH from it (locking order is GL -> 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 GL to unregister THG from TH. We can hold a lock of THG after that (locking order is GL -> THG).
+
+        We still have a lock of THG. By separating GL and THG's lock, we can hold a lock of THG while completely unrelated TH is destructed which takes a lock of GL and unrelated THG.
+
+        * 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::destructionMutex):
+        (WTF::ThreadGroup::~ThreadGroup):
+        (WTF::ThreadGroup::add):
+        (WTF::ThreadGroup::addCurrentThread):
+        (WTF::ThreadGroup::removeCurrentThread):
+        * wtf/ThreadGroup.h: Copied from Source/_javascript_Core/wasm/WasmMachineThreads.cpp.
+        (WTF::ThreadGroup::create):
+        (WTF::ThreadGroup::threads):
+        (WTF::ThreadGroup::getLock):
+        * wtf/Threading.cpp:
+        (WTF::Thread::didExit):
+        (WTF::Thread::addToThreadGroup):
+        (WTF::Thread::removeFromThreadGroup):
+        * wtf/Threading.h:
+        (WTF::Thread::canAddToThreadGroup):
+
 2017-07-06  Yusuke Suzuki  <utatane....@gmail.com>
 
         [WTF] Clean up StringStatics.cpp by using LazyNeverDestroyed<> for Atoms

Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (219237 => 219238)


--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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 (219237 => 219238)


--- trunk/Source/WTF/wtf/AutomaticThread.cpp	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WTF/wtf/AutomaticThread.cpp	2017-07-07 04:56:23 UTC (rev 219238)
@@ -27,6 +27,7 @@
 #include "AutomaticThread.h"
 
 #include "DataLog.h"
+#include "Threading.h"
 
 namespace WTF {
 

Modified: trunk/Source/WTF/wtf/CMakeLists.txt (219237 => 219238)


--- trunk/Source/WTF/wtf/CMakeLists.txt	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WTF/wtf/CMakeLists.txt	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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 (219237 => 219238)


--- trunk/Source/WTF/wtf/CrossThreadCopier.h	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WTF/wtf/CrossThreadCopier.h	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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 (219237 => 219238)


--- trunk/Source/WTF/wtf/ParkingLot.h	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WTF/wtf/ParkingLot.h	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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 219237, trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp) (0 => 219238)


--- trunk/Source/WTF/wtf/ThreadGroup.cpp	                        (rev 0)
+++ trunk/Source/WTF/wtf/ThreadGroup.cpp	2017-07-07 04:56:23 UTC (rev 219238)
@@ -0,0 +1,70 @@
+/*
+ * 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 {
+
+std::mutex& ThreadGroup::destructionMutex()
+{
+    static NeverDestroyed<std::mutex> mutex;
+    return mutex.get();
+}
+
+ThreadGroup::~ThreadGroup()
+{
+    auto destructionMutexLocker = holdLock(destructionMutex());
+    auto locker = holdLock(m_lock);
+    for (auto* thread : m_threads)
+        thread->removeFromThreadGroup(destructionMutexLocker, *this);
+}
+
+bool ThreadGroup::add(Thread& thread)
+{
+    auto destructionMutexLocker = holdLock(destructionMutex());
+    auto locker = holdLock(m_lock);
+    if (!thread.canAddToThreadGroup(destructionMutexLocker))
+        return false;
+    if (m_threads.add(&thread).isNewEntry)
+        thread.addToThreadGroup(destructionMutexLocker, *this);
+    return true;
+}
+
+void ThreadGroup::addCurrentThread()
+{
+    bool isAdded = add(Thread::current());
+    ASSERT_UNUSED(isAdded, isAdded);
+}
+
+void ThreadGroup::removeCurrentThread(const AbstractLocker&, Thread& thread)
+{
+    auto locker = holdLock(m_lock);
+    m_threads.remove(&thread);
+}
+
+} // namespace WTF

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


--- trunk/Source/WTF/wtf/ThreadGroup.h	                        (rev 0)
+++ trunk/Source/WTF/wtf/ThreadGroup.h	2017-07-07 04:56:23 UTC (rev 219238)
@@ -0,0 +1,65 @@
+/*
+ * 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 <wtf/ListHashSet.h>
+#include <wtf/Lock.h>
+#include <wtf/Threading.h>
+
+namespace WTF {
+
+class ThreadGroup : public ThreadSafeRefCounted<ThreadGroup> {
+public:
+    friend class Thread;
+
+    static Ref<ThreadGroup> create()
+    {
+        return adoptRef(*new ThreadGroup());
+    }
+
+    WTF_EXPORT_PRIVATE bool add(Thread&);
+    WTF_EXPORT_PRIVATE void addCurrentThread();
+
+    const ListHashSet<Thread*>& threads(const AbstractLocker&) const { return m_threads; }
+
+    std::mutex& getLock() { return m_lock; }
+
+    WTF_EXPORT_PRIVATE ~ThreadGroup();
+
+private:
+    ThreadGroup() = default;
+
+    static std::mutex& destructionMutex();
+    void removeCurrentThread(const AbstractLocker& destructionMutexLocker, Thread&);
+
+    // We use std::mutex since it can be used when deallocating TLS.
+    std::mutex m_lock;
+    ListHashSet<Thread*> m_threads;
+};
+
+}
+
+using WTF::ThreadGroup;

Modified: trunk/Source/WTF/wtf/Threading.cpp (219237 => 219238)


--- trunk/Source/WTF/wtf/Threading.cpp	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WTF/wtf/Threading.cpp	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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,48 @@
     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;
+}
+
+bool Thread::canAddToThreadGroup(const AbstractLocker&)
+{
+    std::lock_guard<std::mutex> locker(m_mutex);
+    return !m_didExit;
+}
+
 void Thread::didExit()
 {
+    auto destructionMutexLocker = holdLock(ThreadGroup::destructionMutex());
+    if (shouldRemoveThreadFromThreadGroup()) {
+        for (auto* threadGroup : m_threadGroups)
+            threadGroup->removeCurrentThread(destructionMutexLocker, *this);
+    }
     std::lock_guard<std::mutex> locker(m_mutex);
     m_didExit = true;
 }
 
+void Thread::addToThreadGroup(const AbstractLocker& destructionMutexLocker, ThreadGroup& threadGroup)
+{
+    ASSERT(canAddToThreadGroup(destructionMutexLocker));
+    m_threadGroups.add(&threadGroup);
+}
+
+void Thread::removeFromThreadGroup(const AbstractLocker&, ThreadGroup& threadGroup)
+{
+    m_threadGroups.remove(&threadGroup);
+}
+
 void Thread::setCurrentThreadIsUserInteractive(int relativePriority)
 {
 #if HAVE(QOS_CLASSES)

Modified: trunk/Source/WTF/wtf/Threading.h (219237 => 219238)


--- trunk/Source/WTF/wtf/Threading.h	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WTF/wtf/Threading.h	2017-07-07 04:56:23 UTC (rev 219238)
@@ -36,6 +36,7 @@
 #include <wtf/Atomics.h>
 #include <wtf/Expected.h>
 #include <wtf/Function.h>
+#include <wtf/HashSet.h>
 #include <wtf/PlatformRegisters.h>
 #include <wtf/RefPtr.h>
 #include <wtf/StackBounds.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 canAddToThreadGroup(const AbstractLocker& destructionMutexLocker);
+    void addToThreadGroup(const AbstractLocker& destructionMutexLocker, ThreadGroup&);
+    void removeFromThreadGroup(const AbstractLocker& destructionMutexLocker, 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() };
+    HashSet<ThreadGroup*> m_threadGroups;
     bool m_didExit { false };
 #if USE(PTHREADS)
     pthread_t m_handle;

Modified: trunk/Source/WebCore/ChangeLog (219237 => 219238)


--- trunk/Source/WebCore/ChangeLog	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WebCore/ChangeLog	2017-07-07 04:56:23 UTC (rev 219238)
@@ -1,3 +1,12 @@
+2017-07-05  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-06  Yusuke Suzuki  <utatane....@gmail.com>
 
         [WTF] Clean up StringStatics.cpp by using LazyNeverDestroyed<> for Atoms

Modified: trunk/Source/WebCore/page/ResourceUsageThread.h (219237 => 219238)


--- trunk/Source/WebCore/page/ResourceUsageThread.h	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WebCore/page/ResourceUsageThread.h	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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/WebKit2/ChangeLog (219237 => 219238)


--- trunk/Source/WebKit2/ChangeLog	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WebKit2/ChangeLog	2017-07-07 04:56:23 UTC (rev 219238)
@@ -1,3 +1,13 @@
+2017-07-05  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:
+        * UIProcess/Storage/ResourceLoadStatisticsStore.h:
+
 2017-07-06  Chris Dumez  <cdu...@apple.com>
 
         Drop unused ResourceLoadStatistics members

Modified: trunk/Source/WebKit2/Shared/AsyncRequest.h (219237 => 219238)


--- trunk/Source/WebKit2/Shared/AsyncRequest.h	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WebKit2/Shared/AsyncRequest.h	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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/Source/WebKit2/UIProcess/Storage/ResourceLoadStatisticsStore.h (219237 => 219238)


--- trunk/Source/WebKit2/UIProcess/Storage/ResourceLoadStatisticsStore.h	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Source/WebKit2/UIProcess/Storage/ResourceLoadStatisticsStore.h	2017-07-07 04:56:23 UTC (rev 219238)
@@ -29,6 +29,7 @@
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
 #include <wtf/MonotonicTime.h>
+#include <wtf/ThreadSafeRefCounted.h>
 #include <wtf/WallTime.h>
 #include <wtf/text/WTFString.h>
 

Modified: trunk/Tools/ChangeLog (219237 => 219238)


--- trunk/Tools/ChangeLog	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Tools/ChangeLog	2017-07-07 04:56:23 UTC (rev 219238)
@@ -1,3 +1,17 @@
+2017-07-05  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::TEST):
+
 2017-07-06  Yusuke Suzuki  <utatane....@gmail.com>
 
         [WTF] Clean up StringStatics.cpp by using LazyNeverDestroyed<> for Atoms

Modified: trunk/Tools/TestWebKitAPI/CMakeLists.txt (219237 => 219238)


--- trunk/Tools/TestWebKitAPI/CMakeLists.txt	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Tools/TestWebKitAPI/CMakeLists.txt	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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 (219237 => 219238)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-07-07 04:42:04 UTC (rev 219237)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2017-07-07 04:56:23 UTC (rev 219238)
@@ -631,6 +631,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 */; };
 		F415086D1DA040C50044BE9B /* play-audio-on-click.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F415086C1DA040C10044BE9B /* play-audio-on-click.html */; };
 		F41AB99F1EF4696B0083FA08 /* autofocus-contenteditable.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F41AB9981EF4692C0083FA08 /* autofocus-contenteditable.html */; };
@@ -1609,6 +1610,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>"; };
@@ -2391,6 +2393,7 @@
 				7C74D42D188228F300E5ED57 /* StringView.cpp */,
 				5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */,
 				9329AA281DE3F81E003ABD07 /* TextBreakIterator.cpp */,
+				E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */,
 				5311BD5D1EA9490D00525281 /* ThreadMessages.cpp */,
 				0F2C20B71DCD544800542D9E /* Time.cpp */,
 				5C5E633D1D0B67940085A025 /* UniqueRef.cpp */,
@@ -2890,6 +2893,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 => 219238)


--- trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp	2017-07-07 04:56:23 UTC (rev 219238)
@@ -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)
+{
+    Ref<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)
+{
+    Ref<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;
+    Ref<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