Changeset 219653 in webkit
- Timestamp:
- Jul 19, 2017 1:43:57 AM (7 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 24 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/API/tests/ExecutionTimeLimitTest.cpp
r219260 r219653 37 37 #include <wtf/CurrentTime.h> 38 38 #include <wtf/Lock.h> 39 #include <wtf/Threading.h> 39 40 #include <wtf/text/StringBuilder.h> 40 41 -
trunk/Source/JavaScriptCore/ChangeLog
r219648 r219653 1 2017-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 1 55 2017-07-18 Andy Estes <aestes@apple.com> 2 56 -
trunk/Source/JavaScriptCore/heap/MachineStackMarker.cpp
r219647 r219653 24 24 25 25 #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" 33 27 #include <setjmp.h> 34 28 #include <stdlib.h> 35 #include <wtf/MainThread.h> 29 #include <wtf/BitVector.h> 30 #include <wtf/PageBlock.h> 36 31 #include <wtf/StdLibExtras.h> 37 32 … … 40 35 namespace JSC { 41 36 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 #else129 #define FILL_CALLEE_SAVES_FOR_CRASH_INFO(number)130 #define FILL_CALLER_SAVES_FOR_CRASH_INFO(number)131 #endif132 133 37 MachineThreads::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 { 224 40 } 225 41 … … 235 51 conservativeRoots.add(currentThreadState.stackTop, currentThreadState.stackOrigin, jitStubRoutines, codeBlocks); 236 52 } 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() const250 {251 return MachineContext::stackPointer(regs);252 }253 254 #if ENABLE(SAMPLING_PROFILER)255 void* MachineThreads::MachineThread::Registers::framePointer() const256 {257 #if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)258 return MachineContext::framePointer(regs);259 #else260 #error Need a way to get the frame pointer for another thread on this platform261 #endif262 }263 264 void* MachineThreads::MachineThread::Registers::instructionPointer() const265 {266 #if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)267 return MachineContext::instructionPointer(regs);268 #else269 #error Need a way to get the instruction pointer for another thread on this platform270 #endif271 }272 273 void* MachineThreads::MachineThread::Registers::llintPC() const274 {275 // LLInt uses regT4 as PC.276 #if OS(WINDOWS) || HAVE(MACHINE_CONTEXT)277 return MachineContext::llintInstructionPointer(regs);278 #else279 #error Need a way to get the LLIntPC for another thread on this platform280 #endif281 }282 #endif // ENABLE(SAMPLING_PROFILER)283 53 284 54 static inline int osRedZoneAdjustment() … … 297 67 } 298 68 299 st d::pair<void*, size_t> MachineThreads::MachineThread::captureStack(void* stackTop)300 { 301 char* begin = reinterpret_cast_ptr<char*>( stackBase());69 static std::pair<void*, size_t> captureStack(Thread& thread, void* stackTop) 70 { 71 char* begin = reinterpret_cast_ptr<char*>(thread.stack().origin()); 302 72 char* end = bitwise_cast<char*>(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackTop))); 303 73 ASSERT(begin >= end); … … 306 76 ASSERT(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(endWithRedZone)) == reinterpret_cast<uintptr_t>(endWithRedZone)); 307 77 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()); 310 80 311 81 std::swap(begin, endWithRedZone); … … 340 110 // operation. As the heap is generally much larger than the stack the performance hit is minimal. 341 111 // 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);112 void MachineThreads::tryCopyOtherThreadStack(Thread& thread, void* buffer, size_t capacity, size_t* size) 113 { 114 PlatformRegisters registers; 115 size_t registersSize = thread.getRegisters(registers); 346 116 347 117 // This is a workaround for <rdar://problem/27607384>. libdispatch recycles work 348 118 // queue threads without running pthread exit destructors. This can cause us to scan a 349 119 // thread during work queue initialization, when the stack pointer is null. 350 if (UNLIKELY(! registers.stackPointer())) {120 if (UNLIKELY(!MachineContext::stackPointer(registers))) { 351 121 *size = 0; 352 122 return; 353 123 } 354 124 355 std::pair<void*, size_t> stack = thread->captureStack(registers.stackPointer());125 std::pair<void*, size_t> stack = captureStack(thread, MachineContext::stackPointer(registers)); 356 126 357 127 bool canCopy = *size + registersSize + stack.second <= capacity; … … 366 136 } 367 137 368 bool MachineThreads::tryCopyOtherThreadStacks(const AbstractLocker& , void* buffer, size_t capacity, size_t* size)138 bool MachineThreads::tryCopyOtherThreadStacks(const AbstractLocker& locker, void* buffer, size_t capacity, size_t* size) 369 139 { 370 140 // Prevent two VMs from suspending each other's threads at the same time, … … 375 145 *size = 0; 376 146 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 { 385 159 #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 } 406 167 } 407 #else 408 UNUSED_PARAM(numberOfThreads); 409 ASSERT_UNUSED(result, result); 410 #endif 168 ++index; 411 169 } 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 431 190 return *size <= capacity; 432 191 } … … 449 208 size_t capacity = 0; 450 209 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)) 453 212 growBuffer(size, &buffer, &capacity); 454 213 -
trunk/Source/JavaScriptCore/heap/MachineStackMarker.h
r219647 r219653 22 22 #pragma once 23 23 24 #include "MachineContext.h"25 24 #include "RegisterState.h" 26 #include <wtf/DoublyLinkedList.h>27 25 #include <wtf/Lock.h> 28 26 #include <wtf/ScopedLambda.h> 29 #include <wtf/Thread Specific.h>27 #include <wtf/ThreadGroup.h> 30 28 31 29 namespace JSC { … … 46 44 public: 47 45 MachineThreads(); 48 ~MachineThreads();49 46 50 47 void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, CurrentThreadState*); 51 48 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(); } 53 51 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); } 86 54 87 55 private: 88 56 void gatherFromCurrentThread(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&, CurrentThreadState&); 89 57 90 void tryCopyOtherThreadStack( MachineThread*, void*, size_t capacity, size_t*);58 void tryCopyOtherThreadStack(Thread&, void*, size_t capacity, size_t*); 91 59 bool tryCopyOtherThreadStacks(const AbstractLocker&, void*, size_t capacity, size_t*); 92 60 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; 100 62 }; 101 63 -
trunk/Source/JavaScriptCore/runtime/SamplingProfiler.cpp
r219260 r219653 40 40 #include "JSFunction.h" 41 41 #include "LLIntPCRanges.h" 42 #include "MachineContext.h" 42 43 #include "MarkedBlock.h" 43 44 #include "MarkedBlockSet.h" … … 166 167 { 167 168 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()); 172 172 RELEASE_ASSERT(stackBase); 173 173 RELEASE_ASSERT(stackLimit); … … 279 279 , m_stopwatch(WTFMove(stopwatch)) 280 280 , m_timingInterval(std::chrono::microseconds(Options::sampleInterval())) 281 , m_jscExecutionThread(nullptr)282 281 , m_isPaused(false) 283 282 , m_isShutDown(false) … … 339 338 double nowTime = m_stopwatch->elapsedTime(); 340 339 341 LockHolder machineThreadsLocker(m_vm.heap.machineThreads().getLock());340 auto machineThreadsLocker = holdLock(m_vm.heap.machineThreads().getLock()); 342 341 LockHolder codeBlockSetLocker(m_vm.heap.codeBlockSet().getLock()); 343 342 LockHolder executableAllocatorLocker(ExecutableAllocator::singleton().getLock()); … … 353 352 void* llintPC; 354 353 { 355 MachineThreads::MachineThread::Registers registers;354 PlatformRegisters registers; 356 355 m_jscExecutionThread->getRegisters(registers); 357 machineFrame = registers.framePointer();356 machineFrame = MachineContext::framePointer(registers); 358 357 callFrame = static_cast<ExecState*>(machineFrame); 359 machinePC = registers.instructionPointer();360 llintPC = registers.llintPC();358 machinePC = MachineContext::instructionPointer(registers); 359 llintPC = MachineContext::llintInstructionPointer(registers); 361 360 } 362 361 // FIXME: Lets have a way of detecting when we're parsing code. … … 679 678 { 680 679 ASSERT(m_lock.isLocked()); 681 m_jscExecutionThread = m_vm.heap.machineThreads().machineThreadForCurrentThread();680 m_jscExecutionThread = &Thread::current(); 682 681 } 683 682 -
trunk/Source/JavaScriptCore/runtime/SamplingProfiler.h
r219260 r219653 197 197 Lock m_lock; 198 198 RefPtr<Thread> m_thread; 199 MachineThreads::MachineThread*m_jscExecutionThread;199 RefPtr<Thread> m_jscExecutionThread; 200 200 bool m_isPaused; 201 201 bool m_isShutDown; -
trunk/Source/JavaScriptCore/wasm/WasmMachineThreads.cpp
r219260 r219653 56 56 { 57 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&) { 58 for (auto& thread : wasmThreads().threads(locker)) { 59 sendMessage(thread.get(), [] (const PlatformRegisters&) { 62 60 // It's likely that the signal handler will already reset the instruction cache but we might as well be sure. 63 61 WTF::crossModifyingCodeFence(); -
trunk/Source/WTF/ChangeLog
r219648 r219653 1 2017-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 1 75 2017-07-18 Andy Estes <aestes@apple.com> 2 76 -
trunk/Source/WTF/WTF.xcodeproj/project.pbxproj
r219260 r219653 132 132 DCEE22031CEA7551000C2396 /* PlatformUserPreferredLanguagesMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = DCEE21FF1CEA7551000C2396 /* PlatformUserPreferredLanguagesMac.mm */; }; 133 133 E15556F518A0CC18006F48FB /* CryptographicUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E15556F318A0CC18006F48FB /* CryptographicUtilities.cpp */; }; 134 E311FB171F0A568B003C08DE /* ThreadGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E311FB151F0A568B003C08DE /* ThreadGroup.cpp */; }; 134 135 E3200AB81E9A536D003B59D2 /* ThreadHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */; }; 135 136 E38C41251EB4E04C0042957D /* CPUTimeCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = E38C41241EB4E04C0042957D /* CPUTimeCocoa.mm */; }; … … 540 541 E15556F318A0CC18006F48FB /* CryptographicUtilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptographicUtilities.cpp; sourceTree = "<group>"; }; 541 542 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>"; }; 542 545 E3200AB41E9A536D003B59D2 /* PlatformRegisters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformRegisters.h; sourceTree = "<group>"; }; 543 546 E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadHolder.cpp; sourceTree = "<group>"; }; … … 957 960 0FB317C31C488001007E395A /* SystemTracing.h */, 958 961 A8A4732F151A825B004123FF /* ThreadFunctionInvocation.h */, 962 E311FB151F0A568B003C08DE /* ThreadGroup.cpp */, 963 E311FB161F0A568B003C08DE /* ThreadGroup.h */, 959 964 E3200AB51E9A536D003B59D2 /* ThreadHolder.cpp */, 960 965 E3200AB61E9A536D003B59D2 /* ThreadHolder.h */, … … 1389 1394 A8A4744A151A825B004123FF /* Threading.cpp in Sources */, 1390 1395 A8A4744E151A825B004123FF /* ThreadingPthreads.cpp in Sources */, 1396 E311FB171F0A568B003C08DE /* ThreadGroup.cpp in Sources */, 1391 1397 5311BD5C1EA822F900525281 /* ThreadMessage.cpp in Sources */, 1392 1398 0F66B2901DC97BAB004A1D3F /* TimeWithDynamicClockType.cpp in Sources */, -
trunk/Source/WTF/wtf/AutomaticThread.cpp
r219260 r219653 28 28 29 29 #include "DataLog.h" 30 #include "Threading.h" 30 31 31 32 namespace WTF { -
trunk/Source/WTF/wtf/CMakeLists.txt
r219560 r219653 133 133 SystemFree.h 134 134 SystemTracing.h 135 ThreadGroup.h 135 136 ThreadHolder.cpp 136 137 ThreadMessage.h … … 247 248 StackTrace.cpp 248 249 StringPrintStream.cpp 250 ThreadGroup.cpp 249 251 ThreadMessage.cpp 250 252 Threading.cpp -
trunk/Source/WTF/wtf/CrossThreadCopier.h
r219260 r219653 36 36 #include <wtf/HashSet.h> 37 37 #include <wtf/RefPtr.h> 38 #include <wtf/ThreadSafeRefCounted.h> 38 39 #include <wtf/text/WTFString.h> 39 40 -
trunk/Source/WTF/wtf/ParkingLot.h
r219260 r219653 29 29 #include <wtf/Atomics.h> 30 30 #include <wtf/ScopedLambda.h> 31 #include <wtf/Threading.h>32 31 #include <wtf/TimeWithDynamicClockType.h> 33 32 34 33 namespace WTF { 34 35 class Thread; 35 36 36 37 class ParkingLot { -
trunk/Source/WTF/wtf/ThreadGroup.cpp
r219652 r219653 1 1 /* 2 * Copyright (C) 2017 Apple Inc. All rights reserved.2 * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 25 25 26 26 #include "config.h" 27 #include " WasmMachineThreads.h"27 #include "ThreadGroup.h" 28 28 29 #i f ENABLE(WEBASSEMBLY)29 #include <wtf/NeverDestroyed.h> 30 30 31 #include "MachineStackMarker.h" 32 #include <wtf/NeverDestroyed.h> 33 #include <wtf/ThreadMessage.h> 34 #include <wtf/threads/Signals.h> 31 namespace WTF { 35 32 36 namespace JSC { namespace Wasm { 37 38 39 inline MachineThreads& wasmThreads() 33 ThreadGroup::~ThreadGroup() 40 34 { 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); 48 38 } 49 39 50 void startTrackingCurrentThread()40 bool ThreadGroup::add(Thread& thread) 51 41 { 52 wasmThreads().addCurrentThread(); 42 std::lock_guard<std::mutex> locker(m_lock); 43 return thread.addToThreadGroup(locker, *this); 53 44 } 54 45 55 void resetInstructionCacheOnAllThreads()46 void ThreadGroup::addCurrentThread() 56 47 { 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); 66 50 } 67 51 68 69 } } // namespace JSC::Wasm 70 71 #endif // ENABLE(WEBASSEMBLY) 52 } // namespace WTF -
trunk/Source/WTF/wtf/ThreadGroup.h
r219652 r219653 1 1 /* 2 * Copyright (C) 2017 Apple Inc. All rights reserved.2 * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 24 24 */ 25 25 26 #include "config.h" 27 #include "WasmMachineThreads.h" 26 #pragma once 28 27 29 #if ENABLE(WEBASSEMBLY) 28 #include <memory> 29 #include <wtf/ListHashSet.h> 30 #include <wtf/Lock.h> 31 #include <wtf/Threading.h> 30 32 31 #include "MachineStackMarker.h" 32 #include <wtf/NeverDestroyed.h> 33 #include <wtf/ThreadMessage.h> 34 #include <wtf/threads/Signals.h> 33 namespace WTF { 35 34 36 namespace JSC { namespace Wasm { 35 class ThreadGroup : public std::enable_shared_from_this<ThreadGroup> { 36 public: 37 friend class Thread; 37 38 39 static std::shared_ptr<ThreadGroup> create() 40 { 41 return std::make_shared<ThreadGroup>(); 42 } 38 43 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(); 46 46 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 55 private: 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 48 66 } 49 67 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) 68 using WTF::ThreadGroup; -
trunk/Source/WTF/wtf/Threading.cpp
r219647 r219653 27 27 #include "Threading.h" 28 28 29 #include "dtoa.h"30 #include "dtoa/cached-powers.h"31 29 #include <algorithm> 32 30 #include <cmath> … … 35 33 #include <wtf/PrintStream.h> 36 34 #include <wtf/RandomNumberSeed.h> 35 #include <wtf/ThreadGroup.h> 37 36 #include <wtf/ThreadHolder.h> 38 37 #include <wtf/ThreadMessage.h> … … 131 130 } 132 131 132 static 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 133 146 void Thread::didExit() 134 147 { 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. 135 168 std::lock_guard<std::mutex> locker(m_mutex); 136 169 m_didExit = true; 170 } 171 172 bool 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 183 void 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 }); 137 194 } 138 195 -
trunk/Source/WTF/wtf/Threading.h
r219647 r219653 41 41 #include <wtf/StackBounds.h> 42 42 #include <wtf/ThreadSafeRefCounted.h> 43 #include <wtf/Vector.h> 43 44 44 45 #if USE(PTHREADS) && !OS(DARWIN) … … 49 50 namespace WTF { 50 51 52 class AbstractLocker; 51 53 class ThreadMessageData; 52 54 … … 54 56 typedef void (*ThreadFunction)(void* argument); 55 57 58 class ThreadGroup; 56 59 class ThreadHolder; 57 60 class PrintStream; … … 59 62 class Thread : public ThreadSafeRefCounted<Thread> { 60 63 public: 64 friend class ThreadGroup; 61 65 friend class ThreadHolder; 62 66 … … 176 180 bool hasExited() { return m_didExit; } 177 181 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 178 186 // WordLock & Lock rely on ThreadSpecific. But Thread object can be destroyed even after ThreadSpecific things are destroyed. 179 187 std::mutex m_mutex; … … 181 189 JoinableState m_joinableState { Joinable }; 182 190 StackBounds m_stack { StackBounds::emptyBounds() }; 191 Vector<std::weak_ptr<ThreadGroup>> m_threadGroups; 192 bool m_isShuttingDown { false }; 183 193 bool m_didExit { false }; 184 194 #if USE(PTHREADS) -
trunk/Source/WTF/wtf/ThreadingPthreads.cpp
r219647 r219653 41 41 #include <wtf/StdLibExtras.h> 42 42 #include <wtf/ThreadFunctionInvocation.h> 43 #include <wtf/ThreadGroup.h> 43 44 #include <wtf/ThreadHolder.h> 44 45 #include <wtf/ThreadingPrimitives.h> … … 74 75 namespace WTF { 75 76 77 static StaticLock globalSuspendLock; 78 76 79 Thread::Thread() 77 80 { … … 93 96 static constexpr const int SigThreadSuspendResume = SIGUSR1; 94 97 static std::atomic<Thread*> targetThread { nullptr }; 95 static StaticWordLock globalSuspendLock;96 98 97 99 #if COMPILER(GCC) … … 330 332 { 331 333 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); 333 346 #if OS(DARWIN) 334 347 kern_return_t result = thread_suspend(m_platformThread); … … 337 350 return { }; 338 351 #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 { }; 362 366 #endif 363 367 } … … 365 369 void Thread::resume() 366 370 { 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); 368 373 #if OS(DARWIN) 369 374 thread_resume(m_platformThread); 370 375 #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; 391 392 #endif 392 393 } … … 427 428 size_t Thread::getRegisters(PlatformRegisters& registers) 428 429 { 429 std::lock_guard<std::mutex> locker(m_mutex);430 LockHolder locker(globalSuspendLock); 430 431 #if OS(DARWIN) 431 432 auto metadata = threadStateMetadata(); -
trunk/Source/WTF/wtf/ThreadingWin.cpp
r219647 r219653 92 92 #include <windows.h> 93 93 #include <wtf/CurrentTime.h> 94 #include <wtf/Lock.h> 94 95 #include <wtf/MainThread.h> 95 96 #include <wtf/MathExtras.h> … … 104 105 105 106 namespace WTF { 107 108 static StaticLock globalSuspendLock; 106 109 107 110 Thread::Thread() … … 244 247 { 245 248 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); 247 250 DWORD result = SuspendThread(m_handle); 248 251 if (result != (DWORD)-1) … … 254 257 void Thread::resume() 255 258 { 256 std::lock_guard<std::mutex> locker(m_mutex);259 LockHolder locker(globalSuspendLock); 257 260 ResumeThread(m_handle); 258 261 } … … 260 263 size_t Thread::getRegisters(PlatformRegisters& registers) 261 264 { 262 std::lock_guard<std::mutex> locker(m_mutex);265 LockHolder locker(globalSuspendLock); 263 266 registers.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; 264 267 GetThreadContext(m_handle, ®isters); -
trunk/Source/WebCore/ChangeLog
r219648 r219653 1 2017-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 1 10 2017-07-18 Andy Estes <aestes@apple.com> 2 11 -
trunk/Source/WebCore/page/ResourceUsageThread.h
r219260 r219653 36 36 #include <wtf/NeverDestroyed.h> 37 37 #include <wtf/Noncopyable.h> 38 #include <wtf/Threading.h> 38 39 39 40 namespace JSC { -
trunk/Source/WebKit/ChangeLog
r219652 r219653 1 2017-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 1 10 2017-07-18 Carlos Garcia Campos <cgarcia@igalia.com> 2 11 -
trunk/Source/WebKit/Shared/AsyncRequest.h
r219260 r219653 28 28 #define AsyncRequest_h 29 29 30 #include <wtf/Function.h> 30 31 #include <wtf/HashMap.h> 31 32 #include <wtf/RefCounted.h> -
trunk/Tools/ChangeLog
r219648 r219653 1 2017-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 1 16 2017-07-18 Andy Estes <aestes@apple.com> 2 17 -
trunk/Tools/TestWebKitAPI/CMakeLists.txt
r219493 r219653 82 82 ${TESTWEBKITAPI_DIR}/Tests/WTF/StringView.cpp 83 83 ${TESTWEBKITAPI_DIR}/Tests/WTF/TextBreakIterator.cpp 84 ${TESTWEBKITAPI_DIR}/Tests/WTF/ThreadGroup.cpp 84 85 ${TESTWEBKITAPI_DIR}/Tests/WTF/Time.cpp 85 86 ${TESTWEBKITAPI_DIR}/Tests/WTF/UniqueRef.cpp -
trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
r219624 r219653 633 633 E1220DCA155B28AA0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E1220DC9155B287D0013E2FC /* MemoryCacheDisableWithinResourceLoadDelegate.html */; }; 634 634 E194E1BD177E53C7009C4D4E /* StopLoadingFromDidReceiveResponse.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */; }; 635 E3DEA8111F0A589000CBC2E8 /* ThreadGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */; }; 635 636 ECA680CE1E68CC0900731D20 /* StringUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = ECA680CD1E68CC0900731D20 /* StringUtilities.mm */; }; 636 637 F407FE391F1D0DFC0017CF25 /* enormous.svg in Copy Resources */ = {isa = PBXBuildFile; fileRef = F407FE381F1D0DE60017CF25 /* enormous.svg */; }; … … 1617 1618 E194E1BC177E534A009C4D4E /* StopLoadingFromDidReceiveResponse.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = StopLoadingFromDidReceiveResponse.html; sourceTree = "<group>"; }; 1618 1619 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>"; }; 1619 1621 E40019301ACE9B5C001B0A2A /* BloomFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BloomFilter.cpp; sourceTree = "<group>"; }; 1620 1622 E490296714E2E3A4002BEDD1 /* TypingStyleCrash.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TypingStyleCrash.mm; sourceTree = "<group>"; }; … … 2405 2407 5597F8341D9596C80066BC21 /* SynchronizedFixedQueue.cpp */, 2406 2408 9329AA281DE3F81E003ABD07 /* TextBreakIterator.cpp */, 2409 E3DEA8101F0A588000CBC2E8 /* ThreadGroup.cpp */, 2407 2410 5311BD5D1EA9490D00525281 /* ThreadMessages.cpp */, 2408 2411 0F2C20B71DCD544800542D9E /* Time.cpp */, … … 2905 2908 buildActionMask = 2147483647; 2906 2909 files = ( 2910 E3DEA8111F0A589000CBC2E8 /* ThreadGroup.cpp in Sources */, 2907 2911 7C83DE991D0A590C00FEBCF3 /* AtomicString.cpp in Sources */, 2908 2912 1ADAD1501D77A9F600212586 /* BlockPtr.mm in Sources */,
Note: See TracChangeset
for help on using the changeset viewer.