Diff
Modified: trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp (219652 => 219653)
--- trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/API/tests/ExecutionTimeLimitTest.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -36,6 +36,7 @@
#include <wtf/Condition.h>
#include <wtf/CurrentTime.h>
#include <wtf/Lock.h>
+#include <wtf/Threading.h>
#include <wtf/text/StringBuilder.h>
#if HAVE(MACH_EXCEPTIONS)
Modified: trunk/Source/_javascript_Core/ChangeLog (219652 => 219653)
--- trunk/Source/_javascript_Core/ChangeLog 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,57 @@
+2017-07-19 Yusuke Suzuki <utatane....@gmail.com>
+
+ [WTF] Implement WTF::ThreadGroup
+ https://bugs.webkit.org/show_bug.cgi?id=174081
+
+ Reviewed by Mark Lam.
+
+ Large part of MachineThreads are now removed and replaced with WTF::ThreadGroup.
+ And SamplingProfiler and others interact with WTF::Thread directly.
+
+ * API/tests/ExecutionTimeLimitTest.cpp:
+ * heap/MachineStackMarker.cpp:
+ (JSC::MachineThreads::MachineThreads):
+ (JSC::captureStack):
+ (JSC::MachineThreads::tryCopyOtherThreadStack):
+ (JSC::MachineThreads::tryCopyOtherThreadStacks):
+ (JSC::MachineThreads::gatherConservativeRoots):
+ (JSC::ActiveMachineThreadsManager::Locker::Locker): Deleted.
+ (JSC::ActiveMachineThreadsManager::add): Deleted.
+ (JSC::ActiveMachineThreadsManager::remove): Deleted.
+ (JSC::ActiveMachineThreadsManager::contains): Deleted.
+ (JSC::ActiveMachineThreadsManager::ActiveMachineThreadsManager): Deleted.
+ (JSC::activeMachineThreadsManager): Deleted.
+ (JSC::MachineThreads::~MachineThreads): Deleted.
+ (JSC::MachineThreads::addCurrentThread): Deleted.
+ (): Deleted.
+ (JSC::MachineThreads::removeThread): Deleted.
+ (JSC::MachineThreads::removeThreadIfFound): Deleted.
+ (JSC::MachineThreads::MachineThread::MachineThread): Deleted.
+ (JSC::MachineThreads::MachineThread::getRegisters): Deleted.
+ (JSC::MachineThreads::MachineThread::Registers::stackPointer): Deleted.
+ (JSC::MachineThreads::MachineThread::Registers::framePointer): Deleted.
+ (JSC::MachineThreads::MachineThread::Registers::instructionPointer): Deleted.
+ (JSC::MachineThreads::MachineThread::Registers::llintPC): Deleted.
+ (JSC::MachineThreads::MachineThread::captureStack): Deleted.
+ * heap/MachineStackMarker.h:
+ (JSC::MachineThreads::addCurrentThread):
+ (JSC::MachineThreads::getLock):
+ (JSC::MachineThreads::threads):
+ (JSC::MachineThreads::MachineThread::suspend): Deleted.
+ (JSC::MachineThreads::MachineThread::resume): Deleted.
+ (JSC::MachineThreads::MachineThread::threadID): Deleted.
+ (JSC::MachineThreads::MachineThread::stackBase): Deleted.
+ (JSC::MachineThreads::MachineThread::stackEnd): Deleted.
+ (JSC::MachineThreads::threadsListHead): Deleted.
+ * runtime/SamplingProfiler.cpp:
+ (JSC::FrameWalker::isValidFramePointer):
+ (JSC::SamplingProfiler::SamplingProfiler):
+ (JSC::SamplingProfiler::takeSample):
+ (JSC::SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread):
+ * runtime/SamplingProfiler.h:
+ * wasm/WasmMachineThreads.cpp:
+ (JSC::Wasm::resetInstructionCacheOnAllThreads):
+
2017-07-18 Andy Estes <aes...@apple.com>
[Xcode] Enable CLANG_WARN_RANGE_LOOP_ANALYSIS
Modified: trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp (219652 => 219653)
--- trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -23,16 +23,11 @@
#include "MachineStackMarker.h"
#include "ConservativeRoots.h"
-#include "GPRInfo.h"
-#include "Heap.h"
-#include "JSArray.h"
-#include "JSCInlines.h"
-#include "LLIntPCRanges.h"
-#include "MacroAssembler.h"
-#include "VM.h"
+#include "MachineContext.h"
#include <setjmp.h>
#include <stdlib.h>
-#include <wtf/MainThread.h>
+#include <wtf/BitVector.h>
+#include <wtf/PageBlock.h>
#include <wtf/StdLibExtras.h>
using namespace WTF;
@@ -39,190 +34,11 @@
namespace JSC {
-class ActiveMachineThreadsManager;
-static ActiveMachineThreadsManager& activeMachineThreadsManager();
-
-class ActiveMachineThreadsManager {
- WTF_MAKE_NONCOPYABLE(ActiveMachineThreadsManager);
-public:
-
- class Locker {
- public:
- Locker(ActiveMachineThreadsManager& manager)
- : m_locker(manager.m_lock)
- {
- }
-
- private:
- LockHolder m_locker;
- };
-
- void add(MachineThreads* machineThreads)
- {
- LockHolder managerLock(m_lock);
- m_set.add(machineThreads);
- }
-
- void THREAD_SPECIFIC_CALL remove(MachineThreads* machineThreads)
- {
- LockHolder managerLock(m_lock);
- auto recordedMachineThreads = m_set.take(machineThreads);
- RELEASE_ASSERT(recordedMachineThreads == machineThreads);
- }
-
- bool contains(MachineThreads* machineThreads)
- {
- return m_set.contains(machineThreads);
- }
-
-private:
- typedef HashSet<MachineThreads*> MachineThreadsSet;
-
- ActiveMachineThreadsManager() { }
-
- Lock m_lock;
- MachineThreadsSet m_set;
-
- friend ActiveMachineThreadsManager& activeMachineThreadsManager();
-};
-
-static ActiveMachineThreadsManager& activeMachineThreadsManager()
-{
- static std::once_flag initializeManagerOnceFlag;
- static ActiveMachineThreadsManager* manager = nullptr;
-
- std::call_once(initializeManagerOnceFlag, [] {
- manager = new ActiveMachineThreadsManager();
- });
- return *manager;
-}
-
-#if CPU(X86_64) && OS(DARWIN)
-#define FILL_CALLEE_SAVES_FOR_CRASH_INFO(number) \
- asm volatile( \
- "movq $0xc0defefe000000" number ", %%rbx;" \
- "movq $0xc0defefe000000" number ", %%r12;" \
- "movq $0xc0defefe000000" number ", %%r13;" \
- "movq $0xc0defefe000000" number ", %%r14;" \
- "movq $0xc0defefe000000" number ", %%r15;" \
- : \
- : \
- : "%rbx", "%r12", "%r13", "%r14", "%r15" \
- );
-
-#define FILL_CALLER_SAVES_FOR_CRASH_INFO(number) \
- asm volatile( \
- "movq $0xc0defefe000000" number ", %%rax;" \
- "movq $0xc0defefe000000" number ", %%rdi;" \
- "movq $0xc0defefe000000" number ", %%rsi;" \
- "movq $0xc0defefe000000" number ", %%rdx;" \
- "movq $0xc0defefe000000" number ", %%rcx;" \
- "movq $0xc0defefe000000" number ", %%r8;" \
- "movq $0xc0defefe000000" number ", %%r9;" \
- "movq $0xc0defefe000000" number ", %%r10;" \
- "movq $0xc0defefe000000" number ", %%r11;" \
- : \
- : \
- : "%rax", "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9", "%r10", "%r11" \
- );
-#else
-#define FILL_CALLEE_SAVES_FOR_CRASH_INFO(number)
-#define FILL_CALLER_SAVES_FOR_CRASH_INFO(number)
-#endif
-
MachineThreads::MachineThreads()
- : m_registeredThreads()
- , m_threadSpecificForMachineThreads(0)
+ : m_threadGroup(ThreadGroup::create())
{
- FILL_CALLEE_SAVES_FOR_CRASH_INFO("01");
- threadSpecificKeyCreate(&m_threadSpecificForMachineThreads, removeThread);
- FILL_CALLEE_SAVES_FOR_CRASH_INFO("02");
- activeMachineThreadsManager().add(this);
- FILL_CALLER_SAVES_FOR_CRASH_INFO("03");
}
-MachineThreads::~MachineThreads()
-{
- activeMachineThreadsManager().remove(this);
- threadSpecificKeyDelete(m_threadSpecificForMachineThreads);
-
- LockHolder registeredThreadsLock(m_registeredThreadsMutex);
- for (MachineThread* current = m_registeredThreads.head(); current;) {
- MachineThread* next = current->next();
- delete current;
- current = next;
- }
-}
-
-void MachineThreads::addCurrentThread()
-{
- if (threadSpecificGet(m_threadSpecificForMachineThreads)) {
-#ifndef NDEBUG
- LockHolder lock(m_registeredThreadsMutex);
- ASSERT(threadSpecificGet(m_threadSpecificForMachineThreads) == this);
-#endif
- return;
- }
-
- MachineThread* thread = new MachineThread();
- threadSpecificSet(m_threadSpecificForMachineThreads, this);
-
- LockHolder lock(m_registeredThreadsMutex);
-
- m_registeredThreads.append(thread);
-}
-
-auto MachineThreads::machineThreadForCurrentThread() -> MachineThread*
-{
- LockHolder lock(m_registeredThreadsMutex);
- ThreadIdentifier id = currentThread();
- for (MachineThread* thread = m_registeredThreads.head(); thread; thread = thread->next()) {
- if (thread->threadID() == id)
- return thread;
- }
-
- RELEASE_ASSERT_NOT_REACHED();
- return nullptr;
-}
-
-void THREAD_SPECIFIC_CALL MachineThreads::removeThread(void* p)
-{
- auto& manager = activeMachineThreadsManager();
- ActiveMachineThreadsManager::Locker lock(manager);
- auto machineThreads = static_cast<MachineThreads*>(p);
- if (manager.contains(machineThreads)) {
- // There's a chance that the MachineThreads registry that this thread
- // was registered with was already destructed, and another one happened
- // to be instantiated at the same address. Hence, this thread may or
- // may not be found in this MachineThreads registry. We only need to
- // do a removal if this thread is found in it.
-
-#if OS(WINDOWS)
- // On Windows the thread specific destructor is also called when the
- // main thread is exiting. This may lead to the main thread waiting
- // forever for the machine thread lock when exiting, if the sampling
- // profiler thread was terminated by the system while holding the
- // machine thread lock.
- if (WTF::isMainThread())
- return;
-#endif
-
- machineThreads->removeThreadIfFound(currentThread());
- }
-}
-
-void MachineThreads::removeThreadIfFound(ThreadIdentifier id)
-{
- LockHolder lock(m_registeredThreadsMutex);
- for (MachineThread* current = m_registeredThreads.head(); current; current = current->next()) {
- if (current->threadID() == id) {
- m_registeredThreads.remove(current);
- delete current;
- break;
- }
- }
-}
-
SUPPRESS_ASAN
void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, CurrentThreadState& currentThreadState)
{
@@ -235,52 +51,6 @@
conservativeRoots.add(currentThreadState.stackTop, currentThreadState.stackOrigin, jitStubRoutines, codeBlocks);
}
-MachineThreads::MachineThread::MachineThread()
- : m_thread(WTF::Thread::current())
-{
-}
-
-size_t MachineThreads::MachineThread::getRegisters(MachineThread::Registers& registers)
-{
- WTF::PlatformRegisters& regs = registers.regs;
- return m_thread->getRegisters(regs);
-}
-
-void* MachineThreads::MachineThread::Registers::stackPointer() const
-{
- return MachineContext::stackPointer(regs);
-}
-
-#if ENABLE(SAMPLING_PROFILER)
-void* MachineThreads::MachineThread::Registers::framePointer() const
-{
-#if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)
- return MachineContext::framePointer(regs);
-#else
-#error Need a way to get the frame pointer for another thread on this platform
-#endif
-}
-
-void* MachineThreads::MachineThread::Registers::instructionPointer() const
-{
-#if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)
- return MachineContext::instructionPointer(regs);
-#else
-#error Need a way to get the instruction pointer for another thread on this platform
-#endif
-}
-
-void* MachineThreads::MachineThread::Registers::llintPC() const
-{
- // LLInt uses regT4 as PC.
-#if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)
- return MachineContext::llintInstructionPointer(regs);
-#else
-#error Need a way to get the LLIntPC for another thread on this platform
-#endif
-}
-#endif // ENABLE(SAMPLING_PROFILER)
-
static inline int osRedZoneAdjustment()
{
int redZoneAdjustment = 0;
@@ -296,9 +66,9 @@
return redZoneAdjustment;
}
-std::pair<void*, size_t> MachineThreads::MachineThread::captureStack(void* stackTop)
+static std::pair<void*, size_t> captureStack(Thread& thread, void* stackTop)
{
- char* begin = reinterpret_cast_ptr<char*>(stackBase());
+ char* begin = reinterpret_cast_ptr<char*>(thread.stack().origin());
char* end = bitwise_cast<char*>(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackTop)));
ASSERT(begin >= end);
@@ -305,8 +75,8 @@
char* endWithRedZone = end + osRedZoneAdjustment();
ASSERT(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(endWithRedZone)) == reinterpret_cast<uintptr_t>(endWithRedZone));
- if (endWithRedZone < stackEnd())
- endWithRedZone = reinterpret_cast_ptr<char*>(stackEnd());
+ if (endWithRedZone < thread.stack().end())
+ endWithRedZone = reinterpret_cast_ptr<char*>(thread.stack().end());
std::swap(begin, endWithRedZone);
return std::make_pair(begin, endWithRedZone - begin);
@@ -339,20 +109,20 @@
// significant performance loss as tryCopyOtherThreadStack is only called as part of an O(heapsize)
// operation. As the heap is generally much larger than the stack the performance hit is minimal.
// See: https://bugs.webkit.org/show_bug.cgi?id=146297
-void MachineThreads::tryCopyOtherThreadStack(MachineThread* thread, void* buffer, size_t capacity, size_t* size)
+void MachineThreads::tryCopyOtherThreadStack(Thread& thread, void* buffer, size_t capacity, size_t* size)
{
- MachineThread::Registers registers;
- size_t registersSize = thread->getRegisters(registers);
+ PlatformRegisters registers;
+ size_t registersSize = thread.getRegisters(registers);
// This is a workaround for <rdar://problem/27607384>. libdispatch recycles work
// queue threads without running pthread exit destructors. This can cause us to scan a
// thread during work queue initialization, when the stack pointer is null.
- if (UNLIKELY(!registers.stackPointer())) {
+ if (UNLIKELY(!MachineContext::stackPointer(registers))) {
*size = 0;
return;
}
- std::pair<void*, size_t> stack = thread->captureStack(registers.stackPointer());
+ std::pair<void*, size_t> stack = captureStack(thread, MachineContext::stackPointer(registers));
bool canCopy = *size + registersSize + stack.second <= capacity;
@@ -365,7 +135,7 @@
*size += stack.second;
}
-bool MachineThreads::tryCopyOtherThreadStacks(const AbstractLocker&, void* buffer, size_t capacity, size_t* size)
+bool MachineThreads::tryCopyOtherThreadStacks(const AbstractLocker& locker, void* buffer, size_t capacity, size_t* size)
{
// Prevent two VMs from suspending each other's threads at the same time,
// which can cause deadlock: <rdar://problem/20300842>.
@@ -374,60 +144,49 @@
*size = 0;
- ThreadIdentifier id = currentThread();
- int numberOfThreads = 0; // Using 0 to denote that we haven't counted the number of threads yet.
- int index = 1;
- DoublyLinkedList<MachineThread> threadsToBeDeleted;
+ Thread& currentThread = Thread::current();
+ const auto& threads = m_threadGroup->threads(locker);
+ BitVector isSuspended(threads.size());
- for (MachineThread* thread = m_registeredThreads.head(); thread; index++) {
- if (thread->threadID() != id) {
- auto result = thread->suspend();
+ {
+ unsigned index = 0;
+ for (auto& thread : threads) {
+ if (thread.get() != currentThread) {
+ auto result = thread->suspend();
+ if (result)
+ isSuspended.set(index);
+ else {
#if OS(DARWIN)
- if (!result) {
- if (!numberOfThreads)
- numberOfThreads = m_registeredThreads.size();
-
- ASSERT(result.error() != KERN_SUCCESS);
-
- WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
- "_javascript_ garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] id %u.",
- result.error(), index, numberOfThreads, thread, thread->threadID());
-
- // Put the invalid thread on the threadsToBeDeleted list.
- // We can't just delete it here because we have suspended other
- // threads, and they may still be holding the C heap lock which
- // we need for deleting the invalid thread. Hence, we need to
- // defer the deletion till after we have resumed all threads.
- MachineThread* nextThread = thread->next();
- m_registeredThreads.remove(thread);
- threadsToBeDeleted.append(thread);
- thread = nextThread;
- continue;
+ // These threads will be removed from the ThreadGroup. Thus, we do not do anything here except for reporting.
+ ASSERT(result.error() != KERN_SUCCESS);
+ WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
+ "_javascript_ garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] id %u.",
+ result.error(), index, threads.size(), thread.ptr(), thread->id());
+#endif
+ }
}
-#else
- UNUSED_PARAM(numberOfThreads);
- ASSERT_UNUSED(result, result);
-#endif
+ ++index;
}
- thread = thread->next();
}
- for (MachineThread* thread = m_registeredThreads.head(); thread; thread = thread->next()) {
- if (thread->threadID() != id)
- tryCopyOtherThreadStack(thread, buffer, capacity, size);
+ {
+ unsigned index = 0;
+ for (auto& thread : threads) {
+ if (isSuspended.get(index))
+ tryCopyOtherThreadStack(thread.get(), buffer, capacity, size);
+ ++index;
+ }
}
- for (MachineThread* thread = m_registeredThreads.head(); thread; thread = thread->next()) {
- if (thread->threadID() != id)
- thread->resume();
+ {
+ unsigned index = 0;
+ for (auto& thread : threads) {
+ if (isSuspended.get(index))
+ thread->resume();
+ ++index;
+ }
}
- for (MachineThread* thread = threadsToBeDeleted.head(); thread; ) {
- MachineThread* nextThread = thread->next();
- delete thread;
- thread = nextThread;
- }
-
return *size <= capacity;
}
@@ -448,8 +207,8 @@
size_t size;
size_t capacity = 0;
void* buffer = nullptr;
- LockHolder lock(m_registeredThreadsMutex);
- while (!tryCopyOtherThreadStacks(lock, buffer, capacity, &size))
+ auto locker = holdLock(m_threadGroup->getLock());
+ while (!tryCopyOtherThreadStacks(locker, buffer, capacity, &size))
growBuffer(size, &buffer, &capacity);
if (!buffer)
Modified: trunk/Source/_javascript_Core/heap/MachineStackMarker.h (219652 => 219653)
--- trunk/Source/_javascript_Core/heap/MachineStackMarker.h 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/heap/MachineStackMarker.h 2017-07-19 08:43:57 UTC (rev 219653)
@@ -21,12 +21,10 @@
#pragma once
-#include "MachineContext.h"
#include "RegisterState.h"
-#include <wtf/DoublyLinkedList.h>
#include <wtf/Lock.h>
#include <wtf/ScopedLambda.h>
-#include <wtf/ThreadSpecific.h>
+#include <wtf/ThreadGroup.h>
namespace JSC {
@@ -45,58 +43,22 @@
WTF_MAKE_NONCOPYABLE(MachineThreads);
public:
MachineThreads();
- ~MachineThreads();
void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, CurrentThreadState*);
- JS_EXPORT_PRIVATE void addCurrentThread(); // Only needs to be called by clients that can use the same heap from multiple threads.
+ // Only needs to be called by clients that can use the same heap from multiple threads.
+ void addCurrentThread() { m_threadGroup->addCurrentThread(); }
- class MachineThread : public DoublyLinkedListNode<MachineThread> {
- WTF_MAKE_FAST_ALLOCATED;
- public:
- MachineThread();
+ std::mutex& getLock() { return m_threadGroup->getLock(); }
+ const ListHashSet<Ref<Thread>>& threads(const AbstractLocker& locker) const { return m_threadGroup->threads(locker); }
- struct Registers {
- void* stackPointer() const;
-#if ENABLE(SAMPLING_PROFILER)
- void* framePointer() const;
- void* instructionPointer() const;
- void* llintPC() const;
-#endif // ENABLE(SAMPLING_PROFILER)
- PlatformRegisters regs;
- };
-
- Expected<void, Thread::PlatformSuspendError> suspend() { return m_thread->suspend(); }
- void resume() { m_thread->resume(); }
- size_t getRegisters(Registers& regs);
- std::pair<void*, size_t> captureStack(void* stackTop);
-
- WTF::ThreadIdentifier threadID() const { return m_thread->id(); }
- void* stackBase() const { return m_thread->stack().origin(); }
- void* stackEnd() const { return m_thread->stack().end(); }
-
- Ref<WTF::Thread> m_thread;
- MachineThread* m_next { nullptr };
- MachineThread* m_prev { nullptr };
- };
-
- Lock& getLock() { return m_registeredThreadsMutex; }
- const DoublyLinkedList<MachineThread>& threadsListHead(const AbstractLocker&) const { ASSERT(m_registeredThreadsMutex.isLocked()); return m_registeredThreads; }
- MachineThread* machineThreadForCurrentThread();
-
private:
void gatherFromCurrentThread(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, CurrentThreadState&);
- void tryCopyOtherThreadStack(MachineThread*, void*, size_t capacity, size_t*);
+ void tryCopyOtherThreadStack(Thread&, void*, size_t capacity, size_t*);
bool tryCopyOtherThreadStacks(const AbstractLocker&, void*, size_t capacity, size_t*);
- static void THREAD_SPECIFIC_CALL removeThread(void*);
-
- void removeThreadIfFound(ThreadIdentifier);
-
- Lock m_registeredThreadsMutex;
- DoublyLinkedList<MachineThread> m_registeredThreads;
- WTF::ThreadSpecificKey m_threadSpecificForMachineThreads;
+ std::shared_ptr<ThreadGroup> m_threadGroup;
};
#define DECLARE_AND_COMPUTE_CURRENT_THREAD_STATE(stateName) \
Modified: trunk/Source/_javascript_Core/runtime/SamplingProfiler.cpp (219652 => 219653)
--- trunk/Source/_javascript_Core/runtime/SamplingProfiler.cpp 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/runtime/SamplingProfiler.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -39,6 +39,7 @@
#include "JSCInlines.h"
#include "JSFunction.h"
#include "LLIntPCRanges.h"
+#include "MachineContext.h"
#include "MarkedBlock.h"
#include "MarkedBlockSet.h"
#include "MarkedSpaceInlines.h"
@@ -165,10 +166,9 @@
bool isValidFramePointer(void* exec)
{
uint8_t* fpCast = bitwise_cast<uint8_t*>(exec);
- const auto& threadList = m_vm.heap.machineThreads().threadsListHead(m_machineThreadsLocker);
- for (MachineThreads::MachineThread* thread = threadList.head(); thread; thread = thread->next()) {
- uint8_t* stackBase = static_cast<uint8_t*>(thread->stackBase());
- uint8_t* stackLimit = static_cast<uint8_t*>(thread->stackEnd());
+ for (auto& thread : m_vm.heap.machineThreads().threads(m_machineThreadsLocker)) {
+ uint8_t* stackBase = static_cast<uint8_t*>(thread->stack().origin());
+ uint8_t* stackLimit = static_cast<uint8_t*>(thread->stack().end());
RELEASE_ASSERT(stackBase);
RELEASE_ASSERT(stackLimit);
if (fpCast <= stackBase && fpCast >= stackLimit)
@@ -278,7 +278,6 @@
, m_weakRandom()
, m_stopwatch(WTFMove(stopwatch))
, m_timingInterval(std::chrono::microseconds(Options::sampleInterval()))
- , m_jscExecutionThread(nullptr)
, m_isPaused(false)
, m_isShutDown(false)
{
@@ -338,7 +337,7 @@
if (m_vm.entryScope) {
double nowTime = m_stopwatch->elapsedTime();
- LockHolder machineThreadsLocker(m_vm.heap.machineThreads().getLock());
+ auto machineThreadsLocker = holdLock(m_vm.heap.machineThreads().getLock());
LockHolder codeBlockSetLocker(m_vm.heap.codeBlockSet().getLock());
LockHolder executableAllocatorLocker(ExecutableAllocator::singleton().getLock());
@@ -352,12 +351,12 @@
bool topFrameIsLLInt = false;
void* llintPC;
{
- MachineThreads::MachineThread::Registers registers;
+ PlatformRegisters registers;
m_jscExecutionThread->getRegisters(registers);
- machineFrame = registers.framePointer();
+ machineFrame = MachineContext::framePointer(registers);
callFrame = static_cast<ExecState*>(machineFrame);
- machinePC = registers.instructionPointer();
- llintPC = registers.llintPC();
+ machinePC = MachineContext::instructionPointer(registers);
+ llintPC = MachineContext::llintInstructionPointer(registers);
}
// FIXME: Lets have a way of detecting when we're parsing code.
// https://bugs.webkit.org/show_bug.cgi?id=152761
@@ -678,7 +677,7 @@
void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread(const AbstractLocker&)
{
ASSERT(m_lock.isLocked());
- m_jscExecutionThread = m_vm.heap.machineThreads().machineThreadForCurrentThread();
+ m_jscExecutionThread = &Thread::current();
}
void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread()
Modified: trunk/Source/_javascript_Core/runtime/SamplingProfiler.h (219652 => 219653)
--- trunk/Source/_javascript_Core/runtime/SamplingProfiler.h 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/runtime/SamplingProfiler.h 2017-07-19 08:43:57 UTC (rev 219653)
@@ -196,7 +196,7 @@
double m_lastTime;
Lock m_lock;
RefPtr<Thread> m_thread;
- MachineThreads::MachineThread* m_jscExecutionThread;
+ RefPtr<Thread> m_jscExecutionThread;
bool m_isPaused;
bool m_isShutDown;
bool m_needsReportAtExit { false };
Modified: trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp (219652 => 219653)
--- trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -55,10 +55,8 @@
void resetInstructionCacheOnAllThreads()
{
auto locker = holdLock(wasmThreads().getLock());
-
- const DoublyLinkedList<MachineThreads::MachineThread>& threads = wasmThreads().threadsListHead(locker);
- for (const auto* thread = threads.head(); thread; thread = thread->next()) {
- sendMessage(thread->m_thread.get(), [] (const PlatformRegisters&) {
+ for (auto& thread : wasmThreads().threads(locker)) {
+ sendMessage(thread.get(), [] (const PlatformRegisters&) {
// It's likely that the signal handler will already reset the instruction cache but we might as well be sure.
WTF::crossModifyingCodeFence();
});
Modified: trunk/Source/WTF/ChangeLog (219652 => 219653)
--- trunk/Source/WTF/ChangeLog 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/ChangeLog 2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,77 @@
+2017-07-19 Yusuke Suzuki <utatane....@gmail.com>
+
+ [WTF] Implement WTF::ThreadGroup
+ https://bugs.webkit.org/show_bug.cgi?id=174081
+
+ Reviewed by Mark Lam.
+
+ This patch implements WTF::ThreadGroup. It implements core of JSC::MachineThreads with more reliable way.
+ JSC::MachineThreads was complicated because of managing dead threads. Each JSC::MachineThreads has its
+ own TLS with a registered destructor. And everytime a thread dies, the registered TLS destructor is called.
+ And this destructor will remove the current dying thread from JSC::MachineThreads.
+
+ However the above implementation is tricky. And each JSC::MachineThreads requires own TLS space, which is
+ not considered in WTF's Windows ThreadSpecific implementation. Current design works well since we only
+ have small number of MachineThreads right now.
+
+ Instead, we use more reliable way. After introducing WTF::Thread, WTF::Thread has WTF::Thread::didExit,
+ which is called when associated TLS (with WTF::Thread) is destroyed. We leverage this mechanism to remove
+ WTF::Thread from MachineThreads.
+
+ This patch introduces WTF::ThreadGroup. It is tightly integrated with WTF::Thread: WTF::Thread knows
+ ThreadGroups which includes this thread. And WTF::ThreadGroup of course knows WTF::Threads added to it.
+ WTF::Thread::didExit carefully remove itself from WTF::ThreadGroups.
+
+ The most important part of this patch is locking. WTF::Thread can die. And WTF::ThreadGroup can die.
+ If we take a design using two fine grain locks in WTF::Thread and WTF::ThreadGroup, we easily encounter
+ dead lock. Consider the following case.
+
+ 1. When adding WTF::Thread (TH) to WTF::ThreadGroup (THG), we first hold a lock of THG, and hold a lock of TH (locking order is THG -> TH).
+ 2. When TH dies, TH need to hold a lock of TH to iterate THGs. And we hold a lock of THG to unregister TH from it (locking order is TH -> THG).
+ 3. When suspending and resuming THs in THG, we first hold a lock of THG. And then, we hold a lock of TH to suspend and resume it (locking order is THG -> TH).
+ 4. When destroying THG, we need to hold a lock of TH to unregister THG from TH. We can hold a lock of THG before that (locking order is THG -> TH).
+
+ Then, it easily causes dead lock. We cannot swap the locking order of (2) since iterating THG requires a lock of TH.
+ To solve this problem, we use std::shared_ptr and std::weak_ptr.
+
+ 1. When adding WTF::Thread (TH) to WTF::ThreadGroup (THG), we first hold THG, and hold a lock of TH. (THG -> TH)
+ 2. When TH dies, TH first hold lock of TH. And we use std::weak_ptr<>::lock() to retain non-destructed ThreadGroups.
+ If some of ThreadGroups are dying, we just ignore them. It is ok because such a ThreadGroup will be destructed. So we do not need to unregister this thread from
+ such a ThreadGroup. Then, we have Vector<std::shared_ptr<ThreadGroup>>. So we unlock a lock of TH. To unregister a thread from thread group, we first hold a
+ lock of THG and then hold a lock of TH. Both lifetime is ensured: THG is retained by std::shared_ptr. And TH is itself. (TH), (THG -> TH).
+ 3. When suspending and resuming THs in THG, we first hold a lock of THG. And then, we hold a lock of TH to suspend and resume it (THG -> TH).
+ 4. When destroying THG, we hold a lock of THG. And hold a lock of TH. During holding THG's lock, registered thread never dies because (2) holds THG lock. (THG -> TH).
+
+ We also fix suspend and resume locking mechanism to avoid dead lock. We should hold the global lock when suspending and resuming.
+ If we use per-thread lock, the suspended thread can hold the lock of the other threads. It causes dead lock.
+
+ * WTF.xcodeproj/project.pbxproj:
+ * wtf/AutomaticThread.cpp:
+ * wtf/CMakeLists.txt:
+ * wtf/CrossThreadCopier.h:
+ * wtf/ParkingLot.h:
+ * wtf/ThreadGroup.cpp: Copied from Source/_javascript_Core/wasm/WasmMachineThreads.cpp.
+ (WTF::ThreadGroup::~ThreadGroup):
+ (WTF::ThreadGroup::add):
+ (WTF::ThreadGroup::addCurrentThread):
+ * wtf/ThreadGroup.h: Copied from Source/_javascript_Core/wasm/WasmMachineThreads.cpp.
+ (WTF::ThreadGroup::create):
+ (WTF::ThreadGroup::threads):
+ (WTF::ThreadGroup::getLock):
+ (WTF::ThreadGroup::weakFromThis):
+ * wtf/Threading.cpp:
+ (WTF::shouldRemoveThreadFromThreadGroup):
+ (WTF::Thread::didExit):
+ (WTF::Thread::addToThreadGroup):
+ (WTF::Thread::removeFromThreadGroup):
+ * wtf/Threading.h:
+ * wtf/ThreadingPthreads.cpp:
+ (WTF::Thread::resume):
+ (WTF::Thread::getRegisters):
+ * wtf/ThreadingWin.cpp:
+ (WTF::Thread::resume):
+ (WTF::Thread::getRegisters):
+
2017-07-18 Andy Estes <aes...@apple.com>
[Xcode] Enable CLANG_WARN_RANGE_LOOP_ANALYSIS
Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (219652 => 219653)
--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2017-07-19 08:43:57 UTC (rev 219653)
@@ -131,6 +131,7 @@
DCEE22011CEA7551000C2396 /* BlockObjCExceptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = DCEE21FD1CEA7551000C2396 /* BlockObjCExceptions.mm */; };
DCEE22031CEA7551000C2396 /* PlatformUserPreferredLanguagesMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = DCEE21FF1CEA7551000C2396 /* PlatformUserPreferredLanguagesMac.mm */; };
E15556F518A0CC18006F48FB /* CryptographicUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E15556F318A0CC18006F48FB /* CryptographicUtilities.cpp */; };
+ E311FB171F0A568B003C08DE /* ThreadGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E311FB151F0A568B003C08DE /* ThreadGroup.cpp */; };
E3200AB81E9A536D003B59D2 /* ThreadHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */; };
E38C41251EB4E04C0042957D /* CPUTimeCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = E38C41241EB4E04C0042957D /* CPUTimeCocoa.mm */; };
E38C41281EB4E0680042957D /* CPUTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38C41261EB4E0680042957D /* CPUTime.cpp */; };
@@ -539,6 +540,8 @@
DE5A09FB1BA36992003D4424 /* CommonCryptoSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonCryptoSPI.h; sourceTree = "<group>"; };
E15556F318A0CC18006F48FB /* CryptographicUtilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptographicUtilities.cpp; sourceTree = "<group>"; };
E15556F418A0CC18006F48FB /* CryptographicUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptographicUtilities.h; sourceTree = "<group>"; };
+ E311FB151F0A568B003C08DE /* ThreadGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadGroup.cpp; sourceTree = "<group>"; };
+ E311FB161F0A568B003C08DE /* ThreadGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadGroup.h; sourceTree = "<group>"; };
E3200AB41E9A536D003B59D2 /* PlatformRegisters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformRegisters.h; sourceTree = "<group>"; };
E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadHolder.cpp; sourceTree = "<group>"; };
E3200AB61E9A536D003B59D2 /* ThreadHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadHolder.h; sourceTree = "<group>"; };
@@ -956,6 +959,8 @@
E3E158251EADA53C004A079D /* SystemFree.h */,
0FB317C31C488001007E395A /* SystemTracing.h */,
A8A4732F151A825B004123FF /* ThreadFunctionInvocation.h */,
+ E311FB151F0A568B003C08DE /* ThreadGroup.cpp */,
+ E311FB161F0A568B003C08DE /* ThreadGroup.h */,
E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */,
E3200AB61E9A536D003B59D2 /* ThreadHolder.h */,
A8A47330151A825B004123FF /* ThreadHolderPthreads.cpp */,
@@ -1388,6 +1393,7 @@
A8A47448151A825B004123FF /* ThreadHolderPthreads.cpp in Sources */,
A8A4744A151A825B004123FF /* Threading.cpp in Sources */,
A8A4744E151A825B004123FF /* ThreadingPthreads.cpp in Sources */,
+ E311FB171F0A568B003C08DE /* ThreadGroup.cpp in Sources */,
5311BD5C1EA822F900525281 /* ThreadMessage.cpp in Sources */,
0F66B2901DC97BAB004A1D3F /* TimeWithDynamicClockType.cpp in Sources */,
1C181C8F1D307AB800F5FA16 /* UTextProvider.cpp in Sources */,
Modified: trunk/Source/WTF/wtf/AutomaticThread.cpp (219652 => 219653)
--- trunk/Source/WTF/wtf/AutomaticThread.cpp 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/AutomaticThread.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -27,6 +27,7 @@
#include "AutomaticThread.h"
#include "DataLog.h"
+#include "Threading.h"
namespace WTF {
Modified: trunk/Source/WTF/wtf/CMakeLists.txt (219652 => 219653)
--- trunk/Source/WTF/wtf/CMakeLists.txt 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/CMakeLists.txt 2017-07-19 08:43:57 UTC (rev 219653)
@@ -132,6 +132,7 @@
StringPrintStream.h
SystemFree.h
SystemTracing.h
+ ThreadGroup.h
ThreadHolder.cpp
ThreadMessage.h
ThreadSafeRefCounted.h
@@ -246,6 +247,7 @@
StackStats.cpp
StackTrace.cpp
StringPrintStream.cpp
+ ThreadGroup.cpp
ThreadMessage.cpp
Threading.cpp
TimeWithDynamicClockType.cpp
Modified: trunk/Source/WTF/wtf/CrossThreadCopier.h (219652 => 219653)
--- trunk/Source/WTF/wtf/CrossThreadCopier.h 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/CrossThreadCopier.h 2017-07-19 08:43:57 UTC (rev 219653)
@@ -35,6 +35,7 @@
#include <wtf/Forward.h>
#include <wtf/HashSet.h>
#include <wtf/RefPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
#include <wtf/text/WTFString.h>
namespace WTF {
Modified: trunk/Source/WTF/wtf/ParkingLot.h (219652 => 219653)
--- trunk/Source/WTF/wtf/ParkingLot.h 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/ParkingLot.h 2017-07-19 08:43:57 UTC (rev 219653)
@@ -28,11 +28,12 @@
#include <wtf/Atomics.h>
#include <wtf/ScopedLambda.h>
-#include <wtf/Threading.h>
#include <wtf/TimeWithDynamicClockType.h>
namespace WTF {
+class Thread;
+
class ParkingLot {
ParkingLot() = delete;
ParkingLot(const ParkingLot&) = delete;
Copied: trunk/Source/WTF/wtf/ThreadGroup.cpp (from rev 219652, trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp) (0 => 219653)
--- trunk/Source/WTF/wtf/ThreadGroup.cpp (rev 0)
+++ trunk/Source/WTF/wtf/ThreadGroup.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 Yusuke Suzuki <utatane....@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ThreadGroup.h"
+
+#include <wtf/NeverDestroyed.h>
+
+namespace WTF {
+
+ThreadGroup::~ThreadGroup()
+{
+ std::lock_guard<std::mutex> locker(m_lock);
+ for (auto& thread : m_threads)
+ thread->removeFromThreadGroup(locker, *this);
+}
+
+bool ThreadGroup::add(Thread& thread)
+{
+ std::lock_guard<std::mutex> locker(m_lock);
+ return thread.addToThreadGroup(locker, *this);
+}
+
+void ThreadGroup::addCurrentThread()
+{
+ bool isAdded = add(Thread::current());
+ ASSERT_UNUSED(isAdded, isAdded);
+}
+
+} // namespace WTF
Copied: trunk/Source/WTF/wtf/ThreadGroup.h (from rev 219652, trunk/Source/_javascript_Core/wasm/WasmMachineThreads.cpp) (0 => 219653)
--- trunk/Source/WTF/wtf/ThreadGroup.h (rev 0)
+++ trunk/Source/WTF/wtf/ThreadGroup.h 2017-07-19 08:43:57 UTC (rev 219653)
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 Yusuke Suzuki <utatane....@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <memory>
+#include <wtf/ListHashSet.h>
+#include <wtf/Lock.h>
+#include <wtf/Threading.h>
+
+namespace WTF {
+
+class ThreadGroup : public std::enable_shared_from_this<ThreadGroup> {
+public:
+ friend class Thread;
+
+ static std::shared_ptr<ThreadGroup> create()
+ {
+ return std::make_shared<ThreadGroup>();
+ }
+
+ WTF_EXPORT_PRIVATE bool add(Thread&);
+ WTF_EXPORT_PRIVATE void addCurrentThread();
+
+ const ListHashSet<Ref<Thread>>& threads(const AbstractLocker&) const { return m_threads; }
+
+ std::mutex& getLock() { return m_lock; }
+
+ WTF_EXPORT_PRIVATE ~ThreadGroup();
+
+ ThreadGroup() = default;
+
+private:
+ std::weak_ptr<ThreadGroup> weakFromThis()
+ {
+ return shared_from_this();
+ }
+
+ // We use std::mutex since it can be used when deallocating TLS.
+ std::mutex m_lock;
+ ListHashSet<Ref<Thread>> m_threads;
+};
+
+}
+
+using WTF::ThreadGroup;
Modified: trunk/Source/WTF/wtf/Threading.cpp (219652 => 219653)
--- trunk/Source/WTF/wtf/Threading.cpp 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/Threading.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -26,8 +26,6 @@
#include "config.h"
#include "Threading.h"
-#include "dtoa.h"
-#include "dtoa/cached-powers.h"
#include <algorithm>
#include <cmath>
#include <cstring>
@@ -34,6 +32,7 @@
#include <wtf/DateMath.h>
#include <wtf/PrintStream.h>
#include <wtf/RandomNumberSeed.h>
+#include <wtf/ThreadGroup.h>
#include <wtf/ThreadHolder.h>
#include <wtf/ThreadMessage.h>
#include <wtf/ThreadingPrimitives.h>
@@ -130,12 +129,70 @@
m_stack = StackBounds::currentThreadStackBounds();
}
+static bool shouldRemoveThreadFromThreadGroup()
+{
+#if OS(WINDOWS)
+ // On Windows the thread specific destructor is also called when the
+ // main thread is exiting. This may lead to the main thread waiting
+ // forever for the thread group lock when exiting, if the sampling
+ // profiler thread was terminated by the system while holding the
+ // thread group lock.
+ if (WTF::isMainThread())
+ return false;
+#endif
+ return true;
+}
+
void Thread::didExit()
{
+ if (shouldRemoveThreadFromThreadGroup()) {
+ Vector<std::shared_ptr<ThreadGroup>> threadGroups;
+ {
+ std::lock_guard<std::mutex> locker(m_mutex);
+ for (auto& threadGroup : m_threadGroups) {
+ // If ThreadGroup is just being destroyed,
+ // we do not need to perform unregistering.
+ if (auto retained = threadGroup.lock())
+ threadGroups.append(WTFMove(retained));
+ }
+ m_isShuttingDown = true;
+ }
+ for (auto& threadGroup : threadGroups) {
+ std::lock_guard<std::mutex> threadGroupLocker(threadGroup->getLock());
+ std::lock_guard<std::mutex> locker(m_mutex);
+ threadGroup->m_threads.remove(*this);
+ }
+ }
+ // We would like to say "thread is exited" after unregistering threads from thread groups.
+ // So we need to separate m_isShuttingDown from m_didExit.
std::lock_guard<std::mutex> locker(m_mutex);
m_didExit = true;
}
+bool Thread::addToThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup& threadGroup)
+{
+ UNUSED_PARAM(threadGroupLocker);
+ std::lock_guard<std::mutex> locker(m_mutex);
+ if (m_isShuttingDown)
+ return false;
+ if (threadGroup.m_threads.add(*this).isNewEntry)
+ m_threadGroups.append(threadGroup.weakFromThis());
+ return true;
+}
+
+void Thread::removeFromThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup& threadGroup)
+{
+ UNUSED_PARAM(threadGroupLocker);
+ std::lock_guard<std::mutex> locker(m_mutex);
+ if (m_isShuttingDown)
+ return;
+ m_threadGroups.removeFirstMatching([&] (auto weakPtr) {
+ if (auto sharedPtr = weakPtr.lock())
+ return sharedPtr.get() == &threadGroup;
+ return false;
+ });
+}
+
void Thread::setCurrentThreadIsUserInteractive(int relativePriority)
{
#if HAVE(QOS_CLASSES)
Modified: trunk/Source/WTF/wtf/Threading.h (219652 => 219653)
--- trunk/Source/WTF/wtf/Threading.h 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/Threading.h 2017-07-19 08:43:57 UTC (rev 219653)
@@ -40,6 +40,7 @@
#include <wtf/RefPtr.h>
#include <wtf/StackBounds.h>
#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/Vector.h>
#if USE(PTHREADS) && !OS(DARWIN)
#include <semaphore.h>
@@ -48,16 +49,19 @@
namespace WTF {
+class AbstractLocker;
class ThreadMessageData;
using ThreadIdentifier = uint32_t;
typedef void (*ThreadFunction)(void* argument);
+class ThreadGroup;
class ThreadHolder;
class PrintStream;
class Thread : public ThreadSafeRefCounted<Thread> {
public:
+ friend class ThreadGroup;
friend class ThreadHolder;
WTF_EXPORT_PRIVATE ~Thread();
@@ -175,11 +179,17 @@
void didJoin() { m_joinableState = Joined; }
bool hasExited() { return m_didExit; }
+ // These functions are only called from ThreadGroup.
+ bool addToThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup&);
+ void removeFromThreadGroup(const std::lock_guard<std::mutex>& threadGroupLocker, ThreadGroup&);
+
// WordLock & Lock rely on ThreadSpecific. But Thread object can be destroyed even after ThreadSpecific things are destroyed.
std::mutex m_mutex;
ThreadIdentifier m_id { 0 };
JoinableState m_joinableState { Joinable };
StackBounds m_stack { StackBounds::emptyBounds() };
+ Vector<std::weak_ptr<ThreadGroup>> m_threadGroups;
+ bool m_isShuttingDown { false };
bool m_didExit { false };
#if USE(PTHREADS)
pthread_t m_handle;
Modified: trunk/Source/WTF/wtf/ThreadingPthreads.cpp (219652 => 219653)
--- trunk/Source/WTF/wtf/ThreadingPthreads.cpp 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/ThreadingPthreads.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -40,6 +40,7 @@
#include <wtf/RawPointer.h>
#include <wtf/StdLibExtras.h>
#include <wtf/ThreadFunctionInvocation.h>
+#include <wtf/ThreadGroup.h>
#include <wtf/ThreadHolder.h>
#include <wtf/ThreadingPrimitives.h>
#include <wtf/WordLock.h>
@@ -73,6 +74,8 @@
namespace WTF {
+static StaticLock globalSuspendLock;
+
Thread::Thread()
{
#if !OS(DARWIN)
@@ -92,7 +95,6 @@
// We use SIGUSR1 to suspend and resume machine threads in _javascript_Core.
static constexpr const int SigThreadSuspendResume = SIGUSR1;
static std::atomic<Thread*> targetThread { nullptr };
-static StaticWordLock globalSuspendLock;
#if COMPILER(GCC)
#pragma GCC diagnostic push
@@ -329,7 +331,18 @@
auto Thread::suspend() -> Expected<void, PlatformSuspendError>
{
RELEASE_ASSERT_WITH_MESSAGE(id() != currentThread(), "We do not support suspending the current thread itself.");
- std::lock_guard<std::mutex> locker(m_mutex);
+ // During suspend, suspend or resume should not be executed from the other threads.
+ // We use global lock instead of per thread lock.
+ // Consider the following case, there are threads A and B.
+ // And A attempt to suspend B and B attempt to suspend A.
+ // A and B send signals. And later, signals are delivered to A and B.
+ // In that case, both will be suspended.
+ //
+ // And it is important to use a global lock to suspend and resume. Let's consider using per-thread lock.
+ // Your issuing thread (A) attempts to suspend the target thread (B). Then, you will suspend the thread (C) additionally.
+ // This case frequently happens if you stop threads to perform stack scanning. But thread (B) may hold the lock of thread (C).
+ // In that case, dead lock happens. Using global lock here avoids this dead lock.
+ LockHolder locker(globalSuspendLock);
#if OS(DARWIN)
kern_return_t result = thread_suspend(m_platformThread);
if (result != KERN_SUCCESS)
@@ -336,58 +349,46 @@
return makeUnexpected(result);
return { };
#else
- {
- // During suspend, suspend or resume should not be executed from the other threads.
- // We use global lock instead of per thread lock.
- // Consider the following case, there are threads A and B.
- // And A attempt to suspend B and B attempt to suspend A.
- // A and B send signals. And later, signals are delivered to A and B.
- // In that case, both will be suspended.
- WordLockHolder locker(globalSuspendLock);
- if (!m_suspendCount) {
- // Ideally, we would like to use pthread_sigqueue. It allows us to pass the argument to the signal handler.
- // But it can be used in a few platforms, like Linux.
- // Instead, we use Thread* stored in the thread local storage to pass it to the signal handler.
- targetThread.store(this);
- int result = pthread_kill(m_handle, SigThreadSuspendResume);
- if (result)
- return makeUnexpected(result);
- sem_wait(&m_semaphoreForSuspendResume);
- // Release barrier ensures that this operation is always executed after all the above processing is done.
- m_suspended.store(true, std::memory_order_release);
- }
- ++m_suspendCount;
- return { };
+ if (!m_suspendCount) {
+ // Ideally, we would like to use pthread_sigqueue. It allows us to pass the argument to the signal handler.
+ // But it can be used in a few platforms, like Linux.
+ // Instead, we use Thread* stored in the thread local storage to pass it to the signal handler.
+ targetThread.store(this);
+ int result = pthread_kill(m_handle, SigThreadSuspendResume);
+ if (result)
+ return makeUnexpected(result);
+ sem_wait(&m_semaphoreForSuspendResume);
+ // Release barrier ensures that this operation is always executed after all the above processing is done.
+ m_suspended.store(true, std::memory_order_release);
}
+ ++m_suspendCount;
+ return { };
#endif
}
void Thread::resume()
{
- std::lock_guard<std::mutex> locker(m_mutex);
+ // During resume, suspend or resume should not be executed from the other threads.
+ LockHolder locker(globalSuspendLock);
#if OS(DARWIN)
thread_resume(m_platformThread);
#else
- {
- // During resume, suspend or resume should not be executed from the other threads.
- WordLockHolder locker(globalSuspendLock);
- if (m_suspendCount == 1) {
- // When allowing SigThreadSuspendResume interrupt in the signal handler by sigsuspend and SigThreadSuspendResume is actually issued,
- // the signal handler itself will be called once again.
- // There are several ways to distinguish the handler invocation for suspend and resume.
- // 1. Use different signal numbers. And check the signal number in the handler.
- // 2. Use some arguments to distinguish suspend and resume in the handler. If pthread_sigqueue can be used, we can take this.
- // 3. Use thread local storage with atomic variables in the signal handler.
- // In this implementaiton, we take (3). suspended flag is used to distinguish it.
- targetThread.store(this);
- if (pthread_kill(m_handle, SigThreadSuspendResume) == ESRCH)
- return;
- sem_wait(&m_semaphoreForSuspendResume);
- // Release barrier ensures that this operation is always executed after all the above processing is done.
- m_suspended.store(false, std::memory_order_release);
- }
- --m_suspendCount;
+ if (m_suspendCount == 1) {
+ // When allowing SigThreadSuspendResume interrupt in the signal handler by sigsuspend and SigThreadSuspendResume is actually issued,
+ // the signal handler itself will be called once again.
+ // There are several ways to distinguish the handler invocation for suspend and resume.
+ // 1. Use different signal numbers. And check the signal number in the handler.
+ // 2. Use some arguments to distinguish suspend and resume in the handler. If pthread_sigqueue can be used, we can take this.
+ // 3. Use thread local storage with atomic variables in the signal handler.
+ // In this implementaiton, we take (3). suspended flag is used to distinguish it.
+ targetThread.store(this);
+ if (pthread_kill(m_handle, SigThreadSuspendResume) == ESRCH)
+ return;
+ sem_wait(&m_semaphoreForSuspendResume);
+ // Release barrier ensures that this operation is always executed after all the above processing is done.
+ m_suspended.store(false, std::memory_order_release);
}
+ --m_suspendCount;
#endif
}
@@ -426,7 +427,7 @@
size_t Thread::getRegisters(PlatformRegisters& registers)
{
- std::lock_guard<std::mutex> locker(m_mutex);
+ LockHolder locker(globalSuspendLock);
#if OS(DARWIN)
auto metadata = threadStateMetadata();
kern_return_t result = thread_get_state(m_platformThread, metadata.flavor, (thread_state_t)®isters, &metadata.userCount);
Modified: trunk/Source/WTF/wtf/ThreadingWin.cpp (219652 => 219653)
--- trunk/Source/WTF/wtf/ThreadingWin.cpp 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WTF/wtf/ThreadingWin.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -91,6 +91,7 @@
#include <process.h>
#include <windows.h>
#include <wtf/CurrentTime.h>
+#include <wtf/Lock.h>
#include <wtf/MainThread.h>
#include <wtf/MathExtras.h>
#include <wtf/NeverDestroyed.h>
@@ -104,6 +105,8 @@
namespace WTF {
+static StaticLock globalSuspendLock;
+
Thread::Thread()
{
}
@@ -243,7 +246,7 @@
auto Thread::suspend() -> Expected<void, PlatformSuspendError>
{
RELEASE_ASSERT_WITH_MESSAGE(id() != currentThread(), "We do not support suspending the current thread itself.");
- std::lock_guard<std::mutex> locker(m_mutex);
+ LockHolder locker(globalSuspendLock);
DWORD result = SuspendThread(m_handle);
if (result != (DWORD)-1)
return { };
@@ -253,13 +256,13 @@
// During resume, suspend or resume should not be executed from the other threads.
void Thread::resume()
{
- std::lock_guard<std::mutex> locker(m_mutex);
+ LockHolder locker(globalSuspendLock);
ResumeThread(m_handle);
}
size_t Thread::getRegisters(PlatformRegisters& registers)
{
- std::lock_guard<std::mutex> locker(m_mutex);
+ LockHolder locker(globalSuspendLock);
registers.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
GetThreadContext(m_handle, ®isters);
return sizeof(CONTEXT);
Modified: trunk/Source/WebCore/ChangeLog (219652 => 219653)
--- trunk/Source/WebCore/ChangeLog 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WebCore/ChangeLog 2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,12 @@
+2017-07-19 Yusuke Suzuki <utatane....@gmail.com>
+
+ [WTF] Implement WTF::ThreadGroup
+ https://bugs.webkit.org/show_bug.cgi?id=174081
+
+ Reviewed by Mark Lam.
+
+ * page/ResourceUsageThread.h:
+
2017-07-18 Andy Estes <aes...@apple.com>
[Xcode] Enable CLANG_WARN_RANGE_LOOP_ANALYSIS
Modified: trunk/Source/WebCore/page/ResourceUsageThread.h (219652 => 219653)
--- trunk/Source/WebCore/page/ResourceUsageThread.h 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WebCore/page/ResourceUsageThread.h 2017-07-19 08:43:57 UTC (rev 219653)
@@ -35,6 +35,7 @@
#include <wtf/Lock.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/Noncopyable.h>
+#include <wtf/Threading.h>
namespace JSC {
class VM;
Modified: trunk/Source/WebKit/ChangeLog (219652 => 219653)
--- trunk/Source/WebKit/ChangeLog 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WebKit/ChangeLog 2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,12 @@
+2017-07-19 Yusuke Suzuki <utatane....@gmail.com>
+
+ [WTF] Implement WTF::ThreadGroup
+ https://bugs.webkit.org/show_bug.cgi?id=174081
+
+ Reviewed by Mark Lam.
+
+ * Shared/AsyncRequest.h:
+
2017-07-18 Carlos Garcia Campos <cgar...@igalia.com>
WebDriver: handle invalid selector errors
Modified: trunk/Source/WebKit/Shared/AsyncRequest.h (219652 => 219653)
--- trunk/Source/WebKit/Shared/AsyncRequest.h 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Source/WebKit/Shared/AsyncRequest.h 2017-07-19 08:43:57 UTC (rev 219653)
@@ -27,6 +27,7 @@
#ifndef AsyncRequest_h
#define AsyncRequest_h
+#include <wtf/Function.h>
#include <wtf/HashMap.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
Modified: trunk/Tools/ChangeLog (219652 => 219653)
--- trunk/Tools/ChangeLog 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Tools/ChangeLog 2017-07-19 08:43:57 UTC (rev 219653)
@@ -1,3 +1,18 @@
+2017-07-19 Yusuke Suzuki <utatane....@gmail.com>
+
+ [WTF] Implement WTF::ThreadGroup
+ https://bugs.webkit.org/show_bug.cgi?id=174081
+
+ Reviewed by Mark Lam.
+
+ Add WTF::ThreadGroup tests.
+
+ * TestWebKitAPI/CMakeLists.txt:
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WTF/ThreadGroup.cpp: Added.
+ (TestWebKitAPI::testThreadGroup):
+ (TestWebKitAPI::TEST):
+
2017-07-18 Andy Estes <aes...@apple.com>
[Xcode] Enable CLANG_WARN_RANGE_LOOP_ANALYSIS
Modified: trunk/Tools/TestWebKitAPI/CMakeLists.txt (219652 => 219653)
--- trunk/Tools/TestWebKitAPI/CMakeLists.txt 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Tools/TestWebKitAPI/CMakeLists.txt 2017-07-19 08:43:57 UTC (rev 219653)
@@ -81,6 +81,7 @@
${TESTWEBKITAPI_DIR}/Tests/WTF/StringOperators.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/StringView.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/TextBreakIterator.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/ThreadGroup.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/Time.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/UniqueRef.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/Variant.cpp
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (219652 => 219653)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-07-19 06:11:50 UTC (rev 219652)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-07-19 08:43:57 UTC (rev 219653)
@@ -632,6 +632,7 @@
D34E08761E4E42E1005FF14A /* WKWebViewGetContents.mm in Sources */ = {isa = PBXBuildFile; fileRef = D3BE5E341E4CE85E00FD563A /* WKWebViewGetContents.mm */; };
E1220DCA155B28AA0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E1220DC9155B287D0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html */; };
E194E1BD177E53C7009C4D4E /* StopLoadingFromDidReceiveResponse.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */; };
+ E3DEA8111F0A589000CBC2E8 /* ThreadGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */; };
ECA680CE1E68CC0900731D20 /* StringUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = ECA680CD1E68CC0900731D20 /* StringUtilities.mm */; };
F407FE391F1D0DFC0017CF25 /* enormous.svg in Copy Resources */ = {isa = PBXBuildFile; fileRef = F407FE381F1D0DE60017CF25 /* enormous.svg */; };
F415086D1DA040C50044BE9B /* play-audio-on-click.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F415086C1DA040C10044BE9B /* play-audio-on-click.html */; };
@@ -1616,6 +1617,7 @@
E194E1BA177E5145009C4D4E /* StopLoadingFromDidReceiveResponse.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StopLoadingFromDidReceiveResponse.mm; sourceTree = "<group>"; };
E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = StopLoadingFromDidReceiveResponse.html; sourceTree = "<group>"; };
E19DB9781B32137C00DB38D4 /* NavigatorLanguage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NavigatorLanguage.mm; sourceTree = "<group>"; };
+ E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadGroup.cpp; sourceTree = "<group>"; };
E40019301ACE9B5C001B0A2A /* BloomFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BloomFilter.cpp; sourceTree = "<group>"; };
E490296714E2E3A4002BEDD1 /* TypingStyleCrash.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TypingStyleCrash.mm; sourceTree = "<group>"; };
E4A757D3178AEA5B00B5D7A4 /* Deque.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Deque.cpp; sourceTree = "<group>"; };
@@ -2404,6 +2406,7 @@
7C74D42D188228F300E5ED57 /* StringView.cpp */,
5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */,
9329AA281DE3F81E003ABD07 /* TextBreakIterator.cpp */,
+ E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */,
5311BD5D1EA9490D00525281 /* ThreadMessages.cpp */,
0F2C20B71DCD544800542D9E /* Time.cpp */,
5C5E633D1D0B67940085A025 /* UniqueRef.cpp */,
@@ -2904,6 +2907,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ E3DEA8111F0A589000CBC2E8 /* ThreadGroup.cpp in Sources */,
7C83DE991D0A590C00FEBCF3 /* AtomicString.cpp in Sources */,
1ADAD1501D77A9F600212586 /* BlockPtr.mm in Sources */,
7C83DE9C1D0A590C00FEBCF3 /* BloomFilter.cpp in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp (0 => 219653)
--- trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/ThreadGroup.cpp 2017-07-19 08:43:57 UTC (rev 219653)
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 Yusuke Suzuki <utatane....@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/Condition.h>
+#include <wtf/ThreadGroup.h>
+#include <wtf/Vector.h>
+
+namespace TestWebKitAPI {
+
+enum class Mode { Add, AddCurrentThread };
+static void testThreadGroup(Mode mode)
+{
+ std::shared_ptr<ThreadGroup> threadGroup = ThreadGroup::create();
+ unsigned numberOfThreads = 16;
+ unsigned waitingThreads = 0;
+ bool restarting = false;
+ Lock lock;
+ Condition condition;
+ Condition restartCondition;
+ Vector<RefPtr<Thread>> threads;
+
+ {
+ auto locker = holdLock(lock);
+ for (unsigned i = 0; i < numberOfThreads; ++i) {
+ RefPtr<Thread> thread = Thread::create("ThreadGroupWorker", [&] {
+ auto locker = holdLock(lock);
+ if (mode == Mode::AddCurrentThread)
+ threadGroup->addCurrentThread();
+ ++waitingThreads;
+ condition.notifyOne();
+ restartCondition.wait(lock, [&] {
+ return restarting;
+ });
+ });
+ if (mode == Mode::Add)
+ EXPECT_EQ(threadGroup->add(*thread), true);
+ threads.append(thread);
+ }
+
+ condition.wait(lock, [&] {
+ return waitingThreads == numberOfThreads;
+ });
+ }
+
+ {
+ auto threadGroupLocker = holdLock(threadGroup->getLock());
+ EXPECT_EQ(threads.size(), numberOfThreads);
+ EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), numberOfThreads);
+ {
+ auto locker = holdLock(lock);
+ restarting = true;
+ restartCondition.notifyAll();
+ }
+
+ // While holding ThreadGroup lock, threads do not exit.
+ WTF::sleep(0.1);
+ EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), numberOfThreads);
+ }
+ {
+ for (auto& thread : threads)
+ thread->waitForCompletion();
+
+ auto threadGroupLocker = holdLock(threadGroup->getLock());
+ EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 0u);
+ }
+}
+
+TEST(WTF, ThreadGroupAdd)
+{
+ testThreadGroup(Mode::Add);
+}
+
+TEST(WTF, ThreadGroupAddCurrentThread)
+{
+ testThreadGroup(Mode::AddCurrentThread);
+}
+
+TEST(WTF, ThreadGroupDoNotAddDeadThread)
+{
+ std::shared_ptr<ThreadGroup> threadGroup = ThreadGroup::create();
+ RefPtr<Thread> thread = Thread::create("ThreadGroupWorker", [&] { });
+ thread->waitForCompletion();
+ EXPECT_EQ(threadGroup->add(*thread), false);
+
+ auto threadGroupLocker = holdLock(threadGroup->getLock());
+ EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 0u);
+}
+
+TEST(WTF, ThreadGroupAddDuplicateThreads)
+{
+ bool restarting = false;
+ Lock lock;
+ Condition restartCondition;
+ std::shared_ptr<ThreadGroup> threadGroup = ThreadGroup::create();
+ RefPtr<Thread> thread = Thread::create("ThreadGroupWorker", [&] {
+ auto locker = holdLock(lock);
+ restartCondition.wait(lock, [&] {
+ return restarting;
+ });
+ });
+ EXPECT_EQ(threadGroup->add(*thread), true);
+ EXPECT_EQ(threadGroup->add(*thread), true);
+
+ {
+ auto threadGroupLocker = holdLock(threadGroup->getLock());
+ EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 1u);
+ }
+
+ {
+ auto locker = holdLock(lock);
+ restarting = true;
+ restartCondition.notifyAll();
+ }
+ thread->waitForCompletion();
+ {
+ auto threadGroupLocker = holdLock(threadGroup->getLock());
+ EXPECT_EQ(threadGroup->threads(threadGroupLocker).size(), 0u);
+ }
+}
+
+} // namespace TestWebKitAPI