| /* |
| * Copyright (C) 2007-2020 Apple Inc. All rights reserved. |
| * Copyright (C) 2007 Justin Haygood <jhaygood@reaktix.com> |
| * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@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. |
| * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE 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 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. |
| */ |
| |
| #pragma once |
| |
| #include <mutex> |
| #include <stdint.h> |
| #include <wtf/Atomics.h> |
| #include <wtf/Expected.h> |
| #include <wtf/FastTLS.h> |
| #include <wtf/Function.h> |
| #include <wtf/HashMap.h> |
| #include <wtf/HashSet.h> |
| #include <wtf/Lock.h> |
| #include <wtf/PlatformRegisters.h> |
| #include <wtf/Ref.h> |
| #include <wtf/RefPtr.h> |
| #include <wtf/StackBounds.h> |
| #include <wtf/StackStats.h> |
| #include <wtf/ThreadSafeRefCounted.h> |
| #include <wtf/ThreadSafetyAnalysis.h> |
| #include <wtf/Vector.h> |
| #include <wtf/WordLock.h> |
| #include <wtf/text/AtomStringTable.h> |
| |
| #if USE(PTHREADS) && !OS(DARWIN) |
| #include <signal.h> |
| #endif |
| |
| #if OS(WINDOWS) |
| #include <array> |
| #endif |
| |
| #if HAVE(QOS_CLASSES) |
| #include <dispatch/dispatch.h> |
| #endif |
| |
| // X11 headers define a bunch of macros with common terms, interfering with WebCore and WTF enum values. |
| // As a workaround, we explicitly undef them here. |
| #if defined(None) |
| #undef None |
| #endif |
| |
| namespace WTF { |
| |
| class AbstractLocker; |
| class ThreadMessageData; |
| |
| enum class ThreadGroupAddResult; |
| |
| class ThreadGroup; |
| class PrintStream; |
| |
| WTF_EXPORT_PRIVATE void initialize(); |
| |
| enum class GCThreadType : uint8_t { |
| None = 0, |
| Main, |
| Helper, |
| }; |
| |
| enum class ThreadType : uint8_t { |
| Unknown = 0, |
| JavaScript, |
| Compiler, |
| GarbageCollection, |
| Network, |
| Graphics, |
| Audio, |
| }; |
| |
| class ThreadSuspendLocker { |
| WTF_MAKE_NONCOPYABLE(ThreadSuspendLocker); |
| public: |
| WTF_EXPORT_PRIVATE ThreadSuspendLocker(); |
| WTF_EXPORT_PRIVATE ~ThreadSuspendLocker(); |
| }; |
| |
| class WTF_CAPABILITY("is current") Thread : public ThreadSafeRefCounted<Thread> { |
| static std::atomic<uint32_t> s_uid; |
| public: |
| friend class ThreadGroup; |
| friend WTF_EXPORT_PRIVATE void initialize(); |
| |
| class ClientData : public ThreadSafeRefCounted<ClientData> { |
| public: |
| virtual ~ClientData() = default; |
| }; |
| |
| WTF_EXPORT_PRIVATE ~Thread(); |
| |
| enum class QOS { |
| UserInteractive, |
| UserInitiated, |
| Default, |
| Utility, |
| Background |
| }; |
| |
| #if HAVE(QOS_CLASSES) |
| static dispatch_qos_class_t dispatchQOSClass(QOS); |
| #endif |
| |
| // Returns nullptr if thread creation failed. |
| // The thread name must be a literal since on some platforms it's passed in to the thread. |
| WTF_EXPORT_PRIVATE static Ref<Thread> create(const char* threadName, Function<void()>&&, ThreadType = ThreadType::Unknown, QOS = QOS::UserInitiated); |
| |
| // Returns Thread object. |
| static Thread& current(); |
| |
| // Set of all WTF::Thread created threads. |
| WTF_EXPORT_PRIVATE static HashSet<Thread*>& allThreads() WTF_REQUIRES_LOCK(allThreadsLock()); |
| WTF_EXPORT_PRIVATE static Lock& allThreadsLock() WTF_RETURNS_LOCK(s_allThreadsLock); |
| |
| WTF_EXPORT_PRIVATE unsigned numberOfThreadGroups(); |
| |
| uint32_t uid() const { return m_uid; } |
| |
| #if OS(WINDOWS) |
| // Returns ThreadIdentifier directly. It is useful if the user only cares about identity |
| // of threads. At that time, users should know that holding this ThreadIdentifier does not ensure |
| // that the thread information is alive. While Thread::current() is not safe if it is called |
| // from the destructor of the other TLS data, currentID() always returns meaningful thread ID. |
| WTF_EXPORT_PRIVATE static ThreadIdentifier currentID(); |
| |
| ThreadIdentifier id() const { return m_id; } |
| |
| class SpecificStorage { |
| public: |
| using DestroyFunction = void (*)(void*); |
| WTF_EXPORT_PRIVATE static bool allocateKey(int& key, DestroyFunction); |
| WTF_EXPORT_PRIVATE void* get(int key); |
| WTF_EXPORT_PRIVATE void set(int key, void* value); |
| void destroySlots(); |
| |
| private: |
| static constexpr size_t s_maxKeys = 32; |
| static Atomic<int> s_numberOfKeys; |
| static std::array<Atomic<DestroyFunction>, s_maxKeys> s_destroyFunctions; |
| std::array<void*, s_maxKeys> m_slots { }; |
| }; |
| |
| SpecificStorage& specificStorage() { return m_specificStorage; }; |
| |
| struct ThreadHolder; |
| #endif |
| |
| WTF_EXPORT_PRIVATE void changePriority(int); |
| WTF_EXPORT_PRIVATE int waitForCompletion(); |
| WTF_EXPORT_PRIVATE void detach(); |
| |
| #if OS(DARWIN) |
| using PlatformSuspendError = kern_return_t; |
| #elif USE(PTHREADS) |
| using PlatformSuspendError = int; |
| #elif OS(WINDOWS) |
| using PlatformSuspendError = DWORD; |
| #endif |
| |
| WTF_EXPORT_PRIVATE Expected<void, PlatformSuspendError> suspend(const ThreadSuspendLocker&); |
| WTF_EXPORT_PRIVATE void resume(const ThreadSuspendLocker&); |
| WTF_EXPORT_PRIVATE size_t getRegisters(const ThreadSuspendLocker&, PlatformRegisters&); |
| |
| #if USE(PTHREADS) |
| #if OS(LINUX) |
| WTF_EXPORT_PRIVATE static ThreadIdentifier currentID(); |
| ThreadIdentifier id() const { return m_id; } |
| #endif |
| WTF_EXPORT_PRIVATE bool signal(int signalNumber); |
| #endif |
| |
| // Mark the current thread as requiring UI responsiveness. |
| // relativePriority is a value in the range [-15, 0] where a lower value indicates a lower priority. |
| WTF_EXPORT_PRIVATE static void setCurrentThreadIsUserInteractive(int relativePriority = 0); |
| WTF_EXPORT_PRIVATE static void setCurrentThreadIsUserInitiated(int relativePriority = 0); |
| WTF_EXPORT_PRIVATE static QOS currentThreadQOS(); |
| |
| #if HAVE(QOS_CLASSES) |
| WTF_EXPORT_PRIVATE static void setGlobalMaxQOSClass(qos_class_t); |
| #endif |
| |
| // Called in the thread during initialization. |
| // Helpful for platforms where the thread name must be set from within the thread. |
| static void initializeCurrentThreadInternal(const char* threadName); |
| static void initializeCurrentThreadEvenIfNonWTFCreated(); |
| |
| WTF_EXPORT_PRIVATE static void yield(); |
| |
| WTF_EXPORT_PRIVATE static bool exchangeIsCompilationThread(bool newValue); |
| WTF_EXPORT_PRIVATE static void registerGCThread(GCThreadType); |
| WTF_EXPORT_PRIVATE static bool mayBeGCThread(); |
| |
| WTF_EXPORT_PRIVATE void dump(PrintStream& out) const; |
| |
| static void initializePlatformThreading(); |
| |
| const StackBounds& stack() const |
| { |
| return m_stack; |
| } |
| |
| AtomStringTable* atomStringTable() |
| { |
| return m_currentAtomStringTable; |
| } |
| |
| AtomStringTable* setCurrentAtomStringTable(AtomStringTable* atomStringTable) |
| { |
| AtomStringTable* oldAtomStringTable = m_currentAtomStringTable; |
| m_currentAtomStringTable = atomStringTable; |
| return oldAtomStringTable; |
| } |
| |
| #if ENABLE(STACK_STATS) |
| StackStats::PerThreadStats& stackStats() |
| { |
| return m_stackStats; |
| } |
| #endif |
| |
| void* savedStackPointerAtVMEntry() const |
| { |
| return m_savedStackPointerAtVMEntry; |
| } |
| |
| void setSavedStackPointerAtVMEntry(void* stackPointerAtVMEntry) |
| { |
| m_savedStackPointerAtVMEntry = stackPointerAtVMEntry; |
| } |
| |
| void* savedLastStackTop() const |
| { |
| return m_savedLastStackTop; |
| } |
| |
| void setSavedLastStackTop(void* lastStackTop) |
| { |
| m_savedLastStackTop = lastStackTop; |
| } |
| |
| #if OS(DARWIN) |
| mach_port_t machThread() { return m_platformThread; } |
| #endif |
| |
| bool isCompilationThread() const { return m_isCompilationThread; } |
| GCThreadType gcThreadType() const { return static_cast<GCThreadType>(m_gcThreadType); } |
| |
| struct NewThreadContext; |
| static void entryPoint(NewThreadContext*); |
| protected: |
| Thread(); |
| |
| void initializeInThread(); |
| |
| // Internal platform-specific Thread establishment implementation. |
| bool establishHandle(NewThreadContext*, std::optional<size_t> stackSize, QOS); |
| |
| #if USE(PTHREADS) |
| void establishPlatformSpecificHandle(PlatformThreadHandle); |
| #else |
| void establishPlatformSpecificHandle(PlatformThreadHandle, ThreadIdentifier); |
| #endif |
| |
| #if USE(PTHREADS) && !OS(DARWIN) |
| static void signalHandlerSuspendResume(int, siginfo_t*, void* ucontext); |
| #endif |
| |
| #if HAVE(QOS_CLASSES) |
| static qos_class_t adjustedQOSClass(qos_class_t); |
| #endif |
| |
| static const char* normalizeThreadName(const char* threadName); |
| |
| enum JoinableState : uint8_t { |
| // The default thread state. The thread can be joined on. |
| Joinable, |
| |
| // Somebody waited on this thread to exit and this thread finally exited. This state is here because there can be a |
| // period of time between when the thread exits (which causes pthread_join to return and the remainder of waitOnThreadCompletion to run) |
| // and when threadDidExit is called. We need threadDidExit to take charge and delete the thread data since there's |
| // nobody else to pick up the slack in this case (since waitOnThreadCompletion has already returned). |
| Joined, |
| |
| // The thread has been detached and can no longer be joined on. At this point, the thread must take care of cleaning up after itself. |
| Detached, |
| }; |
| |
| JoinableState joinableState() const { return m_joinableState; } |
| void didBecomeDetached() { m_joinableState = Detached; } |
| void didExit(); |
| void didJoin() { m_joinableState = Joined; } |
| bool hasExited() const { return m_didExit; } |
| |
| // These functions are only called from ThreadGroup. |
| ThreadGroupAddResult addToThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup&); |
| void removeFromThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup&); |
| |
| // For pthread, the Thread instance is ref'ed and held in thread-specific storage. It will be deref'ed by destructTLS at thread destruction time. |
| // It employs pthreads-specific 2-pass destruction to reliably remove Thread. |
| |
| #if !HAVE(FAST_TLS) && !OS(WINDOWS) |
| static WTF_EXPORT_PRIVATE ThreadSpecificKey s_key; |
| // One time initialization for this class as a whole. |
| // This method must be called before initializeTLS() and it is not thread-safe. |
| static void initializeTLSKey(); |
| #endif |
| // This thread-specific destructor is called 2 times when thread terminates: |
| // - first, when all the other thread-specific destructors are called, it simply remembers it was 'destroyed once' |
| // and (1) re-sets itself into the thread-specific slot or (2) constructs thread local value to call it again later. |
| // - second, after all thread-specific destructors were invoked, it gets called again - this time, we deref the |
| // Thread in the TLS, completing the cleanup. |
| static void destructTLS(void* data); |
| |
| // Creates and puts an instance of Thread into thread-specific storage. |
| static Thread& initializeTLS(Ref<Thread>&&); |
| WTF_EXPORT_PRIVATE static Thread& initializeCurrentTLS(); |
| |
| // Returns nullptr if thread-specific storage was not initialized. |
| #if OS(WINDOWS) |
| WTF_EXPORT_PRIVATE static Thread* currentMayBeNull(); |
| #else |
| static Thread* currentMayBeNull(); |
| #endif |
| |
| static Lock s_allThreadsLock; |
| |
| JoinableState m_joinableState { Joinable }; |
| bool m_isShuttingDown : 1 { false }; |
| bool m_didExit : 1 { false }; |
| bool m_isDestroyedOnce : 1 { false }; |
| bool m_isCompilationThread: 1 { false }; |
| bool m_didUnregisterFromAllThreads : 1 { false }; |
| unsigned m_gcThreadType : 2 { static_cast<unsigned>(GCThreadType::None) }; |
| |
| // Lock & ParkingLot rely on ThreadSpecific. But Thread object can be destroyed even after ThreadSpecific things are destroyed. |
| // Use WordLock since WordLock does not depend on ThreadSpecific and this "Thread". |
| WordLock m_mutex; |
| StackBounds m_stack { StackBounds::emptyBounds() }; |
| HashMap<ThreadGroup*, std::weak_ptr<ThreadGroup>> m_threadGroupMap; |
| PlatformThreadHandle m_handle; |
| uint32_t m_uid; |
| #if OS(WINDOWS) |
| ThreadIdentifier m_id { 0 }; |
| #elif OS(DARWIN) |
| mach_port_t m_platformThread { MACH_PORT_NULL }; |
| #elif USE(PTHREADS) |
| #if OS(LINUX) |
| ThreadIdentifier m_id { 0 }; |
| #endif |
| PlatformRegisters* m_platformRegisters { nullptr }; |
| unsigned m_suspendCount { 0 }; |
| #endif |
| |
| #if OS(WINDOWS) |
| SpecificStorage m_specificStorage; |
| #endif |
| |
| AtomStringTable* m_currentAtomStringTable { nullptr }; |
| AtomStringTable m_defaultAtomStringTable; |
| |
| #if ENABLE(STACK_STATS) |
| StackStats::PerThreadStats m_stackStats; |
| #endif |
| void* m_savedStackPointerAtVMEntry { nullptr }; |
| void* m_savedLastStackTop; |
| public: |
| void* m_apiData { nullptr }; |
| RefPtr<ClientData> m_clientData { nullptr }; |
| }; |
| |
| inline Thread::Thread() |
| : m_uid(++s_uid) |
| { |
| } |
| |
| #if !OS(WINDOWS) |
| inline Thread* Thread::currentMayBeNull() |
| { |
| #if !HAVE(FAST_TLS) |
| ASSERT(s_key != InvalidThreadSpecificKey); |
| return static_cast<Thread*>(threadSpecificGet(s_key)); |
| #else |
| return static_cast<Thread*>(_pthread_getspecific_direct(WTF_THREAD_DATA_KEY)); |
| #endif |
| } |
| #endif |
| |
| inline Thread& Thread::current() |
| { |
| // WRT WebCore: |
| // Thread::current() is used on main thread before it could possibly be used |
| // on secondary ones, so there is no need for synchronization here. |
| // WRT JavaScriptCore: |
| // Thread::initializeTLSKey() is initially called from initialize(), ensuring |
| // this is initially called in a std::call_once locked context. |
| #if !HAVE(FAST_TLS) && !OS(WINDOWS) |
| if (UNLIKELY(Thread::s_key == InvalidThreadSpecificKey)) |
| WTF::initialize(); |
| #endif |
| if (auto* thread = currentMayBeNull()) |
| return *thread; |
| return initializeCurrentTLS(); |
| } |
| |
| inline void assertIsCurrent(const Thread& thread) WTF_ASSERTS_ACQUIRED_CAPABILITY(thread) |
| { |
| #if ASSERT_ENABLED |
| ASSERT(&thread == &Thread::current()); |
| #else |
| UNUSED_PARAM(thread); |
| #endif |
| } |
| |
| } // namespace WTF |
| |
| using WTF::ThreadSuspendLocker; |
| using WTF::Thread; |
| using WTF::ThreadType; |
| using WTF::GCThreadType; |
| using WTF::assertIsCurrent; |