// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include #include #include #include "common/enum.h" #include "core/libraries/kernel/time_management.h" #include "core/tls.h" namespace Core::Loader { class SymbolsResolver; } namespace Libraries::Kernel { struct Pthread; using ListBaseHook = boost::intrusive::list_base_hook>; enum class PthreadMutexFlags : u32 { TypeMask = 0xff, Defered = 0x200, }; DECLARE_ENUM_FLAG_OPERATORS(PthreadMutexFlags) enum class PthreadMutexType : u32 { ErrorCheck = 1, Recursive = 2, Normal = 3, AdaptiveNp = 4, Max }; enum class PthreadMutexProt : u32 { None = 0, Inherit = 1, Protect = 2, }; struct PthreadMutex : public ListBaseHook { std::timed_mutex m_lock; PthreadMutexFlags m_flags; Pthread* m_owner; int m_count; int m_spinloops; int m_yieldloops; PthreadMutexProt m_protocol; std::string name; PthreadMutexType Type() const noexcept { return static_cast(m_flags & PthreadMutexFlags::TypeMask); } void lock() { Lock(nullptr); } void unlock() { Unlock(); } int SelfTryLock(); int SelfLock(const OrbisKernelTimespec* abstime, u64 usec); int TryLock(); int Lock(const OrbisKernelTimespec* abstime, u64 usec = 0); int Unlock(); bool IsOwned(Pthread* curthread) const; }; using PthreadMutexT = PthreadMutex*; struct PthreadMutexAttr { PthreadMutexType m_type; PthreadMutexProt m_protocol; int m_ceiling; }; using PthreadMutexAttrT = PthreadMutexAttr*; enum class PthreadCondFlags : u32 { Private = 1, Inited = 2, Busy = 4, }; enum class ClockId : u32 { Realtime = 0, Virtual = 1, Prof = 2, Monotonic = 4, Uptime = 5, UptimePrecise = 7, UptimeFast = 8, RealtimePrecise = 9, RealtimeFast = 10, MonotonicPrecise = 11, MonotonicFast = 12, Second = 13, ThreadCputimeID = 14, }; struct PthreadCond { std::condition_variable_any cond; u32 has_user_waiters; u32 has_kern_waiters; u32 flags; ClockId clock_id; std::string name; int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime); int Wait(PthreadMutexT* mutex, u64 usec); }; using PthreadCondT = PthreadCond*; struct PthreadCondAttr { int c_pshared; ClockId c_clockid; }; using PthreadCondAttrT = PthreadCondAttr*; using PthreadCleanupFunc = void PS4_SYSV_ABI (*)(void*); struct PthreadCleanup { PthreadCleanupFunc routine; void* routine_arg; int onheap; }; enum class PthreadAttrFlags : u32 { Detached = 1, ScopeSystem = 2, InheritSched = 4, NoFloat = 8, StackUser = 0x100, }; DECLARE_ENUM_FLAG_OPERATORS(PthreadAttrFlags) enum class SchedPolicy : u32 { Fifo = 0, RoundRobin = 3, }; struct Cpuset { u64 bits; }; struct PthreadAttr { SchedPolicy sched_policy; int sched_inherit; int prio; int suspend; PthreadAttrFlags flags; void* stackaddr_attr; size_t stacksize_attr; size_t guardsize_attr; size_t cpusetsize; Cpuset* cpuset; }; using PthreadAttrT = PthreadAttr*; static constexpr u32 ThrStackDefault = 2_MB; static constexpr u32 ThrStackInitial = 2 * ThrStackDefault; static constexpr u32 ThrPageSize = 16_KB; static constexpr u32 ThrGuardDefault = ThrPageSize; struct PthreadRwlockAttr { int pshared; }; using PthreadRwlockAttrT = PthreadRwlockAttr*; struct PthreadRwlock { std::shared_timed_mutex lock; Pthread* owner; int Wrlock(const OrbisKernelTimespec* abstime); int Rdlock(const OrbisKernelTimespec* abstime); }; using PthreadRwlockT = PthreadRwlock*; enum class PthreadState : u32 { Running, Dead }; struct PthreadSpecificElem { const void* data; int seqno; }; struct PthreadKey { int allocated; int seqno; void PS4_SYSV_ABI (*destructor)(const void*); }; using PthreadKeyT = s32; enum class PthreadOnceState : u32 { NeverDone = 0, Done = 1, InProgress = 2, Wait = 3, }; struct PthreadOnce { std::atomic state; std::mutex mutex; }; enum class ThreadFlags : u32 { Private = 1, NeedSuspend = 2, Suspended = 4, Detached = 8, }; DECLARE_ENUM_FLAG_OPERATORS(ThreadFlags) enum class ThreadListFlags : u32 { GcSafe = 1, InTdList = 2, InGcList = 4, }; using PthreadEntryFunc = void* (*)(void*); constexpr u32 TidTerminated = 1; struct WakeAddr { WakeAddr* link; u32 value; char pad[12]; }; struct SleepQueue { std::list sq_blocked; std::forward_list sq_freeq; std::list sq_hash; std::forward_list sq_flink; void* sq_wchan; int sq_type; }; struct SchedParam { int sched_priority; }; struct Pthread { static constexpr u32 ThrMagic = 0xd09ba115U; std::atomic tid; std::mutex lock; u32 cycle; int locklevel; int critical_count; int sigblock; int refcount; void* PS4_SYSV_ABI (*start_routine)(void*); void* arg; PthreadAttr attr; bool cancel_enable; bool cancel_pending; bool cancel_point; bool no_cancel; bool cancel_async; bool cancelling; sigset_t sigmask; bool unblock_sigcancel; bool in_sigsuspend; bool force_exit; PthreadState state; int error; Pthread* joiner; ThreadFlags flags; ThreadListFlags tlflags; std::list mutexq; std::list pp_mutexq; void* ret; PthreadSpecificElem* specific; int specific_data_count; int rdlock_count; int rtld_bits; Core::Tcb* tcb; std::forward_list cleanup; u32 pad[27]; u32 magic; int report_events; int event_mask; std::string name; WakeAddr* wake_addr; SleepQueue* sleepqueue; void* wchan; PthreadMutex* mutex_obj; bool will_sleep; bool has_user_waiters; bool InCritical() const noexcept { return locklevel > 0 || critical_count > 0; } bool ShouldCollect() const noexcept { return refcount == 0 && state == PthreadState::Dead && True(flags & ThreadFlags::Detached); } bool ShouldCancel() const noexcept { return cancel_pending && cancel_enable && no_cancel == 0; } void Enqueue(PthreadMutex* mutex) { mutex->m_owner = this; // mutexq.push_back(*mutex); } void Dequeue(PthreadMutex* mutex) { mutex->m_owner = nullptr; // mutexq.erase(decltype(mutexq)::s_iterator_to(*mutex)); } }; using PthreadT = Pthread*; extern thread_local Pthread* g_curthread; void RegisterMutex(Core::Loader::SymbolsResolver* sym); void RegisterCond(Core::Loader::SymbolsResolver* sym); void RegisterRwlock(Core::Loader::SymbolsResolver* sym); void RegisterSemaphore(Core::Loader::SymbolsResolver* sym); void RegisterSpec(Core::Loader::SymbolsResolver* sym); void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym); void RegisterThread(Core::Loader::SymbolsResolver* sym); void RegisterRtld(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel