#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/PlatformRegisters.h>
#include <wtf/Ref.h>
#include <wtf/RefPtr.h>
#include <wtf/StackBounds.h>
#include <wtf/StackStats.h>
#include <wtf/ThreadSafeRefCounted.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
namespace WTF {
class AbstractLocker;
class ThreadMessageData;
enum class ThreadGroupAddResult;
class ThreadGroup;
class PrintStream;
WTF_EXPORT_PRIVATE void initialize();
#if USE(PTHREADS)
constexpr const int SigThreadSuspendResume = SIGUSR1;
#endif
enum class GCThreadType : uint8_t {
None = 0,
Main,
Helper,
};
enum class ThreadType : uint8_t {
Unknown = 0,
JavaScript,
Compiler,
GarbageCollection,
Network,
Graphics,
Audio,
};
class Thread : public ThreadSafeRefCounted<Thread> {
public:
friend class ThreadGroup;
friend WTF_EXPORT_PRIVATE void initialize();
WTF_EXPORT_PRIVATE ~Thread();
WTF_EXPORT_PRIVATE static Ref<Thread> create(const char* threadName, Function<void()>&&, ThreadType threadType = ThreadType::Unknown);
static Thread& current();
WTF_EXPORT_PRIVATE static HashSet<Thread*>& allThreads(const LockHolder&);
WTF_EXPORT_PRIVATE static Lock& allThreadsMutex();
WTF_EXPORT_PRIVATE unsigned numberOfThreadGroups();
#if OS(WINDOWS)
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();
WTF_EXPORT_PRIVATE void resume();
WTF_EXPORT_PRIVATE size_t getRegisters(PlatformRegisters&);
#if USE(PTHREADS)
WTF_EXPORT_PRIVATE bool signal(int signalNumber);
#endif
WTF_EXPORT_PRIVATE static void setCurrentThreadIsUserInteractive(int relativePriority = 0);
WTF_EXPORT_PRIVATE static void setCurrentThreadIsUserInitiated(int relativePriority = 0);
#if HAVE(QOS_CLASSES)
WTF_EXPORT_PRIVATE static void setGlobalMaxQOSClass(qos_class_t);
WTF_EXPORT_PRIVATE static qos_class_t adjustedQOSClass(qos_class_t);
#endif
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()
{
return m_savedStackPointerAtVMEntry;
}
void setSavedStackPointerAtVMEntry(void* stackPointerAtVMEntry)
{
m_savedStackPointerAtVMEntry = stackPointerAtVMEntry;
}
void* savedLastStackTop()
{
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();
bool establishHandle(NewThreadContext*, Optional<size_t>);
#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
static const char* normalizeThreadName(const char* threadName);
enum JoinableState : uint8_t {
Joinable,
Joined,
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; }
ThreadGroupAddResult addToThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup&);
void removeFromThreadGroup(const AbstractLocker& threadGroupLocker, ThreadGroup&);
#if !HAVE(FAST_TLS) && !OS(WINDOWS)
static WTF_EXPORT_PRIVATE ThreadSpecificKey s_key;
static void initializeTLSKey();
#endif
static void destructTLS(void* data);
static Thread& initializeTLS(Ref<Thread>&&);
WTF_EXPORT_PRIVATE static Thread& initializeCurrentTLS();
#if OS(WINDOWS)
WTF_EXPORT_PRIVATE static Thread* currentMayBeNull();
#else
static Thread* currentMayBeNull();
#endif
JoinableState m_joinableState { Joinable };
bool m_isShuttingDown : 1;
bool m_didExit : 1;
bool m_isDestroyedOnce : 1;
bool m_isCompilationThread: 1;
unsigned m_gcThreadType : 2;
bool m_didUnregisterFromAllThreads { false };
WordLock m_mutex;
StackBounds m_stack { StackBounds::emptyBounds() };
HashMap<ThreadGroup*, std::weak_ptr<ThreadGroup>> m_threadGroupMap;
PlatformThreadHandle m_handle;
#if OS(WINDOWS)
ThreadIdentifier m_id { 0 };
#elif OS(DARWIN)
mach_port_t m_platformThread { MACH_PORT_NULL };
#elif USE(PTHREADS)
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 };
};
inline Thread::Thread()
: m_isShuttingDown(false)
, m_didExit(false)
, m_isDestroyedOnce(false)
, m_isCompilationThread(false)
, m_gcThreadType(static_cast<unsigned>(GCThreadType::None))
{
}
#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()
{
#if !HAVE(FAST_TLS) && !OS(WINDOWS)
if (UNLIKELY(Thread::s_key == InvalidThreadSpecificKey))
WTF::initialize();
#endif
if (auto* thread = currentMayBeNull())
return *thread;
return initializeCurrentTLS();
}
}
using WTF::Thread;
using WTF::ThreadType;
using WTF::GCThreadType;