mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-05-26 13:50:37 +00:00
65986d8a8a
* Fix for SignalTo behavior Should run FindThread to make sure thread input is valid. * Missing mutex-related exports * Oops * All missing cond exports We have these implemented, so we might as well export them. I also organized them a bit. * Implement pthread_attr_getscope, pthread_attr_setcreatesuspend_np, pthread_attr_setscope Also export all of our pthread_attr functions. * Oops * Fix SchedPolicy::Fifo definition Some tricks FreeBSD source performs breaks with this incorrectly set. Specifically, stuff like setting priorities breaks because we do array accesses with priority - 1 like FreeBSD, but Fifo being 0 makes that an oob read. * Fix error checks in pthread_attr_setschedparam * Fix and run thread destructors _sceKernelSetThreadDtors receives the function itself, not a pointer to the function. * Oops * Rewrite pthread_rename_np for accuracy There's a couple details the initial implementation missed that decomp shows. We weren't locking, we weren't doing the reference add or delete ops, and we were erroneously skipping null names when real hardware actually seems to allow it (the only thing real hardware skips for null name is naming the thread stack). * Avoid changing common code by using the converted thread name instead Slightly cleaner code * Oops * Flip priority defines This better matches with FreeBSD's defines, and fixes the returns of sched_get_priority_min and sched_get_priority_max * Fix args and return for sched_get_priority_max and sched_get_priority_min Behavior is based on my own kernel decomp (though what I'm seeing matches what red_prig's done for fpPS4 too). * Better comment why not * Fix error behavior of scePthreadGetPrio Can error from invalid thread input, which the function hardcodes as an ORBIS_KERNEL_ERROR_ESRCH. * Fix pthread_setprio This should use the RefAdd and RefDelete functions on non-curthread threads, not FindThread. * Remove pthread_set_name_np Decomp shows it's identical to pthread_rename_np. * Bring back pthread_set_name_np What I didn't realize was that it was a void method. Behavior is the same as pthread_rename_np though, so call it instead of doing the direct common setname call. * Move pthread_set_name_np Needs to be under pthread_rename_np so I can actually call it. * Better implementation of pthread_getname_np * rwlock types Some Sony extension to rwlocks, paired with a proper SDK check specific to rwlock init. I've implemented both pthread_rwlockattr_gettype_np and pthread_rwlockattr_settype_np, and added the relevant SDK check for rwlockattr type. * Missing thread unlock in SignalTo SignalTo specifically runs FindThread, then unlocks before running pretty much the same code as Signal. * Fix comment
778 lines
27 KiB
C++
778 lines
27 KiB
C++
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "common/assert.h"
|
|
#include "common/thread.h"
|
|
#include "core/debug_state.h"
|
|
#include "core/libraries/kernel/kernel.h"
|
|
#include "core/libraries/kernel/posix_error.h"
|
|
#include "core/libraries/kernel/threads.h"
|
|
#include "core/libraries/kernel/threads/pthread.h"
|
|
#include "core/libraries/kernel/threads/thread_state.h"
|
|
#include "core/libraries/libs.h"
|
|
#include "core/memory.h"
|
|
|
|
extern "C" void* PS4_SYSV_ABI _runOnAnotherStack(void* arg, void* func,
|
|
void* stackb) asm("_runOnAnotherStack");
|
|
|
|
namespace Libraries::Kernel {
|
|
|
|
extern PthreadAttr PthreadAttrDefault;
|
|
|
|
void _thread_cleanupspecific();
|
|
|
|
using ThreadDtor = void PS4_SYSV_ABI (*)();
|
|
static ThreadDtor ThreadDtors{};
|
|
|
|
void PS4_SYSV_ABI _sceKernelSetThreadDtors(ThreadDtor dtor) {
|
|
ThreadDtors = dtor;
|
|
}
|
|
|
|
static void ExitThread() {
|
|
Pthread* curthread = g_curthread;
|
|
|
|
/* Check if there is thread specific data: */
|
|
if (curthread->specific != nullptr) {
|
|
/* Run the thread-specific data destructors: */
|
|
_thread_cleanupspecific();
|
|
}
|
|
|
|
auto* thread_state = ThrState::Instance();
|
|
ASSERT(thread_state->active_threads.fetch_sub(1) != 1);
|
|
|
|
curthread->lock.lock();
|
|
curthread->state = PthreadState::Dead;
|
|
ASSERT(False(curthread->flags & ThreadFlags::NeedSuspend));
|
|
|
|
/*
|
|
* Thread was created with initial refcount 1, we drop the
|
|
* reference count to allow it to be garbage collected.
|
|
*/
|
|
curthread->refcount--;
|
|
thread_state->TryCollect(curthread); /* thread lock released */
|
|
|
|
/*
|
|
* Kernel will do wakeup at the address, so joiner thread
|
|
* will be resumed if it is sleeping at the address.
|
|
*/
|
|
curthread->tid.store(TidTerminated);
|
|
curthread->tid.notify_all();
|
|
|
|
curthread->native_thr.Exit();
|
|
UNREACHABLE();
|
|
/* Never reach! */
|
|
}
|
|
|
|
void PS4_SYSV_ABI posix_pthread_exit(void* status) {
|
|
Pthread* curthread = g_curthread;
|
|
|
|
/* Check if this thread is already in the process of exiting: */
|
|
ASSERT_MSG(!curthread->cancelling, "Thread {} has called pthread_exit from a destructor",
|
|
fmt::ptr(curthread));
|
|
|
|
/* Flag this thread as exiting. */
|
|
curthread->cancelling = true;
|
|
curthread->no_cancel = true;
|
|
curthread->cancel_async = false;
|
|
curthread->cancel_point = false;
|
|
|
|
/* Save the return value: */
|
|
curthread->ret = status;
|
|
while (!curthread->cleanup.empty()) {
|
|
PthreadCleanup* old = curthread->cleanup.front();
|
|
curthread->cleanup.pop_front();
|
|
old->routine(old->routine_arg);
|
|
if (old->onheap) {
|
|
delete old;
|
|
}
|
|
}
|
|
if (ThreadDtors) {
|
|
(ThreadDtors)();
|
|
}
|
|
ExitThread();
|
|
}
|
|
|
|
static int JoinThread(PthreadT pthread, void** thread_return, const OrbisKernelTimespec* abstime) {
|
|
Pthread* curthread = g_curthread;
|
|
|
|
if (pthread == nullptr) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
if (pthread == curthread) {
|
|
return POSIX_EDEADLK;
|
|
}
|
|
|
|
auto* thread_state = ThrState::Instance();
|
|
if (int ret = thread_state->FindThread(pthread, true); ret != 0) {
|
|
return POSIX_ESRCH;
|
|
}
|
|
|
|
int ret = 0;
|
|
if (True(pthread->flags & ThreadFlags::Detached)) {
|
|
ret = POSIX_EINVAL;
|
|
} else if (pthread->joiner != nullptr) {
|
|
/* Multiple joiners are not supported. */
|
|
ret = POSIX_ENOTSUP;
|
|
}
|
|
if (ret) {
|
|
pthread->lock.unlock();
|
|
return ret;
|
|
}
|
|
/* Set the running thread to be the joiner: */
|
|
pthread->joiner = curthread;
|
|
pthread->lock.unlock();
|
|
|
|
const auto backout_join = [](void* arg) PS4_SYSV_ABI {
|
|
auto* pthread2 = static_cast<Pthread*>(arg);
|
|
std::scoped_lock lk{pthread2->lock};
|
|
pthread2->joiner = nullptr;
|
|
};
|
|
|
|
PthreadCleanup cup{backout_join, pthread, 0};
|
|
curthread->cleanup.push_front(&cup);
|
|
|
|
//_thr_cancel_enter(curthread);
|
|
|
|
const s32 tid = pthread->tid;
|
|
while (pthread->tid.load() != TidTerminated) {
|
|
//_thr_testcancel(curthread);
|
|
ASSERT(abstime == nullptr);
|
|
pthread->tid.wait(tid);
|
|
}
|
|
|
|
//_thr_cancel_leave(curthread, 0);
|
|
curthread->cleanup.pop_front();
|
|
|
|
if (ret == POSIX_ETIMEDOUT) {
|
|
backout_join(pthread);
|
|
return ret;
|
|
}
|
|
|
|
void* tmp = pthread->ret;
|
|
pthread->lock.lock();
|
|
pthread->flags |= ThreadFlags::Detached;
|
|
pthread->joiner = nullptr;
|
|
thread_state->TryCollect(pthread); /* thread lock released */
|
|
if (thread_return != nullptr) {
|
|
*thread_return = tmp;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_join(PthreadT pthread, void** thread_return) {
|
|
return JoinThread(pthread, thread_return, nullptr);
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_timedjoin_np(PthreadT pthread, void** thread_return,
|
|
const OrbisKernelTimespec* abstime) {
|
|
if (abstime == nullptr || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
|
|
abstime->tv_nsec >= 1000000000) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
return JoinThread(pthread, thread_return, abstime);
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_detach(PthreadT pthread) {
|
|
if (pthread == nullptr) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
auto* thread_state = ThrState::Instance();
|
|
if (int ret = thread_state->FindThread(pthread, true); ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* Check if the thread is already detached or has a joiner. */
|
|
if (True(pthread->flags & ThreadFlags::Detached) || pthread->joiner != nullptr) {
|
|
pthread->lock.unlock();
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
/* Flag the thread as detached. */
|
|
pthread->flags |= ThreadFlags::Detached;
|
|
thread_state->TryCollect(pthread); /* thread lock released */
|
|
return 0;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
static DWORD RunThread(void* arg) {
|
|
#else
|
|
static void* RunThread(void* arg) {
|
|
#endif
|
|
auto* curthread = static_cast<Pthread*>(arg);
|
|
g_curthread = curthread;
|
|
Common::SetCurrentThreadName(curthread->name.c_str());
|
|
DebugState.AddCurrentThreadToGuestList();
|
|
Core::InitializeTLS();
|
|
|
|
curthread->native_thr.Initialize();
|
|
|
|
/* Run the current thread's start routine with argument: */
|
|
auto* const stack =
|
|
(void*)(((size_t)curthread->attr.stackaddr_attr + curthread->attr.stacksize_attr) & (~15));
|
|
void* ret = _runOnAnotherStack(curthread->arg, (void*)curthread->start_routine, stack);
|
|
|
|
/* Remove thread from tracking */
|
|
DebugState.RemoveCurrentThreadFromGuestList();
|
|
posix_pthread_exit(ret);
|
|
#ifdef WIN32
|
|
return 0;
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAttrT* attr,
|
|
PthreadEntryFunc start_routine, void* arg,
|
|
const char* name) {
|
|
Pthread* curthread = g_curthread;
|
|
auto* thread_state = ThrState::Instance();
|
|
Pthread* new_thread = thread_state->Alloc(curthread);
|
|
if (new_thread == nullptr) {
|
|
return POSIX_EAGAIN;
|
|
}
|
|
|
|
if (attr == nullptr || *attr == nullptr) {
|
|
new_thread->attr = PthreadAttrDefault;
|
|
} else {
|
|
new_thread->attr = *(*attr);
|
|
new_thread->attr.cpusetsize = 0;
|
|
}
|
|
if (curthread != nullptr && new_thread->attr.sched_inherit == PthreadInheritSched) {
|
|
if (True(curthread->attr.flags & PthreadAttrFlags::ScopeSystem)) {
|
|
new_thread->attr.flags |= PthreadAttrFlags::ScopeSystem;
|
|
} else {
|
|
new_thread->attr.flags &= ~PthreadAttrFlags::ScopeSystem;
|
|
}
|
|
new_thread->attr.prio = curthread->attr.prio;
|
|
new_thread->attr.sched_policy = curthread->attr.sched_policy;
|
|
}
|
|
|
|
static std::atomic<int> TidCounter = 1;
|
|
new_thread->tid = ++TidCounter;
|
|
|
|
if (new_thread->attr.stackaddr_attr == nullptr) {
|
|
/* Add additional stack space for HLE */
|
|
static constexpr size_t AdditionalStack = 128_KB;
|
|
new_thread->attr.stacksize_attr += AdditionalStack;
|
|
}
|
|
|
|
if (thread_state->CreateStack(&new_thread->attr) != 0) {
|
|
/* Insufficient memory to create a stack: */
|
|
thread_state->Free(curthread, new_thread);
|
|
return POSIX_EAGAIN;
|
|
}
|
|
|
|
/*
|
|
* Write a magic value to the thread structure
|
|
* to help identify valid ones:
|
|
*/
|
|
new_thread->magic = Pthread::ThrMagic;
|
|
new_thread->start_routine = start_routine;
|
|
new_thread->arg = arg;
|
|
new_thread->cancel_enable = true;
|
|
new_thread->cancel_async = false;
|
|
|
|
auto* memory = Core::Memory::Instance();
|
|
if (name && memory->IsValidMapping(reinterpret_cast<VAddr>(name))) {
|
|
new_thread->name = name;
|
|
} else {
|
|
new_thread->name = fmt::format("Thread{}", new_thread->tid.load());
|
|
}
|
|
|
|
ASSERT(new_thread->attr.suspend == 0);
|
|
new_thread->state = PthreadState::Running;
|
|
|
|
if (True(new_thread->attr.flags & PthreadAttrFlags::Detached)) {
|
|
new_thread->flags |= ThreadFlags::Detached;
|
|
}
|
|
|
|
/* Add the new thread. */
|
|
new_thread->refcount = 1;
|
|
thread_state->Link(curthread, new_thread);
|
|
|
|
/* Return thread pointer eariler so that new thread can use it. */
|
|
(*thread) = new_thread;
|
|
|
|
/* Create thread */
|
|
new_thread->native_thr = Core::NativeThread();
|
|
int ret = new_thread->native_thr.Create(RunThread, new_thread);
|
|
|
|
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
|
|
|
|
if (attr != nullptr && *attr != nullptr && (*attr)->cpuset != nullptr) {
|
|
new_thread->SetAffinity((*attr)->cpuset);
|
|
}
|
|
if (ret) {
|
|
*thread = nullptr;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_create(PthreadT* thread, const PthreadAttrT* attr,
|
|
PthreadEntryFunc start_routine, void* arg) {
|
|
return posix_pthread_create_name_np(thread, attr, start_routine, arg, nullptr);
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_getthreadid_np() {
|
|
return g_curthread->tid;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_getname_np(PthreadT thread, char* name) {
|
|
if (thread == g_curthread) {
|
|
// Can skip locking and reference logic if thread is curthread.
|
|
std::memcpy(name, thread->name.data(), std::min<size_t>(thread->name.size(), 32));
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
// Find the thread in the list of active threads.
|
|
auto* thread_state = ThrState::Instance();
|
|
if (int ret = thread_state->RefAdd(thread, false); ret != 0) {
|
|
return POSIX_ESRCH;
|
|
}
|
|
|
|
// Lock the thread.
|
|
thread->lock.lock();
|
|
|
|
// Get the thread name
|
|
if (thread->state != PthreadState::Dead) {
|
|
std::memcpy(name, thread->name.data(), std::min<size_t>(thread->name.size(), 32));
|
|
}
|
|
|
|
// Unlock and remove reference.
|
|
thread->lock.unlock();
|
|
thread_state->RefDelete(thread);
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_equal(PthreadT thread1, PthreadT thread2) {
|
|
return (thread1 == thread2 ? 1 : 0);
|
|
}
|
|
|
|
PthreadT PS4_SYSV_ABI posix_pthread_self() {
|
|
return g_curthread;
|
|
}
|
|
|
|
void PS4_SYSV_ABI posix_pthread_yield() {
|
|
std::this_thread::yield();
|
|
}
|
|
|
|
void PS4_SYSV_ABI sched_yield() {
|
|
std::this_thread::yield();
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_getpid() {
|
|
return GLOBAL_PID;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control,
|
|
void PS4_SYSV_ABI (*init_routine)()) {
|
|
for (;;) {
|
|
auto state = once_control->state.load();
|
|
if (state == PthreadOnceState::Done) {
|
|
return 0;
|
|
}
|
|
if (state == PthreadOnceState::NeverDone) {
|
|
if (once_control->state.compare_exchange_strong(state, PthreadOnceState::InProgress,
|
|
std::memory_order_acquire)) {
|
|
break;
|
|
}
|
|
} else if (state == PthreadOnceState::InProgress) {
|
|
if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Wait,
|
|
std::memory_order_acquire)) {
|
|
once_control->state.wait(PthreadOnceState::Wait);
|
|
}
|
|
} else if (state == PthreadOnceState::Wait) {
|
|
once_control->state.wait(state);
|
|
} else {
|
|
return POSIX_EINVAL;
|
|
}
|
|
}
|
|
|
|
const auto once_cancel_handler = [](void* arg) PS4_SYSV_ABI {
|
|
auto* once_control2 = static_cast<PthreadOnce*>(arg);
|
|
auto state = PthreadOnceState::InProgress;
|
|
if (once_control2->state.compare_exchange_strong(state, PthreadOnceState::NeverDone,
|
|
std::memory_order_release)) {
|
|
return;
|
|
}
|
|
|
|
once_control2->state.store(PthreadOnceState::NeverDone, std::memory_order_release);
|
|
once_control2->state.notify_all();
|
|
};
|
|
|
|
PthreadCleanup cup{once_cancel_handler, once_control, 0};
|
|
g_curthread->cleanup.push_front(&cup);
|
|
init_routine();
|
|
g_curthread->cleanup.pop_front();
|
|
|
|
auto state = PthreadOnceState::InProgress;
|
|
if (once_control->state.compare_exchange_strong(state, PthreadOnceState::Done,
|
|
std::memory_order_release)) {
|
|
return 0;
|
|
}
|
|
once_control->state.store(PthreadOnceState::Done);
|
|
once_control->state.notify_all();
|
|
return 0;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_sched_get_priority_max(SchedPolicy policy) {
|
|
if (policy != SchedPolicy::Fifo && policy != SchedPolicy::RoundRobin) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
return ORBIS_KERNEL_PRIO_FIFO_HIGHEST;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_sched_get_priority_min(SchedPolicy policy) {
|
|
if (policy != SchedPolicy::Fifo && policy != SchedPolicy::RoundRobin) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
return ORBIS_KERNEL_PRIO_FIFO_LOWEST;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) {
|
|
LOG_INFO(Kernel_Pthread, "name = {}", name ? name : "(null)");
|
|
auto* thread_state = ThrState::Instance();
|
|
auto* memory = Core::Memory::Instance();
|
|
|
|
if (thread == g_curthread) {
|
|
// If the requested thread is curthread, skip locking and reference logic.
|
|
thread->name = name ? name : std::string{""};
|
|
Common::SetThreadName(reinterpret_cast<void*>(thread->native_thr.GetHandle()),
|
|
thread->name.data());
|
|
if (name && False(thread->attr.flags & PthreadAttrFlags::StackUser)) {
|
|
VAddr stack_addr = std::bit_cast<VAddr>(thread->attr.stackaddr_attr);
|
|
memory->NameVirtualRange(stack_addr, thread->attr.stacksize_attr, name);
|
|
}
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
// Find the thread in the list of active threads.
|
|
if (int ret = thread_state->RefAdd(thread, false); ret != 0) {
|
|
return POSIX_ESRCH;
|
|
}
|
|
|
|
// Lock the thread.
|
|
thread->lock.lock();
|
|
|
|
// Set the thread and thread stack names.
|
|
if (thread->state != PthreadState::Dead) {
|
|
thread->name = name ? name : std::string{""};
|
|
Common::SetThreadName(reinterpret_cast<void*>(thread->native_thr.GetHandle()),
|
|
thread->name.data());
|
|
if (name && False(thread->attr.flags & PthreadAttrFlags::StackUser)) {
|
|
VAddr stack_addr = std::bit_cast<VAddr>(thread->attr.stackaddr_attr);
|
|
memory->NameVirtualRange(stack_addr, thread->attr.stacksize_attr, name);
|
|
}
|
|
}
|
|
|
|
// Unlock and remove reference.
|
|
thread->lock.unlock();
|
|
thread_state->RefDelete(thread);
|
|
return ORBIS_OK;
|
|
}
|
|
|
|
void PS4_SYSV_ABI posix_pthread_set_name_np(PthreadT thread, const char* name) {
|
|
posix_pthread_rename_np(thread, name);
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_getschedparam(PthreadT pthread, SchedPolicy* policy,
|
|
SchedParam* param) {
|
|
if (policy == nullptr || param == nullptr) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
if (pthread == g_curthread) {
|
|
/*
|
|
* Avoid searching the thread list when it is the current
|
|
* thread.
|
|
*/
|
|
std::scoped_lock lk{g_curthread->lock};
|
|
*policy = g_curthread->attr.sched_policy;
|
|
param->sched_priority = g_curthread->attr.prio;
|
|
return 0;
|
|
}
|
|
auto* thread_state = ThrState::Instance();
|
|
/* Find the thread in the list of active threads. */
|
|
if (int ret = thread_state->RefAdd(pthread, /*include dead*/ false); ret != 0) {
|
|
return ret;
|
|
}
|
|
pthread->lock.lock();
|
|
*policy = pthread->attr.sched_policy;
|
|
param->sched_priority = pthread->attr.prio;
|
|
pthread->lock.unlock();
|
|
thread_state->RefDelete(pthread);
|
|
return 0;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_setschedparam(PthreadT pthread, SchedPolicy policy,
|
|
const SchedParam* param) {
|
|
if (pthread == nullptr || param == nullptr) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
auto* thread_state = ThrState::Instance();
|
|
if (pthread == g_curthread) {
|
|
g_curthread->lock.lock();
|
|
} else if (int ret = thread_state->FindThread(pthread, /*include dead*/ false); ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (pthread->attr.sched_policy == policy &&
|
|
(policy == SchedPolicy::Other || pthread->attr.prio == param->sched_priority)) {
|
|
pthread->attr.prio = param->sched_priority;
|
|
pthread->lock.unlock();
|
|
return 0;
|
|
}
|
|
|
|
// TODO: _thr_setscheduler
|
|
pthread->attr.sched_policy = policy;
|
|
pthread->attr.prio = param->sched_priority;
|
|
pthread->lock.unlock();
|
|
return 0;
|
|
}
|
|
|
|
int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) {
|
|
SchedParam param;
|
|
SchedPolicy policy;
|
|
|
|
int ret = posix_pthread_getschedparam(thread, &policy, ¶m);
|
|
if (ret != 0) {
|
|
return ORBIS_KERNEL_ERROR_ESRCH;
|
|
}
|
|
*priority = param.sched_priority;
|
|
return 0;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_setprio(PthreadT thread, int prio) {
|
|
SchedParam param;
|
|
param.sched_priority = prio;
|
|
|
|
auto* thread_state = ThrState::Instance();
|
|
if (thread != g_curthread) {
|
|
const int ret = thread_state->RefAdd(thread, /*include dead*/ false);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
thread->lock.lock();
|
|
if (thread->attr.sched_policy == SchedPolicy::Other || thread->attr.prio == prio) {
|
|
thread->attr.prio = prio;
|
|
} else {
|
|
// TODO: _thr_setscheduler
|
|
thread->attr.prio = prio;
|
|
}
|
|
|
|
thread->lock.unlock();
|
|
if (thread != g_curthread) {
|
|
thread_state->RefDelete(thread);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
enum class PthreadCancelState : u32 {
|
|
Enable = 0,
|
|
Disable = 1,
|
|
};
|
|
|
|
#define POSIX_PTHREAD_CANCELED ((void*)1)
|
|
|
|
static inline void TestCancel(const Pthread* curthread) {
|
|
if (curthread->ShouldCancel() && !curthread->InCritical()) [[unlikely]] {
|
|
posix_pthread_exit(POSIX_PTHREAD_CANCELED);
|
|
}
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_setcancelstate(PthreadCancelState state,
|
|
PthreadCancelState* oldstate) {
|
|
Pthread* curthread = g_curthread;
|
|
int oldval = curthread->cancel_enable;
|
|
switch (state) {
|
|
case PthreadCancelState::Disable:
|
|
curthread->cancel_enable = false;
|
|
break;
|
|
case PthreadCancelState::Enable:
|
|
curthread->cancel_enable = true;
|
|
TestCancel(curthread);
|
|
break;
|
|
default:
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
if (oldstate) {
|
|
*oldstate = oldval ? PthreadCancelState::Enable : PthreadCancelState::Disable;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Pthread::SetAffinity(const Cpuset* cpuset) {
|
|
const auto processor_count = std::thread::hardware_concurrency();
|
|
if (processor_count < 8) {
|
|
return 0;
|
|
}
|
|
if (cpuset == nullptr) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
uintptr_t handle = native_thr.GetHandle();
|
|
if (handle == 0) {
|
|
return POSIX_ESRCH;
|
|
}
|
|
|
|
// We don't use this currently because some games gets performance problems
|
|
// when applying affinity even on strong hardware
|
|
/*
|
|
u64 mask = cpuset->bits;
|
|
#ifdef _WIN64
|
|
DWORD_PTR affinity_mask = static_cast<DWORD_PTR>(mask);
|
|
if (!SetThreadAffinityMask(reinterpret_cast<HANDLE>(handle), affinity_mask)) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
#elif defined(__linux__)
|
|
cpu_set_t cpu_set;
|
|
CPU_ZERO(&cpu_set);
|
|
|
|
u64 mask = cpuset->bits;
|
|
for (int cpu = 0; cpu < std::min(64, CPU_SETSIZE); ++cpu) {
|
|
if (mask & (1ULL << cpu)) {
|
|
CPU_SET(cpu, &cpu_set);
|
|
}
|
|
}
|
|
|
|
int result =
|
|
pthread_setaffinity_np(static_cast<pthread_t>(handle), sizeof(cpu_set_t), &cpu_set);
|
|
if (result != 0) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
#endif
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize, Cpuset* cpusetp) {
|
|
if (thread == nullptr || cpusetp == nullptr) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
auto* thread_state = ThrState::Instance();
|
|
if (thread == g_curthread) {
|
|
g_curthread->lock.lock();
|
|
} else if (const auto ret = thread_state->FindThread(thread, /*include dead*/ false);
|
|
ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
auto* attr_ptr = &thread->attr;
|
|
auto ret = posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp);
|
|
|
|
thread->lock.unlock();
|
|
return ret;
|
|
}
|
|
|
|
int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize,
|
|
const Cpuset* cpusetp) {
|
|
if (thread == nullptr || cpusetp == nullptr) {
|
|
return POSIX_EINVAL;
|
|
}
|
|
|
|
auto* thread_state = ThrState::Instance();
|
|
if (thread == g_curthread) {
|
|
g_curthread->lock.lock();
|
|
} else if (const auto ret = thread_state->FindThread(thread, /*include dead*/ false);
|
|
ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
auto* attr_ptr = &thread->attr;
|
|
auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp);
|
|
|
|
if (ret == ORBIS_OK) {
|
|
ret = thread->SetAffinity(thread->attr.cpuset);
|
|
}
|
|
|
|
thread->lock.unlock();
|
|
return ret;
|
|
}
|
|
|
|
int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) {
|
|
Cpuset cpuset;
|
|
const int ret = posix_pthread_getaffinity_np(thread, sizeof(Cpuset), &cpuset);
|
|
if (ret == 0) {
|
|
*mask = cpuset.bits;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int PS4_SYSV_ABI scePthreadSetaffinity(PthreadT thread, const u64 mask) {
|
|
const Cpuset cpuset = {.bits = mask};
|
|
return posix_pthread_setaffinity_np(thread, sizeof(Cpuset), &cpuset);
|
|
}
|
|
|
|
void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
|
// Posix
|
|
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", posix_pthread_once);
|
|
LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", posix_pthread_equal);
|
|
LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", posix_sched_get_priority_max);
|
|
LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", posix_sched_get_priority_min);
|
|
LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", posix_pthread_self);
|
|
LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", posix_pthread_yield);
|
|
LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", posix_pthread_detach);
|
|
LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", posix_pthread_exit);
|
|
LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", posix_pthread_join);
|
|
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", posix_pthread_create);
|
|
LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", posix_pthread_create_name_np);
|
|
LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", posix_pthread_setcancelstate);
|
|
LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", posix_pthread_setprio);
|
|
LIB_FUNCTION("9vyP6Z7bqzc", "libScePosix", 1, "libkernel", posix_pthread_rename_np);
|
|
LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", posix_pthread_getschedparam);
|
|
LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", posix_pthread_setschedparam);
|
|
LIB_FUNCTION("6XG4B33N09g", "libScePosix", 1, "libkernel", sched_yield);
|
|
LIB_FUNCTION("HoLVWNanBBc", "libScePosix", 1, "libkernel", posix_getpid);
|
|
|
|
// Posix-Kernel
|
|
LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", posix_pthread_once);
|
|
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", posix_pthread_self);
|
|
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", posix_pthread_create);
|
|
LIB_FUNCTION("Jmi+9w9u0E4", "libkernel", 1, "libkernel", posix_pthread_create_name_np);
|
|
LIB_FUNCTION("lZzFeSxPl08", "libkernel", 1, "libkernel", posix_pthread_setcancelstate);
|
|
LIB_FUNCTION("CBNtXOoef-E", "libkernel", 1, "libkernel", posix_sched_get_priority_max);
|
|
LIB_FUNCTION("m0iS6jNsXds", "libkernel", 1, "libkernel", posix_sched_get_priority_min);
|
|
LIB_FUNCTION("Xs9hdiD7sAA", "libkernel", 1, "libkernel", posix_pthread_setschedparam);
|
|
LIB_FUNCTION("+U1R4WtXvoc", "libkernel", 1, "libkernel", posix_pthread_detach);
|
|
LIB_FUNCTION("7Xl257M4VNI", "libkernel", 1, "libkernel", posix_pthread_equal);
|
|
LIB_FUNCTION("h9CcP3J0oVM", "libkernel", 1, "libkernel", posix_pthread_join);
|
|
LIB_FUNCTION("Jb2uGFMr688", "libkernel", 1, "libkernel", posix_pthread_getaffinity_np);
|
|
LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", posix_pthread_setaffinity_np);
|
|
LIB_FUNCTION("3eqs37G74-s", "libkernel", 1, "libkernel", posix_pthread_getthreadid_np);
|
|
|
|
// Orbis
|
|
LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", ORBIS(posix_pthread_once));
|
|
LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", ORBIS(posix_pthread_rename_np));
|
|
LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", ORBIS(posix_pthread_create_name_np));
|
|
LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", ORBIS(posix_pthread_detach));
|
|
LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", ORBIS(posix_pthread_join));
|
|
LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", ORBIS(posix_pthread_getschedparam));
|
|
LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", ORBIS(posix_pthread_setschedparam));
|
|
LIB_FUNCTION("How7B8Oet6k", "libkernel", 1, "libkernel", ORBIS(posix_pthread_getname_np));
|
|
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", posix_pthread_exit);
|
|
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", posix_pthread_self);
|
|
LIB_FUNCTION("oxMp8uPqa+U", "libkernel", 1, "libkernel", posix_pthread_set_name_np);
|
|
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", posix_pthread_equal);
|
|
LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", posix_pthread_yield);
|
|
LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", posix_pthread_getthreadid_np);
|
|
LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", scePthreadGetprio);
|
|
LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", ORBIS(posix_pthread_setprio));
|
|
LIB_FUNCTION("rNhWz+lvOMU", "libkernel", 1, "libkernel", _sceKernelSetThreadDtors);
|
|
LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", sched_yield);
|
|
LIB_FUNCTION("HoLVWNanBBc", "libkernel", 1, "libkernel", posix_getpid);
|
|
LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", ORBIS(scePthreadGetaffinity));
|
|
LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", ORBIS(scePthreadSetaffinity));
|
|
}
|
|
|
|
} // namespace Libraries::Kernel
|