Changeset 219653 in webkit


Ignore:
Timestamp:
Jul 19, 2017 1:43:57 AM (7 years ago)
Author:
Yusuke Suzuki
Message:

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

Reviewed by Mark Lam.

Source/JavaScriptCore:

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).

  1. 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).
  2. 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/JavaScriptCore/wasm/WasmMachineThreads.cpp.

(WTF::ThreadGroup::~ThreadGroup):
(WTF::ThreadGroup::add):
(WTF::ThreadGroup::addCurrentThread):

  • wtf/ThreadGroup.h: Copied from Source/JavaScriptCore/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):

Location:
trunk
Files:
1 added
24 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/tests/ExecutionTimeLimitTest.cpp

    r219260 r219653  
    3737#include <wtf/CurrentTime.h>
    3838#include <wtf/Lock.h>
     39#include <wtf/Threading.h>
    3940#include <wtf/text/StringBuilder.h>
    4041
  • trunk/Source/JavaScriptCore/ChangeLog

    r219648 r219653  
     12017-07-19  Yusuke Suzuki  <utatane.tea@gmail.com>
     2
     3        [WTF] Implement WTF::ThreadGroup
     4        https://bugs.webkit.org/show_bug.cgi?id=174081
     5
     6        Reviewed by Mark Lam.
     7
     8        Large part of MachineThreads are now removed and replaced with WTF::ThreadGroup.
     9        And SamplingProfiler and others interact with WTF::Thread directly.
     10
     11        * API/tests/ExecutionTimeLimitTest.cpp:
     12        * heap/MachineStackMarker.cpp:
     13        (JSC::MachineThreads::MachineThreads):
     14        (JSC::captureStack):
     15        (JSC::MachineThreads::tryCopyOtherThreadStack):
     16        (JSC::MachineThreads::tryCopyOtherThreadStacks):
     17        (JSC::MachineThreads::gatherConservativeRoots):
     18        (JSC::ActiveMachineThreadsManager::Locker::Locker): Deleted.
     19        (JSC::ActiveMachineThreadsManager::add): Deleted.
     20        (JSC::ActiveMachineThreadsManager::remove): Deleted.
     21        (JSC::ActiveMachineThreadsManager::contains): Deleted.
     22        (JSC::ActiveMachineThreadsManager::ActiveMachineThreadsManager): Deleted.
     23        (JSC::activeMachineThreadsManager): Deleted.
     24        (JSC::MachineThreads::~MachineThreads): Deleted.
     25        (JSC::MachineThreads::addCurrentThread): Deleted.
     26        (): Deleted.
     27        (JSC::MachineThreads::removeThread): Deleted.
     28        (JSC::MachineThreads::removeThreadIfFound): Deleted.
     29        (JSC::MachineThreads::MachineThread::MachineThread): Deleted.
     30        (JSC::MachineThreads::MachineThread::getRegisters): Deleted.
     31        (JSC::MachineThreads::MachineThread::Registers::stackPointer): Deleted.
     32        (JSC::MachineThreads::MachineThread::Registers::framePointer): Deleted.
     33        (JSC::MachineThreads::MachineThread::Registers::instructionPointer): Deleted.
     34        (JSC::MachineThreads::MachineThread::Registers::llintPC): Deleted.
     35        (JSC::MachineThreads::MachineThread::captureStack): Deleted.
     36        * heap/MachineStackMarker.h:
     37        (JSC::MachineThreads::addCurrentThread):
     38        (JSC::MachineThreads::getLock):
     39        (JSC::MachineThreads::threads):
     40        (JSC::MachineThreads::MachineThread::suspend): Deleted.
     41        (JSC::MachineThreads::MachineThread::resume): Deleted.
     42        (JSC::MachineThreads::MachineThread::threadID): Deleted.
     43        (JSC::MachineThreads::MachineThread::stackBase): Deleted.
     44        (JSC::MachineThreads::MachineThread::stackEnd): Deleted.
     45        (JSC::MachineThreads::threadsListHead): Deleted.
     46        * runtime/SamplingProfiler.cpp:
     47        (JSC::FrameWalker::isValidFramePointer):
     48        (JSC::SamplingProfiler::SamplingProfiler):
     49        (JSC::SamplingProfiler::takeSample):
     50        (JSC::SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread):
     51        * runtime/SamplingProfiler.h:
     52        * wasm/WasmMachineThreads.cpp:
     53        (JSC::Wasm::resetInstructionCacheOnAllThreads):
     54
    1552017-07-18  Andy Estes  <aestes@apple.com>
    256
  • trunk/Source/JavaScriptCore/heap/MachineStackMarker.cpp

    r219647 r219653  
    2424
    2525#include "ConservativeRoots.h"
    26 #include "GPRInfo.h"
    27 #include "Heap.h"
    28 #include "JSArray.h"
    29 #include "JSCInlines.h"
    30 #include "LLIntPCRanges.h"
    31 #include "MacroAssembler.h"
    32 #include "VM.h"
     26#include "MachineContext.h"
    3327#include <setjmp.h>
    3428#include <stdlib.h>
    35 #include <wtf/MainThread.h>
     29#include <wtf/BitVector.h>
     30#include <wtf/PageBlock.h>
    3631#include <wtf/StdLibExtras.h>
    3732
     
    4035namespace JSC {
    4136
    42 class ActiveMachineThreadsManager;
    43 static ActiveMachineThreadsManager& activeMachineThreadsManager();
    44 
    45 class ActiveMachineThreadsManager {
    46     WTF_MAKE_NONCOPYABLE(ActiveMachineThreadsManager);
    47 public:
    48 
    49     class Locker {
    50     public:
    51         Locker(ActiveMachineThreadsManager& manager)
    52             : m_locker(manager.m_lock)
    53         {
    54         }
    55 
    56     private:
    57         LockHolder m_locker;
    58     };
    59 
    60     void add(MachineThreads* machineThreads)
    61     {
    62         LockHolder managerLock(m_lock);
    63         m_set.add(machineThreads);
    64     }
    65 
    66     void THREAD_SPECIFIC_CALL remove(MachineThreads* machineThreads)
    67     {
    68         LockHolder managerLock(m_lock);
    69         auto recordedMachineThreads = m_set.take(machineThreads);
    70         RELEASE_ASSERT(recordedMachineThreads == machineThreads);
    71     }
    72 
    73     bool contains(MachineThreads* machineThreads)
    74     {
    75         return m_set.contains(machineThreads);
    76     }
    77 
    78 private:
    79     typedef HashSet<MachineThreads*> MachineThreadsSet;
    80 
    81     ActiveMachineThreadsManager() { }
    82    
    83     Lock m_lock;
    84     MachineThreadsSet m_set;
    85 
    86     friend ActiveMachineThreadsManager& activeMachineThreadsManager();
    87 };
    88 
    89 static ActiveMachineThreadsManager& activeMachineThreadsManager()
    90 {
    91     static std::once_flag initializeManagerOnceFlag;
    92     static ActiveMachineThreadsManager* manager = nullptr;
    93 
    94     std::call_once(initializeManagerOnceFlag, [] {
    95         manager = new ActiveMachineThreadsManager();
    96     });
    97     return *manager;
    98 }
    99 
    100 #if CPU(X86_64) && OS(DARWIN)
    101 #define FILL_CALLEE_SAVES_FOR_CRASH_INFO(number)     \
    102     asm volatile(                                    \
    103         "movq $0xc0defefe000000" number ", %%rbx;" \
    104         "movq $0xc0defefe000000" number ", %%r12;" \
    105         "movq $0xc0defefe000000" number ", %%r13;" \
    106         "movq $0xc0defefe000000" number ", %%r14;" \
    107         "movq $0xc0defefe000000" number ", %%r15;" \
    108         :                                            \
    109         :                                            \
    110         : "%rbx", "%r12", "%r13", "%r14", "%r15"     \
    111     );
    112 
    113 #define FILL_CALLER_SAVES_FOR_CRASH_INFO(number)     \
    114     asm volatile(                                    \
    115         "movq $0xc0defefe000000" number ", %%rax;" \
    116         "movq $0xc0defefe000000" number ", %%rdi;" \
    117         "movq $0xc0defefe000000" number ", %%rsi;" \
    118         "movq $0xc0defefe000000" number ", %%rdx;" \
    119         "movq $0xc0defefe000000" number ", %%rcx;" \
    120         "movq $0xc0defefe000000" number ", %%r8;"  \
    121         "movq $0xc0defefe000000" number ", %%r9;"  \
    122         "movq $0xc0defefe000000" number ", %%r10;" \
    123         "movq $0xc0defefe000000" number ", %%r11;" \
    124         :                                            \
    125         :                                            \
    126         : "%rax", "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9", "%r10", "%r11" \
    127     );
    128 #else
    129 #define FILL_CALLEE_SAVES_FOR_CRASH_INFO(number)
    130 #define FILL_CALLER_SAVES_FOR_CRASH_INFO(number)
    131 #endif
    132 
    13337MachineThreads::MachineThreads()
    134     : m_registeredThreads()
    135     , m_threadSpecificForMachineThreads(0)
    136 {
    137     FILL_CALLEE_SAVES_FOR_CRASH_INFO("01");
    138     threadSpecificKeyCreate(&m_threadSpecificForMachineThreads, removeThread);
    139     FILL_CALLEE_SAVES_FOR_CRASH_INFO("02");
    140     activeMachineThreadsManager().add(this);
    141     FILL_CALLER_SAVES_FOR_CRASH_INFO("03");
    142 }
    143 
    144 MachineThreads::~MachineThreads()
    145 {
    146     activeMachineThreadsManager().remove(this);
    147     threadSpecificKeyDelete(m_threadSpecificForMachineThreads);
    148 
    149     LockHolder registeredThreadsLock(m_registeredThreadsMutex);
    150     for (MachineThread* current = m_registeredThreads.head(); current;) {
    151         MachineThread* next = current->next();
    152         delete current;
    153         current = next;
    154     }
    155 }
    156 
    157 void MachineThreads::addCurrentThread()
    158 {
    159     if (threadSpecificGet(m_threadSpecificForMachineThreads)) {
    160 #ifndef NDEBUG
    161         LockHolder lock(m_registeredThreadsMutex);
    162         ASSERT(threadSpecificGet(m_threadSpecificForMachineThreads) == this);
    163 #endif
    164         return;
    165     }
    166 
    167     MachineThread* thread = new MachineThread();
    168     threadSpecificSet(m_threadSpecificForMachineThreads, this);
    169 
    170     LockHolder lock(m_registeredThreadsMutex);
    171 
    172     m_registeredThreads.append(thread);
    173 }
    174 
    175 auto MachineThreads::machineThreadForCurrentThread() -> MachineThread*
    176 {
    177     LockHolder lock(m_registeredThreadsMutex);
    178     ThreadIdentifier id = currentThread();
    179     for (MachineThread* thread = m_registeredThreads.head(); thread; thread = thread->next()) {
    180         if (thread->threadID() == id)
    181             return thread;
    182     }
    183 
    184     RELEASE_ASSERT_NOT_REACHED();
    185     return nullptr;
    186 }
    187 
    188 void THREAD_SPECIFIC_CALL MachineThreads::removeThread(void* p)
    189 {
    190     auto& manager = activeMachineThreadsManager();
    191     ActiveMachineThreadsManager::Locker lock(manager);
    192     auto machineThreads = static_cast<MachineThreads*>(p);
    193     if (manager.contains(machineThreads)) {
    194         // There's a chance that the MachineThreads registry that this thread
    195         // was registered with was already destructed, and another one happened
    196         // to be instantiated at the same address. Hence, this thread may or
    197         // may not be found in this MachineThreads registry. We only need to
    198         // do a removal if this thread is found in it.
    199 
    200 #if OS(WINDOWS)
    201         // On Windows the thread specific destructor is also called when the
    202         // main thread is exiting. This may lead to the main thread waiting
    203         // forever for the machine thread lock when exiting, if the sampling
    204         // profiler thread was terminated by the system while holding the
    205         // machine thread lock.
    206         if (WTF::isMainThread())
    207             return;
    208 #endif
    209 
    210         machineThreads->removeThreadIfFound(currentThread());
    211     }
    212 }
    213 
    214 void MachineThreads::removeThreadIfFound(ThreadIdentifier id)
    215 {
    216     LockHolder lock(m_registeredThreadsMutex);
    217     for (MachineThread* current = m_registeredThreads.head(); current; current = current->next()) {
    218         if (current->threadID() == id) {
    219             m_registeredThreads.remove(current);
    220             delete current;
    221             break;
    222         }
    223     }
     38    : m_threadGroup(ThreadGroup::create())
     39{
    22440}
    22541
     
    23551    conservativeRoots.add(currentThreadState.stackTop, currentThreadState.stackOrigin, jitStubRoutines, codeBlocks);
    23652}
    237 
    238 MachineThreads::MachineThread::MachineThread()
    239     : m_thread(WTF::Thread::current())
    240 {
    241 }
    242 
    243 size_t MachineThreads::MachineThread::getRegisters(MachineThread::Registers& registers)
    244 {
    245     WTF::PlatformRegisters& regs = registers.regs;
    246     return m_thread->getRegisters(regs);
    247 }
    248 
    249 void* MachineThreads::MachineThread::Registers::stackPointer() const
    250 {
    251     return MachineContext::stackPointer(regs);
    252 }
    253 
    254 #if ENABLE(SAMPLING_PROFILER)
    255 void* MachineThreads::MachineThread::Registers::framePointer() const
    256 {
    257 #if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)
    258     return MachineContext::framePointer(regs);
    259 #else
    260 #error Need a way to get the frame pointer for another thread on this platform
    261 #endif
    262 }
    263 
    264 void* MachineThreads::MachineThread::Registers::instructionPointer() const
    265 {
    266 #if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)
    267     return MachineContext::instructionPointer(regs);
    268 #else
    269 #error Need a way to get the instruction pointer for another thread on this platform
    270 #endif
    271 }
    272 
    273 void* MachineThreads::MachineThread::Registers::llintPC() const
    274 {
    275     // LLInt uses regT4 as PC.
    276 #if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)
    277     return MachineContext::llintInstructionPointer(regs);
    278 #else
    279 #error Need a way to get the LLIntPC for another thread on this platform
    280 #endif
    281 }
    282 #endif // ENABLE(SAMPLING_PROFILER)
    28353
    28454static inline int osRedZoneAdjustment()
     
    29767}
    29868
    299 std::pair<void*, size_t> MachineThreads::MachineThread::captureStack(void* stackTop)
    300 {
    301     char* begin = reinterpret_cast_ptr<char*>(stackBase());
     69static std::pair<void*, size_t> captureStack(Thread& thread, void* stackTop)
     70{
     71    char* begin = reinterpret_cast_ptr<char*>(thread.stack().origin());
    30272    char* end = bitwise_cast<char*>(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackTop)));
    30373    ASSERT(begin >= end);
     
    30676    ASSERT(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(endWithRedZone)) == reinterpret_cast<uintptr_t>(endWithRedZone));
    30777
    308     if (endWithRedZone < stackEnd())
    309         endWithRedZone = reinterpret_cast_ptr<char*>(stackEnd());
     78    if (endWithRedZone < thread.stack().end())
     79        endWithRedZone = reinterpret_cast_ptr<char*>(thread.stack().end());
    31080
    31181    std::swap(begin, endWithRedZone);
     
    340110// operation. As the heap is generally much larger than the stack the performance hit is minimal.
    341111// See: https://bugs.webkit.org/show_bug.cgi?id=146297
    342 void MachineThreads::tryCopyOtherThreadStack(MachineThread* thread, void* buffer, size_t capacity, size_t* size)
    343 {
    344     MachineThread::Registers registers;
    345     size_t registersSize = thread->getRegisters(registers);
     112void MachineThreads::tryCopyOtherThreadStack(Thread& thread, void* buffer, size_t capacity, size_t* size)
     113{
     114    PlatformRegisters registers;
     115    size_t registersSize = thread.getRegisters(registers);
    346116
    347117    // This is a workaround for <rdar://problem/27607384>. libdispatch recycles work
    348118    // queue threads without running pthread exit destructors. This can cause us to scan a
    349119    // thread during work queue initialization, when the stack pointer is null.
    350     if (UNLIKELY(!registers.stackPointer())) {
     120    if (UNLIKELY(!MachineContext::stackPointer(registers))) {
    351121        *size = 0;
    352122        return;
    353123    }
    354124
    355     std::pair<void*, size_t> stack = thread->captureStack(registers.stackPointer());
     125    std::pair<void*, size_t> stack = captureStack(thread, MachineContext::stackPointer(registers));
    356126
    357127    bool canCopy = *size + registersSize + stack.second <= capacity;
     
    366136}
    367137
    368 bool MachineThreads::tryCopyOtherThreadStacks(const AbstractLocker&, void* buffer, size_t capacity, size_t* size)
     138bool MachineThreads::tryCopyOtherThreadStacks(const AbstractLocker& locker, void* buffer, size_t capacity, size_t* size)
    369139{
    370140    // Prevent two VMs from suspending each other's threads at the same time,
     
    375145    *size = 0;
    376146
    377     ThreadIdentifier id = currentThread();
    378     int numberOfThreads = 0; // Using 0 to denote that we haven't counted the number of threads yet.
    379     int index = 1;
    380     DoublyLinkedList<MachineThread> threadsToBeDeleted;
    381 
    382     for (MachineThread* thread = m_registeredThreads.head(); thread; index++) {
    383         if (thread->threadID() != id) {
    384             auto result = thread->suspend();
     147    Thread& currentThread = Thread::current();
     148    const auto& threads = m_threadGroup->threads(locker);
     149    BitVector isSuspended(threads.size());
     150
     151    {
     152        unsigned index = 0;
     153        for (auto& thread : threads) {
     154            if (thread.get() != currentThread) {
     155                auto result = thread->suspend();
     156                if (result)
     157                    isSuspended.set(index);
     158                else {
    385159#if OS(DARWIN)
    386             if (!result) {
    387                 if (!numberOfThreads)
    388                     numberOfThreads = m_registeredThreads.size();
    389 
    390                 ASSERT(result.error() != KERN_SUCCESS);
    391 
    392                 WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
    393                     "JavaScript garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] id %u.",
    394                     result.error(), index, numberOfThreads, thread, thread->threadID());
    395 
    396                 // Put the invalid thread on the threadsToBeDeleted list.
    397                 // We can't just delete it here because we have suspended other
    398                 // threads, and they may still be holding the C heap lock which
    399                 // we need for deleting the invalid thread. Hence, we need to
    400                 // defer the deletion till after we have resumed all threads.
    401                 MachineThread* nextThread = thread->next();
    402                 m_registeredThreads.remove(thread);
    403                 threadsToBeDeleted.append(thread);
    404                 thread = nextThread;
    405                 continue;
     160                    // These threads will be removed from the ThreadGroup. Thus, we do not do anything here except for reporting.
     161                    ASSERT(result.error() != KERN_SUCCESS);
     162                    WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
     163                        "JavaScript garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] id %u.",
     164                        result.error(), index, threads.size(), thread.ptr(), thread->id());
     165#endif
     166                }
    406167            }
    407 #else
    408             UNUSED_PARAM(numberOfThreads);
    409             ASSERT_UNUSED(result, result);
    410 #endif
     168            ++index;
    411169        }
    412         thread = thread->next();
    413     }
    414 
    415     for (MachineThread* thread = m_registeredThreads.head(); thread; thread = thread->next()) {
    416         if (thread->threadID() != id)
    417             tryCopyOtherThreadStack(thread, buffer, capacity, size);
    418     }
    419 
    420     for (MachineThread* thread = m_registeredThreads.head(); thread; thread = thread->next()) {
    421         if (thread->threadID() != id)
    422             thread->resume();
    423     }
    424 
    425     for (MachineThread* thread = threadsToBeDeleted.head(); thread; ) {
    426         MachineThread* nextThread = thread->next();
    427         delete thread;
    428         thread = nextThread;
    429     }
    430    
     170    }
     171
     172    {
     173        unsigned index = 0;
     174        for (auto& thread : threads) {
     175            if (isSuspended.get(index))
     176                tryCopyOtherThreadStack(thread.get(), buffer, capacity, size);
     177            ++index;
     178        }
     179    }
     180
     181    {
     182        unsigned index = 0;
     183        for (auto& thread : threads) {
     184            if (isSuspended.get(index))
     185                thread->resume();
     186            ++index;
     187        }
     188    }
     189
    431190    return *size <= capacity;
    432191}
     
    449208    size_t capacity = 0;
    450209    void* buffer = nullptr;
    451     LockHolder lock(m_registeredThreadsMutex);
    452     while (!tryCopyOtherThreadStacks(lock, buffer, capacity, &size))
     210    auto locker = holdLock(m_threadGroup->getLock());
     211    while (!tryCopyOtherThreadStacks(locker, buffer, capacity, &size))
    453212        growBuffer(size, &buffer, &capacity);
    454213
  • trunk/Source/JavaScriptCore/heap/MachineStackMarker.h

    r219647 r219653  
    2222#pragma once
    2323
    24 #include "MachineContext.h"
    2524#include "RegisterState.h"
    26 #include <wtf/DoublyLinkedList.h>
    2725#include <wtf/Lock.h>
    2826#include <wtf/ScopedLambda.h>
    29 #include <wtf/ThreadSpecific.h>
     27#include <wtf/ThreadGroup.h>
    3028
    3129namespace JSC {
     
    4644public:
    4745    MachineThreads();
    48     ~MachineThreads();
    4946
    5047    void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, CurrentThreadState*);
    5148
    52     JS_EXPORT_PRIVATE void addCurrentThread(); // Only needs to be called by clients that can use the same heap from multiple threads.
     49    // Only needs to be called by clients that can use the same heap from multiple threads.
     50    void addCurrentThread() { m_threadGroup->addCurrentThread(); }
    5351
    54     class MachineThread : public DoublyLinkedListNode<MachineThread> {
    55         WTF_MAKE_FAST_ALLOCATED;
    56     public:
    57         MachineThread();
    58 
    59         struct Registers {
    60             void* stackPointer() const;
    61 #if ENABLE(SAMPLING_PROFILER)
    62             void* framePointer() const;
    63             void* instructionPointer() const;
    64             void* llintPC() const;
    65 #endif // ENABLE(SAMPLING_PROFILER)
    66             PlatformRegisters regs;
    67         };
    68 
    69         Expected<void, Thread::PlatformSuspendError> suspend() { return m_thread->suspend(); }
    70         void resume() { m_thread->resume(); }
    71         size_t getRegisters(Registers& regs);
    72         std::pair<void*, size_t> captureStack(void* stackTop);
    73 
    74         WTF::ThreadIdentifier threadID() const { return m_thread->id(); }
    75         void* stackBase() const { return m_thread->stack().origin(); }
    76         void* stackEnd() const { return m_thread->stack().end(); }
    77 
    78         Ref<WTF::Thread> m_thread;
    79         MachineThread* m_next { nullptr };
    80         MachineThread* m_prev { nullptr };
    81     };
    82 
    83     Lock& getLock() { return m_registeredThreadsMutex; }
    84     const DoublyLinkedList<MachineThread>& threadsListHead(const AbstractLocker&) const { ASSERT(m_registeredThreadsMutex.isLocked()); return m_registeredThreads; }
    85     MachineThread* machineThreadForCurrentThread();
     52    std::mutex& getLock() { return m_threadGroup->getLock(); }
     53    const ListHashSet<Ref<Thread>>& threads(const AbstractLocker& locker) const { return m_threadGroup->threads(locker); }
    8654
    8755private:
    8856    void gatherFromCurrentThread(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, CurrentThreadState&);
    8957
    90     void tryCopyOtherThreadStack(MachineThread*, void*, size_t capacity, size_t*);
     58    void tryCopyOtherThreadStack(Thread&, void*, size_t capacity, size_t*);
    9159    bool tryCopyOtherThreadStacks(const AbstractLocker&, void*, size_t capacity, size_t*);
    9260
    93     static void THREAD_SPECIFIC_CALL removeThread(void*);
    94 
    95     void removeThreadIfFound(ThreadIdentifier);
    96 
    97     Lock m_registeredThreadsMutex;
    98     DoublyLinkedList<MachineThread> m_registeredThreads;
    99     WTF::ThreadSpecificKey m_threadSpecificForMachineThreads;
     61    std::shared_ptr<ThreadGroup> m_threadGroup;
    10062};
    10163
  • trunk/Source/JavaScriptCore/runtime/SamplingProfiler.cpp

    r219260 r219653  
    4040#include "JSFunction.h"
    4141#include "LLIntPCRanges.h"
     42#include "MachineContext.h"
    4243#include "MarkedBlock.h"
    4344#include "MarkedBlockSet.h"
     
    166167    {
    167168        uint8_t* fpCast = bitwise_cast<uint8_t*>(exec);
    168         const auto& threadList = m_vm.heap.machineThreads().threadsListHead(m_machineThreadsLocker);
    169         for (MachineThreads::MachineThread* thread = threadList.head(); thread; thread = thread->next()) {
    170             uint8_t* stackBase = static_cast<uint8_t*>(thread->stackBase());
    171             uint8_t* stackLimit = static_cast<uint8_t*>(thread->stackEnd());
     169        for (auto& thread : m_vm.heap.machineThreads().threads(m_machineThreadsLocker)) {
     170            uint8_t* stackBase = static_cast<uint8_t*>(thread->stack().origin());
     171            uint8_t* stackLimit = static_cast<uint8_t*>(thread->stack().end());
    172172            RELEASE_ASSERT(stackBase);
    173173            RELEASE_ASSERT(stackLimit);
     
    279279    , m_stopwatch(WTFMove(stopwatch))
    280280    , m_timingInterval(std::chrono::microseconds(Options::sampleInterval()))
    281     , m_jscExecutionThread(nullptr)
    282281    , m_isPaused(false)
    283282    , m_isShutDown(false)
     
    339338        double nowTime = m_stopwatch->elapsedTime();
    340339
    341         LockHolder machineThreadsLocker(m_vm.heap.machineThreads().getLock());
     340        auto machineThreadsLocker = holdLock(m_vm.heap.machineThreads().getLock());
    342341        LockHolder codeBlockSetLocker(m_vm.heap.codeBlockSet().getLock());
    343342        LockHolder executableAllocatorLocker(ExecutableAllocator::singleton().getLock());
     
    353352            void* llintPC;
    354353            {
    355                 MachineThreads::MachineThread::Registers registers;
     354                PlatformRegisters registers;
    356355                m_jscExecutionThread->getRegisters(registers);
    357                 machineFrame = registers.framePointer();
     356                machineFrame = MachineContext::framePointer(registers);
    358357                callFrame = static_cast<ExecState*>(machineFrame);
    359                 machinePC = registers.instructionPointer();
    360                 llintPC = registers.llintPC();
     358                machinePC = MachineContext::instructionPointer(registers);
     359                llintPC = MachineContext::llintInstructionPointer(registers);
    361360            }
    362361            // FIXME: Lets have a way of detecting when we're parsing code.
     
    679678{
    680679    ASSERT(m_lock.isLocked());
    681     m_jscExecutionThread = m_vm.heap.machineThreads().machineThreadForCurrentThread();
     680    m_jscExecutionThread = &Thread::current();
    682681}
    683682
  • trunk/Source/JavaScriptCore/runtime/SamplingProfiler.h

    r219260 r219653  
    197197    Lock m_lock;
    198198    RefPtr<Thread> m_thread;
    199     MachineThreads::MachineThread* m_jscExecutionThread;
     199    RefPtr<Thread> m_jscExecutionThread;
    200200    bool m_isPaused;
    201201    bool m_isShutDown;
  • trunk/Source/JavaScriptCore/wasm/WasmMachineThreads.cpp

    r219260 r219653  
    5656{
    5757    auto locker = holdLock(wasmThreads().getLock());
    58 
    59     const DoublyLinkedList<MachineThreads::MachineThread>& threads = wasmThreads().threadsListHead(locker);
    60     for (const auto* thread = threads.head(); thread; thread = thread->next()) {
    61         sendMessage(thread->m_thread.get(), [] (const PlatformRegisters&) {
     58    for (auto& thread : wasmThreads().threads(locker)) {
     59        sendMessage(thread.get(), [] (const PlatformRegisters&) {
    6260            // It's likely that the signal handler will already reset the instruction cache but we might as well be sure.
    6361            WTF::crossModifyingCodeFence();
  • trunk/Source/WTF/ChangeLog

    r219648 r219653  
     12017-07-19  Yusuke Suzuki  <utatane.tea@gmail.com>
     2
     3        [WTF] Implement WTF::ThreadGroup
     4        https://bugs.webkit.org/show_bug.cgi?id=174081
     5
     6        Reviewed by Mark Lam.
     7
     8        This patch implements WTF::ThreadGroup. It implements core of JSC::MachineThreads with more reliable way.
     9        JSC::MachineThreads was complicated because of managing dead threads. Each JSC::MachineThreads has its
     10        own TLS with a registered destructor. And everytime a thread dies, the registered TLS destructor is called.
     11        And this destructor will remove the current dying thread from JSC::MachineThreads.
     12
     13        However the above implementation is tricky. And each JSC::MachineThreads requires own TLS space, which is
     14        not considered in WTF's Windows ThreadSpecific implementation. Current design works well since we only
     15        have small number of MachineThreads right now.
     16
     17        Instead, we use more reliable way. After introducing WTF::Thread, WTF::Thread has WTF::Thread::didExit,
     18        which is called when associated TLS (with WTF::Thread) is destroyed. We leverage this mechanism to remove
     19        WTF::Thread from MachineThreads.
     20
     21        This patch introduces WTF::ThreadGroup. It is tightly integrated with WTF::Thread: WTF::Thread knows
     22        ThreadGroups which includes this thread. And WTF::ThreadGroup of course knows WTF::Threads added to it.
     23        WTF::Thread::didExit carefully remove itself from WTF::ThreadGroups.
     24
     25        The most important part of this patch is locking. WTF::Thread can die. And WTF::ThreadGroup can die.
     26        If we take a design using two fine grain locks in WTF::Thread and WTF::ThreadGroup, we easily encounter
     27        dead lock. Consider the following case.
     28
     29        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).
     30        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).
     31        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).
     32        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).
     33
     34        Then, it easily causes dead lock. We cannot swap the locking order of (2) since iterating THG requires a lock of TH.
     35        To solve this problem, we use std::shared_ptr and std::weak_ptr.
     36
     37        1. When adding WTF::Thread (TH) to WTF::ThreadGroup (THG), we first hold THG, and hold a lock of TH. (THG -> TH)
     38        2. When TH dies, TH first hold lock of TH. And we use std::weak_ptr<>::lock() to retain non-destructed ThreadGroups.
     39        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
     40        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
     41        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).
     42        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).
     43        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).
     44
     45        We also fix suspend and resume locking mechanism to avoid dead lock. We should hold the global lock when suspending and resuming.
     46        If we use per-thread lock, the suspended thread can hold the lock of the other threads. It causes dead lock.
     47
     48        * WTF.xcodeproj/project.pbxproj:
     49        * wtf/AutomaticThread.cpp:
     50        * wtf/CMakeLists.txt:
     51        * wtf/CrossThreadCopier.h:
     52        * wtf/ParkingLot.h:
     53        * wtf/ThreadGroup.cpp: Copied from Source/JavaScriptCore/wasm/WasmMachineThreads.cpp.
     54        (WTF::ThreadGroup::~ThreadGroup):
     55        (WTF::ThreadGroup::add):
     56        (WTF::ThreadGroup::addCurrentThread):
     57        * wtf/ThreadGroup.h: Copied from Source/JavaScriptCore/wasm/WasmMachineThreads.cpp.
     58        (WTF::ThreadGroup::create):
     59        (WTF::ThreadGroup::threads):
     60        (WTF::ThreadGroup::getLock):
     61        (WTF::ThreadGroup::weakFromThis):
     62        * wtf/Threading.cpp:
     63        (WTF::shouldRemoveThreadFromThreadGroup):
     64        (WTF::Thread::didExit):
     65        (WTF::Thread::addToThreadGroup):
     66        (WTF::Thread::removeFromThreadGroup):
     67        * wtf/Threading.h:
     68        * wtf/ThreadingPthreads.cpp:
     69        (WTF::Thread::resume):
     70        (WTF::Thread::getRegisters):
     71        * wtf/ThreadingWin.cpp:
     72        (WTF::Thread::resume):
     73        (WTF::Thread::getRegisters):
     74
    1752017-07-18  Andy Estes  <aestes@apple.com>
    276
  • trunk/Source/WTF/WTF.xcodeproj/project.pbxproj

    r219260 r219653  
    132132                DCEE22031CEA7551000C2396 /* PlatformUserPreferredLanguagesMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = DCEE21FF1CEA7551000C2396 /* PlatformUserPreferredLanguagesMac.mm */; };
    133133                E15556F518A0CC18006F48FB /* CryptographicUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E15556F318A0CC18006F48FB /* CryptographicUtilities.cpp */; };
     134                E311FB171F0A568B003C08DE /* ThreadGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E311FB151F0A568B003C08DE /* ThreadGroup.cpp */; };
    134135                E3200AB81E9A536D003B59D2 /* ThreadHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */; };
    135136                E38C41251EB4E04C0042957D /* CPUTimeCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = E38C41241EB4E04C0042957D /* CPUTimeCocoa.mm */; };
     
    540541                E15556F318A0CC18006F48FB /* CryptographicUtilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptographicUtilities.cpp; sourceTree = "<group>"; };
    541542                E15556F418A0CC18006F48FB /* CryptographicUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptographicUtilities.h; sourceTree = "<group>"; };
     543                E311FB151F0A568B003C08DE /* ThreadGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadGroup.cpp; sourceTree = "<group>"; };
     544                E311FB161F0A568B003C08DE /* ThreadGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadGroup.h; sourceTree = "<group>"; };
    542545                E3200AB41E9A536D003B59D2 /* PlatformRegisters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformRegisters.h; sourceTree = "<group>"; };
    543546                E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadHolder.cpp; sourceTree = "<group>"; };
     
    957960                                0FB317C31C488001007E395A /* SystemTracing.h */,
    958961                                A8A4732F151A825B004123FF /* ThreadFunctionInvocation.h */,
     962                                E311FB151F0A568B003C08DE /* ThreadGroup.cpp */,
     963                                E311FB161F0A568B003C08DE /* ThreadGroup.h */,
    959964                                E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */,
    960965                                E3200AB61E9A536D003B59D2 /* ThreadHolder.h */,
     
    13891394                                A8A4744A151A825B004123FF /* Threading.cpp in Sources */,
    13901395                                A8A4744E151A825B004123FF /* ThreadingPthreads.cpp in Sources */,
     1396                                E311FB171F0A568B003C08DE /* ThreadGroup.cpp in Sources */,
    13911397                                5311BD5C1EA822F900525281 /* ThreadMessage.cpp in Sources */,
    13921398                                0F66B2901DC97BAB004A1D3F /* TimeWithDynamicClockType.cpp in Sources */,
  • trunk/Source/WTF/wtf/AutomaticThread.cpp

    r219260 r219653  
    2828
    2929#include "DataLog.h"
     30#include "Threading.h"
    3031
    3132namespace WTF {
  • trunk/Source/WTF/wtf/CMakeLists.txt

    r219560 r219653  
    133133    SystemFree.h
    134134    SystemTracing.h
     135    ThreadGroup.h
    135136    ThreadHolder.cpp
    136137    ThreadMessage.h
     
    247248    StackTrace.cpp
    248249    StringPrintStream.cpp
     250    ThreadGroup.cpp
    249251    ThreadMessage.cpp
    250252    Threading.cpp
  • trunk/Source/WTF/wtf/CrossThreadCopier.h

    r219260 r219653  
    3636#include <wtf/HashSet.h>
    3737#include <wtf/RefPtr.h>
     38#include <wtf/ThreadSafeRefCounted.h>
    3839#include <wtf/text/WTFString.h>
    3940
  • trunk/Source/WTF/wtf/ParkingLot.h

    r219260 r219653  
    2929#include <wtf/Atomics.h>
    3030#include <wtf/ScopedLambda.h>
    31 #include <wtf/Threading.h>
    3231#include <wtf/TimeWithDynamicClockType.h>
    3332
    3433namespace WTF {
     34
     35class Thread;
    3536
    3637class ParkingLot {
  • trunk/Source/WTF/wtf/ThreadGroup.cpp

    r219652 r219653  
    11/*
    2  * Copyright (C) 2017 Apple Inc. All rights reserved.
     2 * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2525
    2626#include "config.h"
    27 #include "WasmMachineThreads.h"
     27#include "ThreadGroup.h"
    2828
    29 #if ENABLE(WEBASSEMBLY)
     29#include <wtf/NeverDestroyed.h>
    3030
    31 #include "MachineStackMarker.h"
    32 #include <wtf/NeverDestroyed.h>
    33 #include <wtf/ThreadMessage.h>
    34 #include <wtf/threads/Signals.h>
     31namespace WTF {
    3532
    36 namespace JSC { namespace Wasm {
    37 
    38 
    39 inline MachineThreads& wasmThreads()
     33ThreadGroup::~ThreadGroup()
    4034{
    41     static LazyNeverDestroyed<MachineThreads> threads;
    42     static std::once_flag once;
    43     std::call_once(once, [] {
    44         threads.construct();
    45     });
    46 
    47     return threads;
     35    std::lock_guard<std::mutex> locker(m_lock);
     36    for (auto& thread : m_threads)
     37        thread->removeFromThreadGroup(locker, *this);
    4838}
    4939
    50 void startTrackingCurrentThread()
     40bool ThreadGroup::add(Thread& thread)
    5141{
    52     wasmThreads().addCurrentThread();
     42    std::lock_guard<std::mutex> locker(m_lock);
     43    return thread.addToThreadGroup(locker, *this);
    5344}
    5445
    55 void resetInstructionCacheOnAllThreads()
     46void ThreadGroup::addCurrentThread()
    5647{
    57     auto locker = holdLock(wasmThreads().getLock());
    58 
    59     const DoublyLinkedList<MachineThreads::MachineThread>& threads = wasmThreads().threadsListHead(locker);
    60     for (const auto* thread = threads.head(); thread; thread = thread->next()) {
    61         sendMessage(thread->m_thread.get(), [] (const PlatformRegisters&) {
    62             // It's likely that the signal handler will already reset the instruction cache but we might as well be sure.
    63             WTF::crossModifyingCodeFence();
    64         });
    65     }
     48    bool isAdded = add(Thread::current());
     49    ASSERT_UNUSED(isAdded, isAdded);
    6650}
    6751
    68    
    69 } } // namespace JSC::Wasm
    70 
    71 #endif // ENABLE(WEBASSEMBLY)
     52} // namespace WTF
  • trunk/Source/WTF/wtf/ThreadGroup.h

    r219652 r219653  
    11/*
    2  * Copyright (C) 2017 Apple Inc. All rights reserved.
     2 * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2424 */
    2525
    26 #include "config.h"
    27 #include "WasmMachineThreads.h"
     26#pragma once
    2827
    29 #if ENABLE(WEBASSEMBLY)
     28#include <memory>
     29#include <wtf/ListHashSet.h>
     30#include <wtf/Lock.h>
     31#include <wtf/Threading.h>
    3032
    31 #include "MachineStackMarker.h"
    32 #include <wtf/NeverDestroyed.h>
    33 #include <wtf/ThreadMessage.h>
    34 #include <wtf/threads/Signals.h>
     33namespace WTF {
    3534
    36 namespace JSC { namespace Wasm {
     35class ThreadGroup : public std::enable_shared_from_this<ThreadGroup> {
     36public:
     37    friend class Thread;
    3738
     39    static std::shared_ptr<ThreadGroup> create()
     40    {
     41        return std::make_shared<ThreadGroup>();
     42    }
    3843
    39 inline MachineThreads& wasmThreads()
    40 {
    41     static LazyNeverDestroyed<MachineThreads> threads;
    42     static std::once_flag once;
    43     std::call_once(once, [] {
    44         threads.construct();
    45     });
     44    WTF_EXPORT_PRIVATE bool add(Thread&);
     45    WTF_EXPORT_PRIVATE void addCurrentThread();
    4646
    47     return threads;
     47    const ListHashSet<Ref<Thread>>& threads(const AbstractLocker&) const { return m_threads; }
     48
     49    std::mutex& getLock() { return m_lock; }
     50
     51    WTF_EXPORT_PRIVATE ~ThreadGroup();
     52
     53    ThreadGroup() = default;
     54
     55private:
     56    std::weak_ptr<ThreadGroup> weakFromThis()
     57    {
     58        return shared_from_this();
     59    }
     60
     61    // We use std::mutex since it can be used when deallocating TLS.
     62    std::mutex m_lock;
     63    ListHashSet<Ref<Thread>> m_threads;
     64};
     65
    4866}
    4967
    50 void startTrackingCurrentThread()
    51 {
    52     wasmThreads().addCurrentThread();
    53 }
    54 
    55 void resetInstructionCacheOnAllThreads()
    56 {
    57     auto locker = holdLock(wasmThreads().getLock());
    58 
    59     const DoublyLinkedList<MachineThreads::MachineThread>& threads = wasmThreads().threadsListHead(locker);
    60     for (const auto* thread = threads.head(); thread; thread = thread->next()) {
    61         sendMessage(thread->m_thread.get(), [] (const PlatformRegisters&) {
    62             // It's likely that the signal handler will already reset the instruction cache but we might as well be sure.
    63             WTF::crossModifyingCodeFence();
    64         });
    65     }
    66 }
    67 
    68    
    69 } } // namespace JSC::Wasm
    70 
    71 #endif // ENABLE(WEBASSEMBLY)
     68using WTF::ThreadGroup;
  • trunk/Source/WTF/wtf/Threading.cpp

    r219647 r219653  
    2727#include "Threading.h"
    2828
    29 #include "dtoa.h"
    30 #include "dtoa/cached-powers.h"
    3129#include <algorithm>
    3230#include <cmath>
     
    3533#include <wtf/PrintStream.h>
    3634#include <wtf/RandomNumberSeed.h>
     35#include <wtf/ThreadGroup.h>
    3736#include <wtf/ThreadHolder.h>
    3837#include <wtf/ThreadMessage.h>
     
    131130}
    132131
     132static bool shouldRemoveThreadFromThreadGroup()
     133{
     134#if OS(WINDOWS)
     135    // On Windows the thread specific destructor is also called when the
     136    // main thread is exiting. This may lead to the main thread waiting
     137    // forever for the thread group lock when exiting, if the sampling
     138    // profiler thread was terminated by the system while holding the
     139    // thread group lock.
     140    if (WTF::isMainThread())
     141        return false;
     142#endif
     143    return true;
     144}
     145
    133146void Thread::didExit()
    134147{
     148    if (shouldRemoveThreadFromThreadGroup()) {
     149        Vector<std::shared_ptr<ThreadGroup>> threadGroups;
     150        {
     151            std::lock_guard<std::mutex> locker(m_mutex);
     152            for (auto& threadGroup : m_threadGroups) {
     153                // If ThreadGroup is just being destroyed,
     154                // we do not need to perform unregistering.
     155                if (auto retained = threadGroup.lock())
     156                    threadGroups.append(WTFMove(retained));
     157            }
     158            m_isShuttingDown = true;
     159        }
     160        for (auto& threadGroup : threadGroups) {
     161            std::lock_guard<std::mutex> threadGroupLocker(threadGroup->getLock());
     162            std::lock_guard<std::mutex> locker(m_mutex);
     163            threadGroup->m_threads.remove(*this);
     164        }
     165    }
     166    // We would like to say "thread is exited" after unregistering threads from thread groups.
     167    // So we need to separate m_isShuttingDown from m_didExit.
    135168    std::lock_guard<std::mutex> locker(m_mutex);
    136169    m_didExit = true;
     170}
     171
     172bool Thread::addToThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup& threadGroup)
     173{
     174    UNUSED_PARAM(threadGroupLocker);
     175    std::lock_guard<std::mutex> locker(m_mutex);
     176    if (m_isShuttingDown)
     177        return false;
     178    if (threadGroup.m_threads.add(*this).isNewEntry)
     179        m_threadGroups.append(threadGroup.weakFromThis());
     180    return true;
     181}
     182
     183void Thread::removeFromThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup& threadGroup)
     184{
     185    UNUSED_PARAM(threadGroupLocker);
     186    std::lock_guard<std::mutex> locker(m_mutex);
     187    if (m_isShuttingDown)
     188        return;
     189    m_threadGroups.removeFirstMatching([&] (auto weakPtr) {
     190        if (auto sharedPtr = weakPtr.lock())
     191            return sharedPtr.get() == &threadGroup;
     192        return false;
     193    });
    137194}
    138195
  • trunk/Source/WTF/wtf/Threading.h

    r219647 r219653  
    4141#include <wtf/StackBounds.h>
    4242#include <wtf/ThreadSafeRefCounted.h>
     43#include <wtf/Vector.h>
    4344
    4445#if USE(PTHREADS) && !OS(DARWIN)
     
    4950namespace WTF {
    5051
     52class AbstractLocker;
    5153class ThreadMessageData;
    5254
     
    5456typedef void (*ThreadFunction)(void* argument);
    5557
     58class ThreadGroup;
    5659class ThreadHolder;
    5760class PrintStream;
     
    5962class Thread : public ThreadSafeRefCounted<Thread> {
    6063public:
     64    friend class ThreadGroup;
    6165    friend class ThreadHolder;
    6266
     
    176180    bool hasExited() { return m_didExit; }
    177181
     182    // These functions are only called from ThreadGroup.
     183    bool addToThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup&);
     184    void removeFromThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup&);
     185
    178186    // WordLock & Lock rely on ThreadSpecific. But Thread object can be destroyed even after ThreadSpecific things are destroyed.
    179187    std::mutex m_mutex;
     
    181189    JoinableState m_joinableState { Joinable };
    182190    StackBounds m_stack { StackBounds::emptyBounds() };
     191    Vector<std::weak_ptr<ThreadGroup>> m_threadGroups;
     192    bool m_isShuttingDown { false };
    183193    bool m_didExit { false };
    184194#if USE(PTHREADS)
  • trunk/Source/WTF/wtf/ThreadingPthreads.cpp

    r219647 r219653  
    4141#include <wtf/StdLibExtras.h>
    4242#include <wtf/ThreadFunctionInvocation.h>
     43#include <wtf/ThreadGroup.h>
    4344#include <wtf/ThreadHolder.h>
    4445#include <wtf/ThreadingPrimitives.h>
     
    7475namespace WTF {
    7576
     77static StaticLock globalSuspendLock;
     78
    7679Thread::Thread()
    7780{
     
    9396static constexpr const int SigThreadSuspendResume = SIGUSR1;
    9497static std::atomic<Thread*> targetThread { nullptr };
    95 static StaticWordLock globalSuspendLock;
    9698
    9799#if COMPILER(GCC)
     
    330332{
    331333    RELEASE_ASSERT_WITH_MESSAGE(id() != currentThread(), "We do not support suspending the current thread itself.");
    332     std::lock_guard<std::mutex> locker(m_mutex);
     334    // During suspend, suspend or resume should not be executed from the other threads.
     335    // We use global lock instead of per thread lock.
     336    // Consider the following case, there are threads A and B.
     337    // And A attempt to suspend B and B attempt to suspend A.
     338    // A and B send signals. And later, signals are delivered to A and B.
     339    // In that case, both will be suspended.
     340    //
     341    // And it is important to use a global lock to suspend and resume. Let's consider using per-thread lock.
     342    // Your issuing thread (A) attempts to suspend the target thread (B). Then, you will suspend the thread (C) additionally.
     343    // This case frequently happens if you stop threads to perform stack scanning. But thread (B) may hold the lock of thread (C).
     344    // In that case, dead lock happens. Using global lock here avoids this dead lock.
     345    LockHolder locker(globalSuspendLock);
    333346#if OS(DARWIN)
    334347    kern_return_t result = thread_suspend(m_platformThread);
     
    337350    return { };
    338351#else
    339     {
    340         // During suspend, suspend or resume should not be executed from the other threads.
    341         // We use global lock instead of per thread lock.
    342         // Consider the following case, there are threads A and B.
    343         // And A attempt to suspend B and B attempt to suspend A.
    344         // A and B send signals. And later, signals are delivered to A and B.
    345         // In that case, both will be suspended.
    346         WordLockHolder locker(globalSuspendLock);
    347         if (!m_suspendCount) {
    348             // Ideally, we would like to use pthread_sigqueue. It allows us to pass the argument to the signal handler.
    349             // But it can be used in a few platforms, like Linux.
    350             // Instead, we use Thread* stored in the thread local storage to pass it to the signal handler.
    351             targetThread.store(this);
    352             int result = pthread_kill(m_handle, SigThreadSuspendResume);
    353             if (result)
    354                 return makeUnexpected(result);
    355             sem_wait(&m_semaphoreForSuspendResume);
    356             // Release barrier ensures that this operation is always executed after all the above processing is done.
    357             m_suspended.store(true, std::memory_order_release);
    358         }
    359         ++m_suspendCount;
    360         return { };
    361     }
     352    if (!m_suspendCount) {
     353        // Ideally, we would like to use pthread_sigqueue. It allows us to pass the argument to the signal handler.
     354        // But it can be used in a few platforms, like Linux.
     355        // Instead, we use Thread* stored in the thread local storage to pass it to the signal handler.
     356        targetThread.store(this);
     357        int result = pthread_kill(m_handle, SigThreadSuspendResume);
     358        if (result)
     359            return makeUnexpected(result);
     360        sem_wait(&m_semaphoreForSuspendResume);
     361        // Release barrier ensures that this operation is always executed after all the above processing is done.
     362        m_suspended.store(true, std::memory_order_release);
     363    }
     364    ++m_suspendCount;
     365    return { };
    362366#endif
    363367}
     
    365369void Thread::resume()
    366370{
    367     std::lock_guard<std::mutex> locker(m_mutex);
     371    // During resume, suspend or resume should not be executed from the other threads.
     372    LockHolder locker(globalSuspendLock);
    368373#if OS(DARWIN)
    369374    thread_resume(m_platformThread);
    370375#else
    371     {
    372         // During resume, suspend or resume should not be executed from the other threads.
    373         WordLockHolder locker(globalSuspendLock);
    374         if (m_suspendCount == 1) {
    375             // When allowing SigThreadSuspendResume interrupt in the signal handler by sigsuspend and SigThreadSuspendResume is actually issued,
    376             // the signal handler itself will be called once again.
    377             // There are several ways to distinguish the handler invocation for suspend and resume.
    378             // 1. Use different signal numbers. And check the signal number in the handler.
    379             // 2. Use some arguments to distinguish suspend and resume in the handler. If pthread_sigqueue can be used, we can take this.
    380             // 3. Use thread local storage with atomic variables in the signal handler.
    381             // In this implementaiton, we take (3). suspended flag is used to distinguish it.
    382             targetThread.store(this);
    383             if (pthread_kill(m_handle, SigThreadSuspendResume) == ESRCH)
    384                 return;
    385             sem_wait(&m_semaphoreForSuspendResume);
    386             // Release barrier ensures that this operation is always executed after all the above processing is done.
    387             m_suspended.store(false, std::memory_order_release);
    388         }
    389         --m_suspendCount;
    390     }
     376    if (m_suspendCount == 1) {
     377        // When allowing SigThreadSuspendResume interrupt in the signal handler by sigsuspend and SigThreadSuspendResume is actually issued,
     378        // the signal handler itself will be called once again.
     379        // There are several ways to distinguish the handler invocation for suspend and resume.
     380        // 1. Use different signal numbers. And check the signal number in the handler.
     381        // 2. Use some arguments to distinguish suspend and resume in the handler. If pthread_sigqueue can be used, we can take this.
     382        // 3. Use thread local storage with atomic variables in the signal handler.
     383        // In this implementaiton, we take (3). suspended flag is used to distinguish it.
     384        targetThread.store(this);
     385        if (pthread_kill(m_handle, SigThreadSuspendResume) == ESRCH)
     386            return;
     387        sem_wait(&m_semaphoreForSuspendResume);
     388        // Release barrier ensures that this operation is always executed after all the above processing is done.
     389        m_suspended.store(false, std::memory_order_release);
     390    }
     391    --m_suspendCount;
    391392#endif
    392393}
     
    427428size_t Thread::getRegisters(PlatformRegisters& registers)
    428429{
    429     std::lock_guard<std::mutex> locker(m_mutex);
     430    LockHolder locker(globalSuspendLock);
    430431#if OS(DARWIN)
    431432    auto metadata = threadStateMetadata();
  • trunk/Source/WTF/wtf/ThreadingWin.cpp

    r219647 r219653  
    9292#include <windows.h>
    9393#include <wtf/CurrentTime.h>
     94#include <wtf/Lock.h>
    9495#include <wtf/MainThread.h>
    9596#include <wtf/MathExtras.h>
     
    104105
    105106namespace WTF {
     107
     108static StaticLock globalSuspendLock;
    106109
    107110Thread::Thread()
     
    244247{
    245248    RELEASE_ASSERT_WITH_MESSAGE(id() != currentThread(), "We do not support suspending the current thread itself.");
    246     std::lock_guard<std::mutex> locker(m_mutex);
     249    LockHolder locker(globalSuspendLock);
    247250    DWORD result = SuspendThread(m_handle);
    248251    if (result != (DWORD)-1)
     
    254257void Thread::resume()
    255258{
    256     std::lock_guard<std::mutex> locker(m_mutex);
     259    LockHolder locker(globalSuspendLock);
    257260    ResumeThread(m_handle);
    258261}
     
    260263size_t Thread::getRegisters(PlatformRegisters& registers)
    261264{
    262     std::lock_guard<std::mutex> locker(m_mutex);
     265    LockHolder locker(globalSuspendLock);
    263266    registers.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
    264267    GetThreadContext(m_handle, &registers);
  • trunk/Source/WebCore/ChangeLog

    r219648 r219653  
     12017-07-19  Yusuke Suzuki  <utatane.tea@gmail.com>
     2
     3        [WTF] Implement WTF::ThreadGroup
     4        https://bugs.webkit.org/show_bug.cgi?id=174081
     5
     6        Reviewed by Mark Lam.
     7
     8        * page/ResourceUsageThread.h:
     9
    1102017-07-18  Andy Estes  <aestes@apple.com>
    211
  • trunk/Source/WebCore/page/ResourceUsageThread.h

    r219260 r219653  
    3636#include <wtf/NeverDestroyed.h>
    3737#include <wtf/Noncopyable.h>
     38#include <wtf/Threading.h>
    3839
    3940namespace JSC {
  • trunk/Source/WebKit/ChangeLog

    r219652 r219653  
     12017-07-19  Yusuke Suzuki  <utatane.tea@gmail.com>
     2
     3        [WTF] Implement WTF::ThreadGroup
     4        https://bugs.webkit.org/show_bug.cgi?id=174081
     5
     6        Reviewed by Mark Lam.
     7
     8        * Shared/AsyncRequest.h:
     9
    1102017-07-18  Carlos Garcia Campos  <cgarcia@igalia.com>
    211
  • trunk/Source/WebKit/Shared/AsyncRequest.h

    r219260 r219653  
    2828#define AsyncRequest_h
    2929
     30#include <wtf/Function.h>
    3031#include <wtf/HashMap.h>
    3132#include <wtf/RefCounted.h>
  • trunk/Tools/ChangeLog

    r219648 r219653  
     12017-07-19  Yusuke Suzuki  <utatane.tea@gmail.com>
     2
     3        [WTF] Implement WTF::ThreadGroup
     4        https://bugs.webkit.org/show_bug.cgi?id=174081
     5
     6        Reviewed by Mark Lam.
     7
     8        Add WTF::ThreadGroup tests.
     9
     10        * TestWebKitAPI/CMakeLists.txt:
     11        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
     12        * TestWebKitAPI/Tests/WTF/ThreadGroup.cpp: Added.
     13        (TestWebKitAPI::testThreadGroup):
     14        (TestWebKitAPI::TEST):
     15
    1162017-07-18  Andy Estes  <aestes@apple.com>
    217
  • trunk/Tools/TestWebKitAPI/CMakeLists.txt

    r219493 r219653  
    8282    ${TESTWEBKITAPI_DIR}/Tests/WTF/StringView.cpp
    8383    ${TESTWEBKITAPI_DIR}/Tests/WTF/TextBreakIterator.cpp
     84    ${TESTWEBKITAPI_DIR}/Tests/WTF/ThreadGroup.cpp
    8485    ${TESTWEBKITAPI_DIR}/Tests/WTF/Time.cpp
    8586    ${TESTWEBKITAPI_DIR}/Tests/WTF/UniqueRef.cpp
  • trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj

    r219624 r219653  
    633633                E1220DCA155B28AA0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E1220DC9155B287D0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html */; };
    634634                E194E1BD177E53C7009C4D4E /* StopLoadingFromDidReceiveResponse.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */; };
     635                E3DEA8111F0A589000CBC2E8 /* ThreadGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */; };
    635636                ECA680CE1E68CC0900731D20 /* StringUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = ECA680CD1E68CC0900731D20 /* StringUtilities.mm */; };
    636637                F407FE391F1D0DFC0017CF25 /* enormous.svg in Copy Resources */ = {isa = PBXBuildFile; fileRef = F407FE381F1D0DE60017CF25 /* enormous.svg */; };
     
    16171618                E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = StopLoadingFromDidReceiveResponse.html; sourceTree = "<group>"; };
    16181619                E19DB9781B32137C00DB38D4 /* NavigatorLanguage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NavigatorLanguage.mm; sourceTree = "<group>"; };
     1620                E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadGroup.cpp; sourceTree = "<group>"; };
    16191621                E40019301ACE9B5C001B0A2A /* BloomFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BloomFilter.cpp; sourceTree = "<group>"; };
    16201622                E490296714E2E3A4002BEDD1 /* TypingStyleCrash.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TypingStyleCrash.mm; sourceTree = "<group>"; };
     
    24052407                                5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */,
    24062408                                9329AA281DE3F81E003ABD07 /* TextBreakIterator.cpp */,
     2409                                E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */,
    24072410                                5311BD5D1EA9490D00525281 /* ThreadMessages.cpp */,
    24082411                                0F2C20B71DCD544800542D9E /* Time.cpp */,
     
    29052908                        buildActionMask = 2147483647;
    29062909                        files = (
     2910                                E3DEA8111F0A589000CBC2E8 /* ThreadGroup.cpp in Sources */,
    29072911                                7C83DE991D0A590C00FEBCF3 /* AtomicString.cpp in Sources */,
    29082912                                1ADAD1501D77A9F600212586 /* BlockPtr.mm in Sources */,
Note: See TracChangeset for help on using the changeset viewer.