shadPS4/src/core/libraries/kernel/threads/pthread.h
Fire Cube 1aa7eb8a42
add scePthreadSetaffinity and emulate affinity (#2885)
* add implementation

* fix preprocessor

* fixes squidbus's comments

* fix clang

* comment became fucked up?

* fix removed return
2025-05-07 14:50:16 -07:00

354 lines
7.8 KiB
C++

// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <forward_list>
#include <list>
#include <mutex>
#include <semaphore>
#include <shared_mutex>
#include "common/enum.h"
#include "core/libraries/kernel/sync/mutex.h"
#include "core/libraries/kernel/sync/semaphore.h"
#include "core/libraries/kernel/time.h"
#include "core/thread.h"
#include "core/tls.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Kernel {
struct Pthread;
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 {
TimedMutex 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<PthreadMutexType>(m_flags & PthreadMutexFlags::TypeMask);
}
int SelfTryLock();
int SelfLock(const OrbisKernelTimespec* abstime, u64 usec);
int TryLock();
int Lock(const OrbisKernelTimespec* abstime, u64 usec = 0);
int CvLock(int recurse) {
const int error = Lock(nullptr);
if (error == 0) {
m_count = recurse;
}
return error;
}
int Unlock();
int CvUnlock(int* recurse) {
*recurse = m_count;
m_count = 0;
return Unlock();
}
int 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 {
u32 has_user_waiters;
u32 has_kern_waiters;
u32 flags;
ClockId clock_id;
std::string name;
int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime, u64 usec = 0);
int Signal(Pthread* thread);
int Broadcast();
};
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,
Other = 2,
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 = 1_MB;
static constexpr u32 ThrStackInitial = 2_MB;
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;
};
using PthreadKeyDestructor = void PS4_SYSV_ABI (*)(const void*);
struct PthreadKey {
int allocated;
int seqno;
PthreadKeyDestructor destructor;
};
using PthreadKeyT = s32;
enum class PthreadOnceState : u32 {
NeverDone = 0,
Done = 1,
InProgress = 2,
Wait = 3,
};
struct PthreadOnce {
std::atomic<PthreadOnceState> 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* PS4_SYSV_ABI (*)(void*);
constexpr u32 TidTerminated = 1;
struct SleepQueue;
struct SchedParam {
int sched_priority;
};
#define THR_RELTIME (const OrbisKernelTimespec*)-1
struct Pthread {
static constexpr u32 ThrMagic = 0xd09ba115U;
static constexpr u32 MaxDeferWaiters = 50;
std::atomic<long> tid;
std::mutex lock;
u32 cycle;
int locklevel;
int critical_count;
int sigblock;
int refcount;
PthreadEntryFunc start_routine;
void* arg;
Core::NativeThread native_thr;
PthreadAttr attr;
bool cancel_enable;
bool cancel_pending;
bool cancel_point;
bool no_cancel;
bool cancel_async;
bool cancelling;
Cpuset sigmask;
bool unblock_sigcancel;
bool in_sigsuspend;
bool force_exit;
PthreadState state;
int error;
Pthread* joiner;
ThreadFlags flags;
ThreadListFlags tlflags;
void* ret;
PthreadSpecificElem* specific;
int specific_data_count;
int rdlock_count;
int rtld_bits;
Core::Tcb* tcb;
std::forward_list<PthreadCleanup*> cleanup;
u32 pad[27];
u32 magic;
int report_events;
int event_mask;
std::string name;
BinarySemaphore wake_sema{0};
SleepQueue* sleepqueue;
void* wchan;
PthreadMutex* mutex_obj;
bool will_sleep;
bool has_user_waiters;
int nwaiter_defer;
BinarySemaphore* defer_waiters[MaxDeferWaiters];
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 WakeAll() {
for (int i = 0; i < nwaiter_defer; i++) {
defer_waiters[i]->release();
}
nwaiter_defer = 0;
}
bool Sleep(const OrbisKernelTimespec* abstime, u64 usec) {
will_sleep = 0;
if (nwaiter_defer > 0) {
WakeAll();
}
if (abstime == THR_RELTIME) {
return wake_sema.try_acquire_for(std::chrono::microseconds(usec));
} else if (abstime != nullptr) {
return wake_sema.try_acquire_until(abstime->TimePoint());
} else {
wake_sema.acquire();
return true;
}
}
int SetAffinity(const Cpuset* cpuset);
};
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);
void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym);
void RegisterPthreadClean(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Kernel