diff --git a/CMakeLists.txt b/CMakeLists.txt index d320d49e..d50bd853 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,10 +212,14 @@ set(KERNEL_LIB src/core/libraries/kernel/event_flag/event_flag.cpp src/core/libraries/kernel/event_flag/event_flag.h src/core/libraries/kernel/event_flag/event_flag_obj.cpp src/core/libraries/kernel/event_flag/event_flag_obj.h - src/core/libraries/kernel/threads/rwlock.cpp - src/core/libraries/kernel/threads/semaphore.cpp - src/core/libraries/kernel/threads/keys.cpp + src/core/libraries/kernel/threads/thr_attr.cpp + src/core/libraries/kernel/threads/thr_cond.cpp + src/core/libraries/kernel/threads/thr_create.cpp src/core/libraries/kernel/threads/threads.h + src/core/libraries/kernel/threads/thr_mutex.cpp + src/core/libraries/kernel/threads/thr_rwlock.cpp + src/core/libraries/kernel/threads/thr_sem.cpp + src/core/libraries/kernel/threads/thr_spec.cpp src/core/libraries/kernel/cpu_management.cpp src/core/libraries/kernel/cpu_management.h src/core/libraries/kernel/event_queue.cpp @@ -812,6 +816,11 @@ else() src/emulator.h src/sdl_window.h src/sdl_window.cpp + src/core/libraries/kernel/threads/thr_stack.cpp + src/core/libraries/kernel/threads/thr_exit.cpp + src/core/libraries/kernel/threads/thr_clean.cpp + src/core/libraries/kernel/threads/thread_state.h src/core/libraries/kernel/threads/thread_state.cpp + src/core/libraries/kernel/threads/thr_ctrdtr.cpp ) endif() diff --git a/src/core/file_sys/file.cpp b/src/core/file_sys/file.cpp new file mode 100644 index 00000000..e0660607 --- /dev/null +++ b/src/core/file_sys/file.cpp @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/error.h" +#include "core/file_sys/file.h" + +#ifdef _WIN64 +#include "common/ntapi.h" +#include +#include +#include +#endif + +namespace Core::FileSys { + +#ifdef _WIN64 + +int File::Open(const std::filesystem::path& path, Common::FS::FileAccessMode f_access) { + DWORD access{}; + if (f_access == Common::FS::FileAccessMode::Read) { + access = GENERIC_READ; + } else if (f_access == Common::FS::FileAccessMode::Write) { + access = GENERIC_WRITE; + } else if (f_access == Common::FS::FileAccessMode::ReadWrite) { + access = GENERIC_READ | GENERIC_WRITE; + } else { + UNREACHABLE(); + } + handle = CreateFileW(path.native().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) { + return ENOENT; + } +} + +s64 File::Read(void* buf, size_t nbytes) { + DWORD bytes_read; + if (!ReadFile(handle, buf, nbytes, &bytes_read, nullptr)) { + UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_read; +} + +s64 File::Pread(void* buf, size_t nbytes, s64 offset) { + OVERLAPPED ol{}; + ol.Offset = offset; + ol.OffsetHigh = offset >> 32; + DWORD bytes_read; + if (!ReadFile(handle, buf, nbytes, &bytes_read, &ol)) { + UNREACHABLE_MSG("ReadFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_read; +} + +s64 File::Write(const void* buf, size_t nbytes) { + DWORD bytes_written; + if (!WriteFile(handle, buf, nbytes, &bytes_written, nullptr)) { + UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_written; +} + +s64 File::Pwrite(const void* buf, size_t nbytes, s64 offset) { + OVERLAPPED ol{}; + ol.Offset = offset; + ol.OffsetHigh = offset >> 32; + DWORD bytes_written; + if (!WriteFile(handle, buf, nbytes, &bytes_written, &ol)) { + UNREACHABLE_MSG("WriteFile failed: {}", Common::GetLastErrorMsg()); + } + return bytes_written; +} + +void File::SetSize(s64 size) { + Lseek(size, 0); + if (!SetEndOfFile(handle)) { + UNREACHABLE_MSG("SetEndOfFile failed: {}", Common::GetLastErrorMsg()); + } +} + +void File::Flush() { + FlushFileBuffers(handle); +} + +s64 File::Lseek(s64 offset, int whence) { + LARGE_INTEGER new_file_pointer; + DWORD origin{}; + if (whence == 0) { + origin = FILE_BEGIN; + } else if (whence == 1) { + origin = FILE_CURRENT; + } else if (whence == 2) { + origin = FILE_END; + } + if (!SetFilePointerEx(handle, LARGE_INTEGER{.QuadPart = offset}, &new_file_pointer, origin)) { + UNREACHABLE_MSG("SetFilePointerEx failed: {}", Common::GetLastErrorMsg()); + } + return new_file_pointer.QuadPart; +} + +void File::Unlink() { + FILE_DISPOSITION_INFORMATION disposition; + IO_STATUS_BLOCK iosb; + disposition.DeleteFile = TRUE; + NtSetInformationFile(handle, &iosb, &disposition, sizeof(disposition), + FileDispositionInformation); +} + +#else + +#endif + +} // namespace Core::FileSys \ No newline at end of file diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 49f5c0eb..223034e9 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -289,6 +289,15 @@ void PS4_SYSV_ABI sched_yield() { return std::this_thread::yield(); } +struct OrbisKernelUuid { + u32 timeLow; + u16 timeMid; + u16 timeHiAndVersion; + u8 clockSeqHiAndReserved; + u8 clockSeqLow; + u8 node[6]; +}; + int PS4_SYSV_ABI sceKernelUuidCreate(OrbisKernelUuid* orbisUuid) { #ifdef _WIN64 UUID uuid; @@ -324,7 +333,7 @@ int PS4_SYSV_ABI posix_getpagesize() { return 4096; } -void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { +void RegisterKernel(Core::Loader::SymbolsResolver* sym) { service_thread = std::jthread{KernelServiceThread}; Libraries::Kernel::RegisterFileSystem(sym); diff --git a/src/core/libraries/kernel/libkernel.h b/src/core/libraries/kernel/libkernel.h index 286f5481..235fbc48 100644 --- a/src/core/libraries/kernel/libkernel.h +++ b/src/core/libraries/kernel/libkernel.h @@ -4,8 +4,8 @@ #pragma once #include - #include "common/types.h" +#include "core/libraries/error_codes.h" namespace Core::Loader { class SymbolsResolver; @@ -17,17 +17,27 @@ void ErrSceToPosix(int result); int ErrnoToSceKernelError(int e); void SetPosixErrno(int e); -typedef struct { - uint32_t timeLow; - uint16_t timeMid; - uint16_t timeHiAndVersion; - uint8_t clockSeqHiAndReserved; - uint8_t clockSeqLow; - uint8_t node[6]; -} OrbisKernelUuid; +template +struct WrapperImpl; + +template +struct WrapperImpl { + static R PS4_SYSV_ABI wrap(Args... args) { + u32 ret = f(args...); + if (ret != 0) { + ret += SCE_KERNEL_ERROR_UNKNOWN; + } + return ret; + } +}; + +template +constexpr auto OrbisWrapper = WrapperImpl::wrap; + +#define ORBIS(func) OrbisWrapper int* PS4_SYSV_ABI __Error(); -void LibKernel_Register(Core::Loader::SymbolsResolver* sym); +void RegisterKernel(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 7575354d..7a81ac79 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -1,1716 +1,19 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include - -#include "common/alignment.h" -#include "common/assert.h" -#include "common/error.h" -#include "common/logging/log.h" -#include "common/singleton.h" -#include "common/thread.h" -#include "core/debug_state.h" -#include "core/libraries/error_codes.h" #include "core/libraries/kernel/libkernel.h" #include "core/libraries/kernel/thread_management.h" #include "core/libraries/kernel/threads/threads.h" -#include "core/libraries/libs.h" -#include "core/linker.h" -#include "core/tls.h" -#ifdef _WIN64 -#include -#else -#include -#endif namespace Libraries::Kernel { -thread_local ScePthread g_pthread_self{}; -PThreadCxt* g_pthread_cxt = nullptr; - -void init_pthreads() { - g_pthread_cxt = new PThreadCxt{}; - // default mutex init - ScePthreadMutexattr default_mutexattr = nullptr; - scePthreadMutexattrInit(&default_mutexattr); - g_pthread_cxt->setDefaultMutexattr(default_mutexattr); - ScePthreadMutexattr adaptive_mutexattr = nullptr; - scePthreadMutexattrInit(&adaptive_mutexattr); - scePthreadMutexattrSettype(&adaptive_mutexattr, ORBIS_PTHREAD_MUTEX_ADAPTIVE); - g_pthread_cxt->setAdaptiveMutexattr(adaptive_mutexattr); - // default cond init - ScePthreadCondattr default_condattr = nullptr; - scePthreadCondattrInit(&default_condattr); - g_pthread_cxt->setDefaultCondattr(default_condattr); - // default attr init - ScePthreadAttr default_attr = nullptr; - scePthreadAttrInit(&default_attr); - g_pthread_cxt->SetDefaultAttr(default_attr); - // default rw init - OrbisPthreadRwlockattr default_rwattr = nullptr; - scePthreadRwlockattrInit(&default_rwattr); - g_pthread_cxt->setDefaultRwattr(default_rwattr); - - g_pthread_cxt->SetPthreadPool(new PThreadPool); -} - -void pthreadInitSelfMainThread() { - const char* name = "Main_Thread"; - auto* pthread_pool = g_pthread_cxt->GetPthreadPool(); - g_pthread_self = pthread_pool->Create(name); - scePthreadAttrInit(&g_pthread_self->attr); - g_pthread_self->pth = pthread_self(); - g_pthread_self->name = name; -} - -int PS4_SYSV_ABI scePthreadAttrInit(ScePthreadAttr* attr) { - *attr = new PthreadAttrInternal{}; - - int result = pthread_attr_init(&(*attr)->pth_attr); - - (*attr)->affinity = 0x7f; - (*attr)->guard_size = 0x1000; - - SceKernelSchedParam param{}; - param.sched_priority = 700; - - result = (result == 0 ? scePthreadAttrSetinheritsched(attr, 4) : result); - result = (result == 0 ? scePthreadAttrSetschedparam(attr, ¶m) : result); - result = (result == 0 ? scePthreadAttrSetschedpolicy(attr, SCHED_OTHER) : result); - result = (result == 0 ? scePthreadAttrSetdetachstate(attr, PTHREAD_CREATE_JOINABLE) : result); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadAttrDestroy(ScePthreadAttr* attr) { - - int result = pthread_attr_destroy(&(*attr)->pth_attr); - - delete *attr; - *attr = nullptr; - - if (result == 0) { - return SCE_OK; - } - return SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetguardsize(ScePthreadAttr* attr, size_t guard_size) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - (*attr)->guard_size = guard_size; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetguardsize(const ScePthreadAttr* attr, size_t* guard_size) { - if (guard_size == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - *guard_size = (*attr)->guard_size; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetinheritsched(const ScePthreadAttr* attr, int* inherit_sched) { - - if (inherit_sched == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getinheritsched(&(*attr)->pth_attr, inherit_sched); - - switch (*inherit_sched) { - case PTHREAD_EXPLICIT_SCHED: - *inherit_sched = 0; - break; - case PTHREAD_INHERIT_SCHED: - *inherit_sched = 4; - break; - default: - UNREACHABLE(); - } - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadAttrGetdetachstate(const ScePthreadAttr* attr, int* state) { - if (state == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - // int result = pthread_attr_getdetachstate(&(*attr)->pth_attr, state); - int result = 0; - *state = ((*attr)->detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE); - - switch (*state) { - case PTHREAD_CREATE_JOINABLE: - *state = 0; - break; - case PTHREAD_CREATE_DETACHED: - *state = 1; - break; - default: - UNREACHABLE(); - } - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadAttrSetdetachstate(ScePthreadAttr* attr, int detachstate) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int pstate = PTHREAD_CREATE_JOINABLE; - switch (detachstate) { - case 0: - pstate = PTHREAD_CREATE_JOINABLE; - break; - case 1: - pstate = PTHREAD_CREATE_DETACHED; - break; - default: - UNREACHABLE_MSG("Invalid detachstate: {}", detachstate); - } - - // int result = pthread_attr_setdetachstate(&(*attr)->pth_attr, pstate); - int result = 0; - (*attr)->detached = (pstate == PTHREAD_CREATE_DETACHED); - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetinheritsched(ScePthreadAttr* attr, int inheritSched) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int pinherit_sched = PTHREAD_INHERIT_SCHED; - switch (inheritSched) { - case 0: - pinherit_sched = PTHREAD_EXPLICIT_SCHED; - break; - case 4: - pinherit_sched = PTHREAD_INHERIT_SCHED; - break; - default: - UNREACHABLE_MSG("Invalid inheritSched: {}", inheritSched); - } - - int result = pthread_attr_setinheritsched(&(*attr)->pth_attr, pinherit_sched); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetschedparam(const ScePthreadAttr* attr, - SceKernelSchedParam* param) { - - if (param == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getschedparam(&(*attr)->pth_attr, param); - - if (param->sched_priority <= -2) { - param->sched_priority = 767; - } else if (param->sched_priority >= +2) { - param->sched_priority = 256; - } else { - param->sched_priority = 700; - } - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param) { - if (param == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - SceKernelSchedParam pparam{}; - if (param->sched_priority <= 478) { - pparam.sched_priority = +2; - } else if (param->sched_priority >= 733) { - pparam.sched_priority = -2; - } else { - pparam.sched_priority = 0; - } - - // We always use SCHED_OTHER for now, so don't call this for now. - // int result = pthread_attr_setschedparam(&(*attr)->pth_attr, &pparam); - int result = 0; - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetschedpolicy(const ScePthreadAttr* attr, int* policy) { - if (policy == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getschedpolicy(&(*attr)->pth_attr, policy); - - switch (*policy) { - case SCHED_OTHER: - *policy = (*attr)->policy; - break; - case SCHED_FIFO: - *policy = 1; - break; - case SCHED_RR: - *policy = 3; - break; - default: - UNREACHABLE(); - } - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetschedpolicy(ScePthreadAttr* attr, int policy) { - if (attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int ppolicy = SCHED_OTHER; // winpthreads only supports SCHED_OTHER - if (policy != SCHED_OTHER) { - LOG_ERROR(Kernel_Pthread, "policy={} not supported by winpthreads", policy); - } - - (*attr)->policy = policy; - int result = pthread_attr_setschedpolicy(&(*attr)->pth_attr, ppolicy); - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -ScePthread PS4_SYSV_ABI scePthreadSelf() { - return g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr, - const /*SceKernelCpumask*/ u64 mask) { - LOG_DEBUG(Kernel_Pthread, "called"); - - if (pattr == nullptr || *pattr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - (*pattr)->affinity = mask; - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetaffinity(const ScePthreadAttr* pattr, - /* SceKernelCpumask*/ u64* mask) { - if (pattr == nullptr || *pattr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - *mask = (*pattr)->affinity; - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadAttrGetstackaddr(const ScePthreadAttr* attr, void** stack_addr) { - - if (stack_addr == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - size_t stack_size = 0; - int result = pthread_attr_getstack(&(*attr)->pth_attr, stack_addr, &stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrGetstacksize(const ScePthreadAttr* attr, size_t* stack_size) { - - if (stack_size == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_getstacksize(&(*attr)->pth_attr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstackaddr(ScePthreadAttr* attr, void* addr) { - - if (addr == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - size_t stack_size = 0; - pthread_attr_getstacksize(&(*attr)->pth_attr, &stack_size); - - int result = pthread_attr_setstack(&(*attr)->pth_attr, addr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstacksize(ScePthreadAttr* attr, size_t stack_size) { - - if (stack_size == 0 || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_attr_setstacksize(&(*attr)->pth_attr, stack_size); - - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI posix_pthread_attr_init(ScePthreadAttr* attr) { - int result = scePthreadAttrInit(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setstacksize(ScePthreadAttr* attr, size_t stacksize) { - int result = scePthreadAttrSetstacksize(attr, stacksize); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask) { - LOG_DEBUG(Kernel_Pthread, "called"); - - if (thread == nullptr) { - return SCE_KERNEL_ERROR_ESRCH; - } - - auto result = scePthreadAttrSetaffinity(&thread->attr, mask); - - return result; -} - -int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask) { - LOG_INFO(Kernel_Pthread, "called"); - - if (thread == nullptr) { - return SCE_KERNEL_ERROR_ESRCH; - } - - auto result = scePthreadAttrGetaffinity(&thread->attr, mask); - - return result; -} - -ScePthreadMutex* createMutex(ScePthreadMutex* addr) { - if (addr == nullptr || - (*addr != nullptr && *addr != ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER)) { - return addr; - } - - const VAddr vaddr = reinterpret_cast(addr); - std::string name = fmt::format("mutex{:#x}", vaddr); - scePthreadMutexInit(addr, nullptr, name.c_str()); - return addr; -} - -int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* mutex_attr, - const char* name) { - const ScePthreadMutexattr* attr; - - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex_attr == nullptr || *mutex_attr == nullptr) { - if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { - attr = g_pthread_cxt->getAdaptiveMutexattr(); - } else { - attr = g_pthread_cxt->getDefaultMutexattr(); - } - } else { - attr = mutex_attr; - } - - *mutex = new PthreadMutexInternal{}; - if (name != nullptr) { - (*mutex)->name = name; - } else { - (*mutex)->name = "nonameMutex"; - } - - int result = pthread_mutex_init(&(*mutex)->pth_mutex, &(*attr)->pth_mutex_attr); - - if (name != nullptr) { - LOG_INFO(Kernel_Pthread, "name={}, result={}", name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexDestroy(ScePthreadMutex* mutex) { - - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - if (*mutex == ORBIS_PTHREAD_MUTEX_ADAPTIVE_INITIALIZER) { - return ORBIS_OK; - } - - int result = pthread_mutex_destroy(&(*mutex)->pth_mutex); - - LOG_DEBUG(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result); - - delete *mutex; - *mutex = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - case EINVAL: - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} -int PS4_SYSV_ABI scePthreadMutexattrInit(ScePthreadMutexattr* attr) { - *attr = new PthreadMutexattrInternal{}; - - int result = pthread_mutexattr_init(&(*attr)->pth_mutex_attr); - - result = (result == 0 ? scePthreadMutexattrSettype(attr, 1) : result); - result = (result == 0 ? scePthreadMutexattrSetprotocol(attr, 0) : result); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexattrSettype(ScePthreadMutexattr* attr, int type) { - int ptype = PTHREAD_MUTEX_DEFAULT; - switch (type) { - case ORBIS_PTHREAD_MUTEX_ERRORCHECK: - ptype = PTHREAD_MUTEX_ERRORCHECK; - break; - case ORBIS_PTHREAD_MUTEX_RECURSIVE: - ptype = PTHREAD_MUTEX_RECURSIVE; - break; - case ORBIS_PTHREAD_MUTEX_NORMAL: - ptype = PTHREAD_MUTEX_NORMAL; - break; - case ORBIS_PTHREAD_MUTEX_ADAPTIVE: - LOG_ERROR(Kernel_Pthread, "Unimplemented adaptive mutex"); - ptype = PTHREAD_MUTEX_ERRORCHECK; - break; - default: - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutexattr_settype(&(*attr)->pth_mutex_attr, ptype); - ASSERT(result == 0); - - return SCE_OK; -} - -int PS4_SYSV_ABI scePthreadMutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol) { - int pprotocol = PTHREAD_PRIO_NONE; - switch (protocol) { - case 0: - pprotocol = PTHREAD_PRIO_NONE; - break; - case 1: - pprotocol = PTHREAD_PRIO_INHERIT; - break; - case 2: - pprotocol = PTHREAD_PRIO_PROTECT; - break; - default: - UNREACHABLE_MSG("Invalid protocol: {}", protocol); - } - -#if _WIN64 - int result = 0; -#else - int result = pthread_mutexattr_setprotocol(&(*attr)->pth_mutex_attr, pprotocol); -#endif - (*attr)->pprotocol = pprotocol; - return result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_lock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "Locked name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case EDEADLK: - return SCE_KERNEL_ERROR_EDEADLK; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex) { - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_unlock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "Unlocking name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case EPERM: - return SCE_KERNEL_ERROR_EPERM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexattrDestroy(ScePthreadMutexattr* attr) { - int result = pthread_mutexattr_destroy(&(*attr)->pth_mutex_attr); - - delete *attr; - *attr = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -ScePthreadCond* createCond(ScePthreadCond* addr) { - if (addr == nullptr || *addr != nullptr) { - return addr; - } - static std::mutex mutex; - std::scoped_lock lk{mutex}; - if (*addr != nullptr) { - return addr; - } - const VAddr vaddr = reinterpret_cast(addr); - std::string name = fmt::format("cond{:#x}", vaddr); - scePthreadCondInit(static_cast(addr), nullptr, name.c_str()); - return addr; -} - -int PS4_SYSV_ABI scePthreadCondInit(ScePthreadCond* cond, const ScePthreadCondattr* attr, - const char* name) { - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - if (attr == nullptr) { - attr = g_pthread_cxt->getDefaultCondattr(); - } - - *cond = new PthreadCondInternal{}; - - if (name != nullptr) { - (*cond)->name = name; - } else { - (*cond)->name = "nonameCond"; - } - - int result = pthread_cond_init(&(*cond)->cond, &(*attr)->cond_attr); - - if (name != nullptr) { - LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*cond)->name, result); - } - - switch (result) { - case 0: - return SCE_OK; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EINVAL: - return SCE_KERNEL_ERROR_EINVAL; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondattrInit(ScePthreadCondattr* attr) { - *attr = new PthreadCondAttrInternal{}; - - int result = pthread_condattr_init(&(*attr)->cond_attr); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondBroadcast(ScePthreadCond* cond) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_cond_broadcast(&(*cond)->cond); - - LOG_TRACE(Kernel_Pthread, "called name={}, result={}", (*cond)->name, result); - - return (result == 0 ? SCE_OK : SCE_KERNEL_ERROR_EINVAL); -} - -int PS4_SYSV_ABI scePthreadCondTimedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, u64 usec) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - timespec time{}; - time.tv_sec = usec / 1000000; - time.tv_nsec = ((usec % 1000000) * 1000); - int result = pthread_cond_timedwait(&(*cond)->cond, &(*mutex)->pth_mutex, &time); - - // LOG_INFO(Kernel_Pthread, "scePthreadCondTimedwait, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case ETIMEDOUT: - return SCE_KERNEL_ERROR_ETIMEDOUT; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondDestroy(ScePthreadCond* cond) { - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_cond_destroy(&(*cond)->cond); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondDestroy, result={}", result); - - delete *cond; - *cond = nullptr; - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI posix_pthread_mutex_init(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit"); - int result = scePthreadMutexInit(mutex, attr, nullptr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_lock(ScePthreadMutex* mutex) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_lock redirect to scePthreadMutexLock"); - int result = scePthreadMutexLock(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_unlock(ScePthreadMutex* mutex) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_unlock redirect to scePthreadMutexUnlock"); - int result = scePthreadMutexUnlock(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutex_destroy(ScePthreadMutex* mutex) { - int result = scePthreadMutexDestroy(mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_wait(ScePthreadCond* cond, ScePthreadMutex* mutex) { - int result = scePthreadCondWait(cond, mutex); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_timedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, - u64 usec) { - int result = scePthreadCondTimedwait(cond, mutex, usec); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_broadcast(ScePthreadCond* cond) { - int result = scePthreadCondBroadcast(cond); - if (result != 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_init(ScePthreadMutexattr* attr) { - int result = scePthreadMutexattrInit(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_settype(ScePthreadMutexattr* attr, int type) { - int result = scePthreadMutexattrSettype(attr, type); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(ScePthreadMutexattr* attr) { - int result = scePthreadMutexattrDestroy(attr); - if (result < 0) { - UNREACHABLE(); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_once(pthread_once_t* once_control, void (*init_routine)(void)) { - return pthread_once(once_control, init_routine); -} - -int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(ScePthreadMutexattr* attr, int protocol) { - int result = scePthreadMutexattrSetprotocol(attr, protocol); - if (result < 0) { - UNREACHABLE(); - } - return result; -} - -#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK -static int pthread_mutex_timedlock(pthread_mutex_t* mutex, const struct timespec* abstime) { - int rc; - while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) { - struct timespec curr_time; - clock_gettime(CLOCK_REALTIME, &curr_time); - - s64 remaining_ns = 0; - remaining_ns += - (static_cast(abstime->tv_sec) - static_cast(curr_time.tv_sec)) * 1000000000L; - remaining_ns += static_cast(abstime->tv_nsec) - static_cast(curr_time.tv_nsec); - - if (remaining_ns <= 0) { - return ETIMEDOUT; - } - - struct timespec sleep_time; - sleep_time.tv_sec = 0; - if (remaining_ns < 5000000L) { - sleep_time.tv_nsec = remaining_ns; - } else { - sleep_time.tv_nsec = 5000000; - } - - nanosleep(&sleep_time, nullptr); - } - - return rc; -} -#endif - -int PS4_SYSV_ABI scePthreadMutexTimedlock(ScePthreadMutex* mutex, u64 usec) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - timespec time{}; - time.tv_sec = usec / 1000000; - time.tv_nsec = ((usec % 1000000) * 1000); - int result = pthread_mutex_timedlock(&(*mutex)->pth_mutex, &time); - - switch (result) { - case 0: - return SCE_OK; - case ETIMEDOUT: - return SCE_KERNEL_ERROR_ETIMEDOUT; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -static int pthread_copy_attributes(ScePthreadAttr* dst, const ScePthreadAttr* src) { - if (dst == nullptr || *dst == nullptr || src == nullptr || *src == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - u64 mask = 0; - int state = 0; - size_t guard_size = 0; - int inherit_sched = 0; - SceKernelSchedParam param = {}; - int policy = 0; - void* stack_addr = nullptr; - size_t stack_size = 0; - - int result = 0; - - result = (result == 0 ? scePthreadAttrGetaffinity(src, &mask) : result); - result = (result == 0 ? scePthreadAttrGetdetachstate(src, &state) : result); - result = (result == 0 ? scePthreadAttrGetguardsize(src, &guard_size) : result); - result = (result == 0 ? scePthreadAttrGetinheritsched(src, &inherit_sched) : result); - result = (result == 0 ? scePthreadAttrGetschedparam(src, ¶m) : result); - result = (result == 0 ? scePthreadAttrGetschedpolicy(src, &policy) : result); - result = (result == 0 ? scePthreadAttrGetstackaddr(src, &stack_addr) : result); - result = (result == 0 ? scePthreadAttrGetstacksize(src, &stack_size) : result); - - result = (result == 0 ? scePthreadAttrSetaffinity(dst, mask) : result); - result = (result == 0 ? scePthreadAttrSetdetachstate(dst, state) : result); - result = (result == 0 ? scePthreadAttrSetguardsize(dst, guard_size) : result); - result = (result == 0 ? scePthreadAttrSetinheritsched(dst, inherit_sched) : result); - result = (result == 0 ? scePthreadAttrSetschedparam(dst, ¶m) : result); - result = (result == 0 ? scePthreadAttrSetschedpolicy(dst, policy) : result); - if (stack_addr != nullptr) { - result = (result == 0 ? scePthreadAttrSetstackaddr(dst, stack_addr) : result); - } - if (stack_size != 0) { - result = (result == 0 ? scePthreadAttrSetstacksize(dst, stack_size) : result); - } - - return result; -} - -int PS4_SYSV_ABI scePthreadAttrGet(ScePthread thread, ScePthreadAttr* attr) { - if (thread == nullptr || attr == nullptr || *attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - return pthread_copy_attributes(attr, &thread->attr); -} - -static void cleanup_thread(void* arg) { - auto* thread = static_cast(arg); - for (const auto& [key, destructor] : thread->key_destructors) { - if (void* value = pthread_getspecific(key); value != nullptr) { - destructor(value); - } - } - Core::SetTcbBase(nullptr); - thread->is_almost_done = true; - DebugState.RemoveCurrentThreadFromGuestList(); -} - -static void* run_thread(void* arg) { - auto* thread = static_cast(arg); - Common::SetCurrentThreadName(thread->name.c_str()); - const auto* linker = Common::Singleton::Instance(); - void* ret = nullptr; - g_pthread_self = thread; - pthread_cleanup_push(cleanup_thread, thread); - thread->is_started = true; - DebugState.AddCurrentThreadToGuestList(); - ret = linker->ExecuteGuest(thread->entry, thread->arg); - pthread_cleanup_pop(1); - return ret; -} - -int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, const char* name) { - if (thread == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - auto* pthread_pool = g_pthread_cxt->GetPthreadPool(); - - if (attr == nullptr) { - attr = g_pthread_cxt->GetDefaultAttr(); - } - - *thread = pthread_pool->Create(name); - - if ((*thread)->attr != nullptr) { - scePthreadAttrDestroy(&(*thread)->attr); - } - scePthreadAttrInit(&(*thread)->attr); - - int result = pthread_copy_attributes(&(*thread)->attr, attr); - ASSERT(result == 0); - - if (name != NULL) { - (*thread)->name = name; - } else { - (*thread)->name = "no-name"; - } - (*thread)->entry = start_routine; - (*thread)->arg = arg; - (*thread)->is_almost_done = false; - (*thread)->is_detached = (*attr)->detached; - (*thread)->is_started = false; - - pthread_attr_setstacksize(&(*attr)->pth_attr, 2_MB); - result = pthread_create(&(*thread)->pth, &(*attr)->pth_attr, run_thread, *thread); - - LOG_INFO(Kernel_Pthread, "thread create name = {}", (*thread)->name); - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - case EDEADLK: - return SCE_KERNEL_ERROR_EDEADLK; - case EPERM: - return SCE_KERNEL_ERROR_EPERM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -ScePthread PThreadPool::Create(const char* name) { - std::scoped_lock lock{m_mutex}; - - for (auto* p : m_threads) { - if (p->is_free && name != nullptr && p->name == name) { - p->is_free = false; - return p; - } - } - - auto* ret = new PthreadInternal{}; - ret->is_free = false; - ret->is_detached = false; - ret->is_almost_done = false; - ret->attr = nullptr; - - m_threads.push_back(ret); - - return ret; -} - -void PS4_SYSV_ABI scePthreadYield() { - sched_yield(); -} - -void PS4_SYSV_ABI posix_pthread_yield() { - sched_yield(); -} - -int PS4_SYSV_ABI scePthreadAttrGetstack(ScePthreadAttr* attr, void** addr, size_t* size) { - - int result = pthread_attr_getstack(&(*attr)->pth_attr, addr, size); - LOG_INFO(Kernel_Pthread, "scePthreadAttrGetstack: result = {}", result); - - if (result == 0) { - return SCE_OK; - } - return SCE_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadAttrSetstack(ScePthreadAttr* attr, void* addr, size_t size) { - if (attr == nullptr || *attr == nullptr || addr == nullptr || size < 0x4000) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_attr_setstack(&(*attr)->pth_attr, addr, size); - LOG_INFO(Kernel_Pthread, "scePthreadAttrSetstack: result = {}", result); - - if (result == 0) { - return ORBIS_OK; - } - return ORBIS_KERNEL_ERROR_EINVAL; -} - -int PS4_SYSV_ABI scePthreadJoin(ScePthread thread, void** res) { - int result = pthread_join(thread->pth, res); - LOG_INFO(Kernel_Pthread, "scePthreadJoin result = {}", result); - thread->is_detached = false; - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_join(ScePthread thread, void** res) { - int result = pthread_join(thread->pth, res); - LOG_INFO(Kernel_Pthread, "posix_pthread_join result = {}", result); - thread->is_detached = false; - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadDetach(ScePthread thread) { - thread->is_detached = true; - return ORBIS_OK; -} - -ScePthread PS4_SYSV_ABI posix_pthread_self() { - return g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadCondSignal(ScePthreadCond* cond) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - - int result = pthread_cond_signal(&(*cond)->cond); - - // LOG_INFO(Kernel_Pthread, "scePthreadCondSignal, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case EBUSY: - return SCE_KERNEL_ERROR_EBUSY; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondWait(ScePthreadCond* cond, ScePthreadMutex* mutex) { - cond = createCond(cond); - if (cond == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - if (mutex == nullptr || *mutex == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_cond_wait(&(*cond)->cond, &(*mutex)->pth_mutex); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondWait, result={}", result); - - switch (result) { - case 0: - return SCE_OK; - case EINTR: - return SCE_KERNEL_ERROR_EINTR; - case EAGAIN: - return SCE_KERNEL_ERROR_EAGAIN; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadCondattrDestroy(ScePthreadCondattr* attr) { - if (attr == nullptr) { - return SCE_KERNEL_ERROR_EINVAL; - } - int result = pthread_condattr_destroy(&(*attr)->cond_attr); - - LOG_DEBUG(Kernel_Pthread, "scePthreadCondattrDestroy: result = {} ", result); - delete *attr; - - switch (result) { - case 0: - return SCE_OK; - case ENOMEM: - return SCE_KERNEL_ERROR_ENOMEM; - default: - return SCE_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadMutexTrylock(ScePthreadMutex* mutex) { - mutex = createMutex(mutex); - if (mutex == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - int result = pthread_mutex_trylock(&(*mutex)->pth_mutex); - if (result != 0) { - LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result); - } - - switch (result) { - case 0: - return ORBIS_OK; - case EAGAIN: - return ORBIS_KERNEL_ERROR_EAGAIN; - case EBUSY: - return ORBIS_KERNEL_ERROR_EBUSY; - case EINVAL: - default: - return ORBIS_KERNEL_ERROR_EINVAL; - } -} - -int PS4_SYSV_ABI scePthreadEqual(ScePthread thread1, ScePthread thread2) { - return (thread1 == thread2 ? 1 : 0); -} - -int PS4_SYSV_ABI posix_pthread_equal(ScePthread thread1, ScePthread thread2) { - return (thread1 == thread2 ? 1 : 0); -} - -struct TlsIndex { - u64 ti_module; - u64 ti_offset; -}; - -void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) { - auto* linker = Common::Singleton::Instance(); - return linker->TlsGetAddr(index->ti_module, index->ti_offset); -} - -int PS4_SYSV_ABI posix_sched_get_priority_max() { - return ORBIS_KERNEL_PRIO_FIFO_HIGHEST; -} - -int PS4_SYSV_ABI posix_sched_get_priority_min() { - return ORBIS_KERNEL_PRIO_FIFO_LOWEST; -} - -int PS4_SYSV_ABI posix_pthread_mutex_trylock(ScePthreadMutex* mutex) { - int result = scePthreadMutexTrylock(mutex); - if (result < 0) { - // UNREACHABLE(); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_destroy(ScePthreadAttr* attr) { - int result = scePthreadAttrDestroy(attr); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setschedparam(ScePthreadAttr* attr, - const SceKernelSchedParam* param) { - int result = scePthreadAttrSetschedparam(attr, param); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(ScePthreadAttr* attr, int inheritSched) { - int result = scePthreadAttrSetinheritsched(attr, inheritSched); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_setprio(ScePthread thread, int prio) { - int result = scePthreadSetprio(thread, prio); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(ScePthreadAttr* attr, int detachstate) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit"); - int result = scePthreadAttrSetdetachstate(attr, detachstate); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_create_name_np(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg, - const char* name) { - int result = scePthreadCreate(thread, attr, start_routine, arg, name); - if (result != 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_create(ScePthread* thread, const ScePthreadAttr* attr, - PthreadEntryFunc start_routine, void* arg) { - return posix_pthread_create_name_np(thread, attr, start_routine, arg, "NoName"); -} - -using Destructor = void (*)(void*); - -int PS4_SYSV_ABI posix_pthread_key_create(u32* key, Destructor func) { - pthread_key_t thread_key; - int rc = pthread_key_create(&thread_key, func); - *key = static_cast(thread_key); - return rc; -} - -int PS4_SYSV_ABI posix_pthread_setspecific(int key, const void* value) { - return pthread_setspecific(key, value); -} - -void* PS4_SYSV_ABI posix_pthread_getspecific(int key) { - return pthread_getspecific(key); -} - -int PS4_SYSV_ABI posix_pthread_cond_init(ScePthreadCond* cond, const ScePthreadCondattr* attr) { - // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit"); - int result = scePthreadCondInit(cond, attr, "NoName"); - if (result < 0) { - int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP - ? result + -SCE_KERNEL_ERROR_UNKNOWN - : POSIX_EOTHER; - return rt; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_signal(ScePthreadCond* cond) { - int result = scePthreadCondSignal(cond); - return result; -} - -int PS4_SYSV_ABI posix_pthread_cond_destroy(ScePthreadCond* cond) { - int result = scePthreadCondDestroy(cond); - return result; -} - -int PS4_SYSV_ABI posix_pthread_setcancelstate(int state, int* oldstate) { - return pthread_setcancelstate(state, oldstate); -} - -int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) { - return pthread_detach(thread->pth); -} - -int PS4_SYSV_ABI posix_sem_init(PthreadSemInternal** sem, int pshared, unsigned int value) { - if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EINVAL); - return -1; - } - if (sem != nullptr) { - *sem = new PthreadSemInternal{ - .semaphore = std::counting_semaphore{value}, - .value = {static_cast(value)}, - }; - } - return 0; -} - -int PS4_SYSV_ABI posix_sem_wait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - (*sem)->semaphore.acquire(); - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_trywait(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if (!(*sem)->semaphore.try_acquire()) { - SetPosixErrno(EAGAIN); - return -1; - } - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_timedwait(PthreadSemInternal** sem, const timespec* t) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - - using std::chrono::duration_cast; - using std::chrono::nanoseconds; - using std::chrono::seconds; - using std::chrono::system_clock; - - const system_clock::time_point time{ - duration_cast(seconds{t->tv_sec} + nanoseconds{t->tv_nsec})}; - if (!(*sem)->semaphore.try_acquire_until(time)) { - SetPosixErrno(ETIMEDOUT); - return -1; - } - --(*sem)->value; - return 0; -} - -int PS4_SYSV_ABI posix_sem_post(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) { - SetPosixErrno(EOVERFLOW); - return -1; - } - ++(*sem)->value; - (*sem)->semaphore.release(); - return 0; -} - -int PS4_SYSV_ABI posix_sem_destroy(PthreadSemInternal** sem) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - delete *sem; - *sem = nullptr; - return 0; -} - -int PS4_SYSV_ABI posix_sem_getvalue(PthreadSemInternal** sem, int* sval) { - if (sem == nullptr || *sem == nullptr) { - SetPosixErrno(EINVAL); - return -1; - } - if (sval) { - *sval = (*sem)->value; - } - return 0; -} - -int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) { - return pthread_attr_getstacksize(attr, size); -} - -int PS4_SYSV_ABI scePthreadGetschedparam(ScePthread thread, int* policy, - SceKernelSchedParam* param) { - return pthread_getschedparam(thread->pth, policy, param); -} - -int PS4_SYSV_ABI scePthreadSetschedparam(ScePthread thread, int policy, - const SceKernelSchedParam* param) { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called policy={}, sched_priority={}", policy, - param->sched_priority); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadOnce(int* once_control, void (*init_routine)(void)) { - return pthread_once(reinterpret_cast(once_control), init_routine); -} - -[[noreturn]] void PS4_SYSV_ABI scePthreadExit(void* value_ptr) { - g_pthread_self->is_free = true; - - pthread_exit(value_ptr); - UNREACHABLE(); -} - -[[noreturn]] void PS4_SYSV_ABI posix_pthread_exit(void* value_ptr) { - pthread_exit(value_ptr); - UNREACHABLE(); -} - -int PS4_SYSV_ABI scePthreadGetthreadid() { - return (int)(size_t)g_pthread_self; -} - -int PS4_SYSV_ABI scePthreadGetprio(ScePthread thread, int* prio) { - *prio = thread->prio; - return ORBIS_OK; -} -int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio) { - if (thread == nullptr) { - LOG_ERROR(Kernel_Pthread, "scePthreadSetprio: thread is nullptr"); - return ORBIS_KERNEL_ERROR_EINVAL; - } - thread->prio = prio; - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_condattr_init(ScePthreadCondattr* attr) { - int result = scePthreadCondattrInit(attr); - LOG_INFO(Kernel_Pthread, - "posix_pthread_condattr_init redirect to scePthreadCondattrInit, result = {}", result); - return result; -} - -int PS4_SYSV_ABI posix_pthread_condattr_destroy(ScePthreadCondattr* attr) { - int result = scePthreadCondattrDestroy(attr); - LOG_INFO(Kernel_Pthread, - "posix_pthread_condattr_destroy redirect to scePthreadCondattrDestroy, result = {}", - result); - return result; -} - -int PS4_SYSV_ABI posix_pthread_condattr_setclock(ScePthreadCondattr* attr, clockid_t clock) { - (*attr)->clock = clock; - return SCE_OK; -} - -int PS4_SYSV_ABI posix_pthread_getschedparam(ScePthread thread, int* policy, - SceKernelSchedParam* param) { - return scePthreadGetschedparam(thread, policy, param); -} - -int PS4_SYSV_ABI posix_pthread_setschedparam(ScePthread thread, int policy, - const SceKernelSchedParam* param) { - return scePthreadSetschedparam(thread, policy, param); -} - -int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const ScePthreadAttr* attr, int* policy) { - return scePthreadAttrGetschedpolicy(attr, policy); -} - -int PS4_SYSV_ABI scePthreadRename(ScePthread thread, const char* name) { - thread->name = name; - LOG_INFO(Kernel_Pthread, "scePthreadRename: name = {}", thread->name); - return SCE_OK; -} - void RegisterThreads(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate); - LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init); - LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal); - LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy); - LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create); - LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific); - LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific); - LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedpolicy); - LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetdetachstate); - LIB_FUNCTION("JaRMy+QcpeU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetdetachstate); - LIB_FUNCTION("eXbUSpEaTsA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetinheritsched); - LIB_FUNCTION("DzES9hQF4f4", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedparam); - LIB_FUNCTION("nsYoNRywwNg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrInit); - LIB_FUNCTION("62KCwEMmzcM", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrDestroy); - LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, scePthreadJoin); - LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, scePthreadDetach); - LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, scePthreadEqual); - LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, scePthreadExit); - LIB_FUNCTION("FJrT5LuUBAU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_exit); - LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal); - LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join); - LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, scePthreadGetthreadid); - LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio); - LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio); - LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, scePthreadRename); - LIB_FUNCTION("F+yfmduIBB8", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstackaddr); - LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetguardsize); - - LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSelf); - LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); - LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self); - LIB_FUNCTION("3qxgM4ezETA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetaffinity); - LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetaffinity); - LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGet); - LIB_FUNCTION("FXPWHNk8Of0", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetschedparam); - LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, scePthreadGetschedparam); - LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", 1, 1, scePthreadSetschedparam); - LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstacksize); - LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr); - LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity); - LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", 1, 1, scePthreadGetaffinity); - LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate); - LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, scePthreadYield); - LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield); - - LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstack); - LIB_FUNCTION("Bvn74vj6oLo", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstack); - LIB_FUNCTION("Ru36fiTtJzA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstackaddr); - LIB_FUNCTION("-fA+7ZlGDQs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstacksize); - LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, scePthreadOnce); - - // mutex calls - LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit); - LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexDestroy); - LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrInit); - LIB_FUNCTION("smWEktiyyG0", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrDestroy); - LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrSettype); - LIB_FUNCTION("1FGvU0i9saQ", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrSetprotocol); - LIB_FUNCTION("9UK1vLZQft4", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexLock); - LIB_FUNCTION("tn3VlD0hG60", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexUnlock); - LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexTrylock); - LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexTimedlock); - - // scePthreadMutexInitForInternalLibc, scePthreadMutexattrInitForInternalLibc - LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit); - LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexattrInit); - - // cond calls - LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, scePthreadCondInit); - LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1, scePthreadCondattrInit); - LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, scePthreadCondBroadcast); - LIB_FUNCTION("WKAXJ4XBPQ4", "libkernel", 1, "libkernel", 1, 1, scePthreadCondWait); - LIB_FUNCTION("waPcxYiR3WA", "libkernel", 1, "libkernel", 1, 1, scePthreadCondattrDestroy); - LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1, scePthreadCondSignal); - LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1, scePthreadCondTimedwait); - LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, scePthreadCondDestroy); - - // posix calls - LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init); - LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setstacksize); - LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init); - LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); - LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); - LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy); - LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait); - LIB_FUNCTION("Op8TBGY5KHg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_wait); - LIB_FUNCTION("27bAgiJmOh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_timedwait); - LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); - LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init); - LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_mutexattr_settype); - LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_mutexattr_setprotocol); - LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_mutexattr_destroy); - LIB_FUNCTION("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init); - LIB_FUNCTION("dJcuQVn6-Iw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_condattr_destroy); - LIB_FUNCTION("EjllaAqAPZo", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_condattr_setclock); - LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); - LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_getschedpolicy); - - // openorbis weird functions - LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); - LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); - LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); - LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock); - LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setdetachstate); - LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy); - LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setschedparam); - LIB_FUNCTION("7ZlAakEf0Qg", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_setinheritsched); - LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio); - LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np); - LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create); - LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach); - LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max); - LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min); - LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam); - LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam); - LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init); - LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait); - LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait); - LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait); - LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post); - LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy); - LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue); - LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_attr_getstacksize); - // libs - RwlockSymbolsRegister(sym); - SemaphoreSymbolsRegister(sym); - KeySymbolsRegister(sym); + RegisterMutex(sym); + RegisterCond(sym); + RegisterRwlock(sym); + RegisterSemaphore(sym); + RegisterSpec(sym); + RegisterThreadAttr(sym); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/keys.cpp b/src/core/libraries/kernel/threads/keys.cpp deleted file mode 100644 index cf5104d2..00000000 --- a/src/core/libraries/kernel/threads/keys.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/thread_management.h" -#include "core/libraries/libs.h" - -namespace Libraries::Kernel { - -int PS4_SYSV_ABI scePthreadKeyCreate(OrbisPthreadKey* key, PthreadKeyDestructor destructor) { - if (key == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - pthread_key_t thread_key; - int result = pthread_key_create(&thread_key, nullptr); - *key = static_cast(thread_key); - - if (destructor) { - auto thread = scePthreadSelf(); - thread->key_destructors.emplace_back(*key, destructor); - } - - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadKeyCreate: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -void* PS4_SYSV_ABI scePthreadGetspecific(OrbisPthreadKey key) { - return pthread_getspecific(key); -} - -int PS4_SYSV_ABI scePthreadSetspecific(OrbisPthreadKey key, /* const*/ void* value) { - int result = pthread_setspecific(key, value); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadSetspecific: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -void KeySymbolsRegister(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, scePthreadKeyCreate); - LIB_FUNCTION("eoht7mQOCmo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetspecific); - LIB_FUNCTION("+BzXYkqYeLE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetspecific); -} - -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/rwlock.cpp b/src/core/libraries/kernel/threads/rwlock.cpp deleted file mode 100644 index 87271fe2..00000000 --- a/src/core/libraries/kernel/threads/rwlock.cpp +++ /dev/null @@ -1,383 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/libs.h" -#include "threads.h" - -namespace Libraries::Kernel { - -extern PThreadCxt* g_pthread_cxt; - -int PS4_SYSV_ABI posix_pthread_rwlock_destroy(OrbisPthreadRwlock* rwlock) { - int result = pthread_rwlock_destroy(&(*rwlock)->pth_rwlock); - delete *rwlock; - *rwlock = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_destroy: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_init(OrbisPthreadRwlock* rwlock, - const OrbisPthreadRwlockattr* attr, const char* name) { - *rwlock = new PthreadRwInternal{}; - if (attr == nullptr || *attr == nullptr) { - attr = g_pthread_cxt->getDefaultRwattr(); - } - int result = pthread_rwlock_init(&(*rwlock)->pth_rwlock, &(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_init: error = {}", result); - } - return ORBIS_OK; -} - -OrbisPthreadRwlock* createRwlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock != nullptr) { - return rwlock; - } - static std::mutex mutex; - std::scoped_lock lk{mutex}; - if (*rwlock != nullptr) { - return rwlock; - } - const VAddr addr = std::bit_cast(rwlock); - const auto name = fmt::format("rwlock{:#x}", addr); - posix_pthread_rwlock_init(rwlock, nullptr, name.c_str()); - return rwlock; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - int result = pthread_rwlock_rdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_rdlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_reltimedrdlock_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_reltimedwrlock_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_setname_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_tryrdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_tryrdlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_trywrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_trywrlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_unlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_unlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_unlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - if (rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_wrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_wrlock: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(OrbisPthreadRwlockattr* attr) { - int result = pthread_rwlockattr_destroy(&(*attr)->attr_rwlock); - delete *attr; - *attr = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlockattr_destroy: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_getpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_gettype_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_init(OrbisPthreadRwlockattr* attr) { - *attr = new PthreadRwLockAttrInternal{}; - int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlockattr_init: error = {}", result); - } - return result; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_setpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_pthread_rwlockattr_settype_np() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrDestroy(OrbisPthreadRwlockattr* attr) { - int result = pthread_rwlockattr_destroy(&(*attr)->attr_rwlock); - delete *attr; - *attr = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockattrDestroy: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockattrGetpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrGettype() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrInit(OrbisPthreadRwlockattr* attr) { - *attr = new PthreadRwLockAttrInternal{}; - int result = pthread_rwlockattr_init(&(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockattrInit: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockattrSetpshared() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockattrSettype() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockDestroy(OrbisPthreadRwlock* rwlock) { - int result = pthread_rwlock_destroy(&(*rwlock)->pth_rwlock); - delete *rwlock; - *rwlock = nullptr; - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockDestroy: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockInit(OrbisPthreadRwlock* rwlock, - const OrbisPthreadRwlockattr* attr, const char* name) { - *rwlock = new PthreadRwInternal{}; - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - - if (attr == nullptr || *attr == nullptr) { - attr = g_pthread_cxt->getDefaultRwattr(); - } - if (name != nullptr) { - (*rwlock)->name = name; - } - int result = pthread_rwlock_init(&(*rwlock)->pth_rwlock, &(*attr)->attr_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockInit: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockRdlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_rdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockRdlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockTimedrdlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockTimedwrlock() { - LOG_ERROR(Kernel_Pthread, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI scePthreadRwlockTryrdlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_tryrdlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockTryrdlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockTrywrlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_trywrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockTrywrlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockUnlock(OrbisPthreadRwlock* rwlock) { - if (rwlock == nullptr || *rwlock == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; - } - int result = pthread_rwlock_unlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockUnlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -int PS4_SYSV_ABI scePthreadRwlockWrlock(OrbisPthreadRwlock* rwlock) { - rwlock = createRwlock(rwlock); - int result = pthread_rwlock_wrlock(&(*rwlock)->pth_rwlock); - if (result != 0) { - LOG_ERROR(Kernel_Pthread, "scePthreadRwlockWrlock: error = {}", result); - result += ORBIS_KERNEL_ERROR_UNKNOWN; - } - return result; -} - -void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("1471ajPzxh0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy); - LIB_FUNCTION("ytQULN-nhL4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_init); - LIB_FUNCTION("iGjsr1WAtI0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock); - LIB_FUNCTION("dYv-+If2GPk", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlock_reltimedrdlock_np); - LIB_FUNCTION("RRnSj8h8VR4", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlock_reltimedwrlock_np); - LIB_FUNCTION("Uwxgnsi3xeM", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_setname_np); - LIB_FUNCTION("lb8lnYo-o7k", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlock_timedrdlock); - LIB_FUNCTION("9zklzAl9CGM", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlock_timedwrlock); - LIB_FUNCTION("SFxTMOfuCkE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_tryrdlock); - LIB_FUNCTION("XhWHn6P5R7U", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_trywrlock); - LIB_FUNCTION("EgmLo6EWgso", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_unlock); - LIB_FUNCTION("sIlRvQqsN2Y", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_wrlock); - LIB_FUNCTION("qsdmgXjqSgk", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_destroy); - LIB_FUNCTION("VqEMuCv-qHY", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_getpshared); - LIB_FUNCTION("l+bG5fsYkhg", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_gettype_np); - LIB_FUNCTION("xFebsA4YsFI", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init); - LIB_FUNCTION("OuKg+kRDD7U", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_setpshared); - LIB_FUNCTION("8NuOHiTr1Vw", "libkernel", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_settype_np); - LIB_FUNCTION("1471ajPzxh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy); - LIB_FUNCTION("ytQULN-nhL4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_init); - LIB_FUNCTION("iGjsr1WAtI0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock); - LIB_FUNCTION("lb8lnYo-o7k", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlock_timedrdlock); - LIB_FUNCTION("9zklzAl9CGM", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlock_timedwrlock); - LIB_FUNCTION("SFxTMOfuCkE", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlock_tryrdlock); - LIB_FUNCTION("XhWHn6P5R7U", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlock_trywrlock); - LIB_FUNCTION("EgmLo6EWgso", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_unlock); - LIB_FUNCTION("sIlRvQqsN2Y", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_wrlock); - LIB_FUNCTION("qsdmgXjqSgk", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_destroy); - LIB_FUNCTION("VqEMuCv-qHY", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_getpshared); - LIB_FUNCTION("l+bG5fsYkhg", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_gettype_np); - LIB_FUNCTION("xFebsA4YsFI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init); - LIB_FUNCTION("OuKg+kRDD7U", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_setpshared); - LIB_FUNCTION("8NuOHiTr1Vw", "libScePosix", 1, "libkernel", 1, 1, - posix_pthread_rwlockattr_settype_np); - LIB_FUNCTION("i2ifZ3fS2fo", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrDestroy); - LIB_FUNCTION("LcOZBHGqbFk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrGetpshared); - LIB_FUNCTION("Kyls1ChFyrc", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrGettype); - LIB_FUNCTION("yOfGg-I1ZII", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrInit); - LIB_FUNCTION("-ZvQH18j10c", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrSetpshared); - LIB_FUNCTION("h-OifiouBd8", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockattrSettype); - LIB_FUNCTION("BB+kb08Tl9A", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockDestroy); - LIB_FUNCTION("6ULAa0fq4jA", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockInit); - LIB_FUNCTION("Ox9i0c7L5w0", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockRdlock); - LIB_FUNCTION("iPtZRWICjrM", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTimedrdlock); - LIB_FUNCTION("adh--6nIqTk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTimedwrlock); - LIB_FUNCTION("XD3mDeybCnk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTryrdlock); - LIB_FUNCTION("bIHoZCTomsI", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockTrywrlock); - LIB_FUNCTION("+L98PIbGttk", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockUnlock); - LIB_FUNCTION("mqdNorrB+gI", "libkernel", 1, "libkernel", 1, 1, scePthreadRwlockWrlock); -} -} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_attr.cpp b/src/core/libraries/kernel/threads/thr_attr.cpp new file mode 100644 index 00000000..ec6941e7 --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_attr.cpp @@ -0,0 +1,284 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/libraries/kernel/threads/threads.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static constexpr u32 PthreadStackMin = 16_KB; + +struct PthreadPrio { + s32 pri_min; + s32 pri_max; + s32 pri_default; +}; + +static constexpr std::array ThrPriorities = {{ + {0x2BC, 0x300, 0x3BF}, // Fifo + {0x384, 0x100, 0x2FF}, // Other + {0x2BC, 0, 1}, // Round-Robin +}}; + +PthreadAttr PthreadAttrDefault = { + .sched_policy = SchedPolicy::Fifo, + .sched_inherit = PTHREAD_INHERIT_SCHED, + .prio = 0, + .suspend = false, + .flags = PthreadAttrFlags::ScopeSystem, + .stackaddr_attr = NULL, + .stacksize_attr = ThrStackDefault, + .guardsize_attr = 0, + .cpusetsize = 0, +}; + +int PS4_SYSV_ABI posix_pthread_attr_destroy(PthreadAttrT* attr) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + free(*attr); + *attr = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getdetachstate(const PthreadAttrT* attr, int* detachstate) { + if (attr == nullptr || *attr == nullptr || detachstate == nullptr) { + return POSIX_EINVAL; + } + *detachstate = True((*attr)->flags & PthreadAttrFlags::Detached); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getguardsize(const PthreadAttrT* attr, size_t* guardsize) { + if (attr == nullptr || *attr == nullptr || guardsize == nullptr) { + return POSIX_EINVAL; + } + *guardsize = (*attr)->guardsize_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getinheritsched(const PthreadAttrT* attr, int* sched_inherit) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + *sched_inherit = (*attr)->sched_inherit; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getschedparam(const PthreadAttrT* attr, + struct sched_param* param) { + if (attr == nullptr || *attr == nullptr || param == nullptr) { + return POSIX_EINVAL; + } + param->sched_priority = (*attr)->prio; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const PthreadAttrT* attr, SchedPolicy* policy) { + if (attr == nullptr || *attr == nullptr || policy == nullptr) { + return POSIX_EINVAL; + } + *policy = (*attr)->sched_policy; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getstack(const PthreadAttrT* attr, void** stackaddr, + size_t* stacksize) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr || stacksize == nullptr) { + return POSIX_EINVAL; + } + *stackaddr = (*attr)->stackaddr_attr; + *stacksize = (*attr)->stacksize_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getstackaddr(const PthreadAttrT* attr, void** stackaddr) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr) { + return POSIX_EINVAL; + } + *stackaddr = (*attr)->stackaddr_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const PthreadAttrT* attr, size_t* stacksize) { + if (attr == nullptr || *attr == nullptr || stacksize == nullptr) { + return POSIX_EINVAL; + } + *stacksize = (*attr)->stacksize_attr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_init(PthreadAttrT* attr) { + PthreadAttrT pattr = (PthreadAttrT)malloc(sizeof(PthreadAttr)); + if (pattr == nullptr) { + return POSIX_ENOMEM; + } + memcpy(pattr, &PthreadAttrDefault, sizeof(PthreadAttr)); + *attr = pattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setschedpolicy(PthreadAttrT* attr, SchedPolicy policy) { + if (attr == NULL || *attr == NULL) { + return POSIX_EINVAL; + } else if ((policy < SchedPolicy::Fifo) || (policy > SchedPolicy::RoundRobin)) { + return POSIX_ENOTSUP; + } + (*attr)->sched_policy = policy; + (*attr)->prio = ThrPriorities[u32(policy) - 1].pri_default; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setstack(PthreadAttrT* attr, void* stackaddr, + size_t stacksize) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr || + stacksize < PthreadStackMin) { + return POSIX_EINVAL; + } + (*attr)->stackaddr_attr = stackaddr; + (*attr)->stacksize_attr = stacksize; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setstackaddr(PthreadAttrT* attr, void* stackaddr) { + if (attr == nullptr || *attr == nullptr || stackaddr == nullptr) { + return POSIX_EINVAL; + } + (*attr)->stackaddr_attr = stackaddr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setstacksize(PthreadAttrT* attr, size_t stacksize) { + if (attr == nullptr || *attr == nullptr || stacksize < PthreadStackMin) { + return POSIX_EINVAL; + } + (*attr)->stacksize_attr = stacksize; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(PthreadAttrT* attr, int detachstate) { + if (attr == nullptr || *attr == nullptr || (detachstate != 1 && detachstate != 0)) { + return POSIX_EINVAL; + } + if (detachstate) { + (*attr)->flags |= PthreadAttrFlags::Detached; + } else { + (*attr)->flags &= ~PthreadAttrFlags::Detached; + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setschedparam(PthreadAttrT* attr, const sched_param* param) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (param == nullptr) { + return POSIX_ENOTSUP; + } + + const auto policy = (*attr)->sched_policy; + if (policy == SchedPolicy::Fifo || policy == SchedPolicy::RoundRobin) { + if (param->sched_priority < ThrPriorities[u32(policy) - 1].pri_min || + param->sched_priority > ThrPriorities[u32(policy) - 1].pri_max) { + return POSIX_ENOTSUP; + } + } + (*attr)->prio = param->sched_priority; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(PthreadAttrT* attr, int sched_inherit) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (sched_inherit != 4 && sched_inherit != 0) { + return POSIX_ENOTSUP; + } + + (*attr)->sched_inherit = sched_inherit; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_setguardsize(PthreadAttrT* attr, size_t guardsize) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + (*attr)->guardsize_attr = guardsize; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_attr_get_np(PthreadT pthread, PthreadAttrT* dstattr) { + PthreadAttr* dst; + if (pthread == nullptr || dstattr == nullptr || (dst = *dstattr) == nullptr) { + return POSIX_EINVAL; + } + auto* thread_state = ThrState::Instance(); + int ret = thread_state->FindThread(pthread, /*include dead*/ 0); + if (ret != 0) { + return ret; + } + PthreadAttr attr = pthread->attr; + if (True(pthread->flags & ThreadFlags::Detached)) { + attr.flags |= PthreadAttrFlags::Detached; + } + if (ret == 0) { + memcpy(dst, &attr, sizeof(PthreadAttr)); + } + return ret; +} + +void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("wtkt-teR1so", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_init); + LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setstacksize); + LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getschedpolicy); + LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setdetachstate); + LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy); + LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setschedparam); + LIB_FUNCTION("7ZlAakEf0Qg", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_setinheritsched); + LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getstacksize); + + // Orbis + LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setschedpolicy)); + LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setdetachstate)); + LIB_FUNCTION("JaRMy+QcpeU", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getdetachstate)); + LIB_FUNCTION("eXbUSpEaTsA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setinheritsched)); + LIB_FUNCTION("DzES9hQF4f4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setschedparam)); + LIB_FUNCTION("nsYoNRywwNg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_attr_init)); + LIB_FUNCTION("62KCwEMmzcM", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_destroy)); + LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getstack)); + LIB_FUNCTION("Bvn74vj6oLo", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setstack)); + LIB_FUNCTION("Ru36fiTtJzA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getstackaddr)); + LIB_FUNCTION("-fA+7ZlGDQs", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getstacksize)); + LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_get_np)); + LIB_FUNCTION("FXPWHNk8Of0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_getschedparam)); + LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setstacksize)); + LIB_FUNCTION("F+yfmduIBB8", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setstackaddr)); + LIB_FUNCTION("El+cQ20DynU", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_attr_setguardsize)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_clean.cpp b/src/core/libraries/kernel/threads/thr_clean.cpp new file mode 100644 index 00000000..26f98445 --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_clean.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/kernel/threads/threads.h" + +namespace Libraries::Kernel { + +void __pthread_cleanup_push_imp(PthreadCleanupFunc routine, void* arg, PthreadCleanup* newbuf) { + Pthread* curthread = g_curthread; + newbuf->routine = routine; + newbuf->routine_arg = arg; + newbuf->onheap = 0; + curthread->cleanup.push_front(newbuf); +} + +void PS4_SYSV_ABI posix_pthread_cleanup_push(PthreadCleanupFunc routine, void* arg) { + Pthread* curthread = g_curthread; + PthreadCleanup* newbuf = (PthreadCleanup*)malloc(sizeof(PthreadCleanup)); + if (newbuf == nullptr) { + return; + } + + newbuf->routine = routine; + newbuf->routine_arg = arg; + newbuf->onheap = 1; + curthread->cleanup.push_front(newbuf); +} + +void PS4_SYSV_ABI posix_pthread_cleanup_pop(int execute) { + Pthread* curthread = g_curthread; + if (!curthread->cleanup.empty()) { + PthreadCleanup* old = curthread->cleanup.front(); + curthread->cleanup.pop_front(); + if (execute) { + old->routine(old->routine_arg); + } + if (old->onheap) { + free(old); + } + } +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_cond.cpp b/src/core/libraries/kernel/threads/thr_cond.cpp new file mode 100644 index 00000000..d41081b9 --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_cond.cpp @@ -0,0 +1,232 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/threads/threads.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static std::mutex CondStaticLock; + +#define THR_COND_INITIALIZER ((PthreadCond*)NULL) +#define THR_COND_DESTROYED ((PthreadCond*)1) + +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, +}; + +static constexpr PthreadCondAttr PhreadCondattrDefault = {.c_pshared = PTHREAD_PROCESS_PRIVATE, + .c_clockid = CLOCK_REALTIME}; + +static int CondInit(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) { + PthreadCond* cvp = (PthreadCond*)calloc(1, sizeof(PthreadCond)); + if (cvp == nullptr) { + return POSIX_ENOMEM; + } + std::construct_at(cvp); + if (cond_attr == nullptr || *cond_attr == nullptr) { + cvp->clock_id = CLOCK_REALTIME; + } else { + // if ((*cond_attr)->c_pshared) { + // cvp->flags |= USYNC_PROCESS_SHARED; + // } + cvp->clock_id = (*cond_attr)->c_clockid; + } + *cond = cvp; + return 0; +} + +static int InitStatic(Pthread* thread, PthreadCondT* cond) { + std::scoped_lock lk{CondStaticLock}; + if (*cond == NULL) + return CondInit(cond, NULL); + return 0; +} + +#define CHECK_AND_INIT_COND \ + if (cvp = *cond; cvp <= THR_COND_DESTROYED) [[unlikely]] { \ + if (cvp == THR_COND_INITIALIZER) { \ + int ret; \ + ret = InitStatic(g_curthread, cond); \ + if (ret) \ + return (ret); \ + } else if (cvp == THR_COND_DESTROYED) { \ + return POSIX_EINVAL; \ + } \ + } + +int PS4_SYSV_ABI posix_pthread_cond_init(PthreadCondT* cond, const PthreadCondAttrT* cond_attr) { + *cond = nullptr; + return CondInit(cond, cond_attr); +} + +int PS4_SYSV_ABI posix_pthread_cond_destroy(PthreadCondT* cond) { + PthreadCond* cvp = *cond; + if (cvp == THR_COND_INITIALIZER) { + return 0; + } + if (cvp == THR_COND_DESTROYED) { + return POSIX_EINVAL; + } + cvp = *cond; + *cond = THR_COND_DESTROYED; + std::destroy_at(cvp); + free(cvp); + return 0; +} + +int PthreadCond::Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + PthreadMutex* mp = *mutex; + + if (int error = mp->IsOwned(curthread); error != 0) { + return error; + } + + //_thr_testcancel(curthread); + //_thr_cancel_enter2(curthread, 0); + if (abstime) { + const auto status = cond.wait_until(mp->m_lock, abstime->TimePoint()); + return status == std::cv_status::timeout ? POSIX_ETIMEDOUT : 0; + } else { + cond.wait(mp->m_lock); + return 0; + } + //_thr_cancel_leave(curthread, 0); +} + +int PS4_SYSV_ABI posix_pthread_cond_wait(PthreadCondT* cond, PthreadMutexT* mutex) { + PthreadCond* cvp; + CHECK_AND_INIT_COND + return cvp->Wait(mutex, nullptr); +} + +int PS4_SYSV_ABI posix_pthread_cond_timedwait(PthreadCondT* cond, PthreadMutexT* mutex, + const OrbisKernelTimespec* abstime) { + if (abstime == nullptr || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || + abstime->tv_nsec >= 1000000000) { + return POSIX_EINVAL; + } + + PthreadCond* cvp; + CHECK_AND_INIT_COND + return cvp->Wait(mutex, abstime); +} + +int PS4_SYSV_ABI posix_pthread_cond_signal(PthreadCondT* cond) { + PthreadCond* cvp; + CHECK_AND_INIT_COND + cvp->cond.notify_one(); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_cond_broadcast(PthreadCondT* cond) { + PthreadCond* cvp; + CHECK_AND_INIT_COND + cvp->cond.notify_all(); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_init(PthreadCondAttrT* attr) { + PthreadCondAttr* pattr = (PthreadCondAttr*)malloc(sizeof(PthreadCondAttr)); + if (pattr == nullptr) { + return POSIX_ENOMEM; + } + memcpy(pattr, &PhreadCondattrDefault, sizeof(PthreadCondAttr)); + *attr = pattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_destroy(PthreadCondAttrT* attr) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + free(*attr); + *attr = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_getclock(const PthreadCondAttrT* attr, ClockId* clock_id) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + *clock_id = static_cast((*attr)->c_clockid); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_setclock(PthreadCondAttrT* attr, ClockId clock_id) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (clock_id != ClockId::Realtime && clock_id != ClockId::Virtual && + clock_id != ClockId::Prof && clock_id != ClockId::Monotonic) { + return POSIX_EINVAL; + } + (*attr)->c_clockid = static_cast(clock_id); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_getpshared(const PthreadCondAttrT* attr, int* pshared) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + *pshared = PTHREAD_PROCESS_PRIVATE; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_condattr_setpshared(PthreadCondAttrT* attr, int pshared) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + if (pshared != PTHREAD_PROCESS_PRIVATE) { + return POSIX_EINVAL; + } + return 0; +} + +void RegisterCond(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init); + LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal); + LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy); + LIB_FUNCTION("Op8TBGY5KHg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_wait); + LIB_FUNCTION("27bAgiJmOh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_timedwait); + LIB_FUNCTION("mkx2fVhNMsg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); + + // Posix-Kernel + LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_wait); + LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_cond_broadcast); + + // Orbis + LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_init)); + LIB_FUNCTION("m5-2bsNfv7s", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_condattr_init)); + LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_broadcast)); + LIB_FUNCTION("WKAXJ4XBPQ4", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_cond_wait)); + LIB_FUNCTION("waPcxYiR3WA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_condattr_destroy)); + LIB_FUNCTION("kDh-NfxgMtE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_signal)); + LIB_FUNCTION("BmMjYxmew1w", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_timedwait)); + LIB_FUNCTION("g+PZd2hiacg", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_cond_destroy)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_create.cpp b/src/core/libraries/kernel/threads/thr_create.cpp new file mode 100644 index 00000000..3dfcaff8 --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_create.cpp @@ -0,0 +1,221 @@ +// SPDX-FileCopyrightText: Copyright 2024 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/error_codes.h" +#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/libraries/kernel/threads/threads.h" +#include "core/libraries/libs.h" +#include "core/linker.h" +#include "core/memory.h" + +#include + +namespace Libraries::Kernel { + +constexpr int ORBIS_KERNEL_PRIO_FIFO_DEFAULT = 700; +constexpr int ORBIS_KERNEL_PRIO_FIFO_HIGHEST = 256; +constexpr int ORBIS_KERNEL_PRIO_FIFO_LOWEST = 767; + +extern PthreadAttr PthreadAttrDefault; + +using PthreadEntryFunc = void* (*)(void*); + +void PS4_SYSV_ABI posix_pthread_exit(void* status); + +static void RunThread(Pthread* curthread) { + g_curthread = curthread; + Common::SetCurrentThreadName(curthread->name.c_str()); + DebugState.AddCurrentThreadToGuestList(); + + /* Run the current thread's start routine with argument: */ + const auto* linker = Common::Singleton::Instance(); + void* ret = linker->ExecuteGuest(curthread->start_routine, curthread->arg); + + /* Remove thread from tracking */ + DebugState.RemoveCurrentThreadFromGuestList(); + posix_pthread_exit(ret); +} + +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 (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) { + 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; + } + + new_thread->tid = TidTerminated; + + 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 = 1; + new_thread->cancel_async = 0; + + auto* memory = Core::Memory::Instance(); + if (memory->IsValidAddress(name)) { + new_thread->name = name; + } + + 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 */ + pthread_attr_t pattr; + pthread_attr_init(&pattr); + pthread_attr_setstack(&pattr, new_thread->attr.stackaddr_attr, new_thread->attr.stacksize_attr); + pthread_t pthr; + int ret = pthread_create(&pthr, &pattr, (PthreadEntryFunc)RunThread, new_thread); + if (ret) { + *thread = nullptr; + } + return ret; +} + +int PS4_SYSV_ABI posix_pthread_getthreadid_np() { + return g_curthread->tid; +} + +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(); +} + +int PS4_SYSV_ABI posix_pthread_once(PthreadOnce* once_control, void (*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) { + PthreadOnce* once_control = (PthreadOnce*)arg; + auto state = PthreadOnceState::InProgress; + if (once_control->state.compare_exchange_strong(state, PthreadOnceState::NeverDone, + std::memory_order_release)) { + return; + } + + once_control->state.store(PthreadOnceState::NeverDone, std::memory_order_release); + once_control->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() { + return ORBIS_KERNEL_PRIO_FIFO_HIGHEST; +} + +int PS4_SYSV_ABI posix_sched_get_priority_min() { + 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); + thread->name = name; + return SCE_OK; +} + +void RegisterThread(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once); + LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal); + LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max); + LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min); + LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield); + + // Posix-Kernel + LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); + + // Orbis + LIB_FUNCTION("14bOACANTBo", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_once)); + LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_rename_np)); + LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_create_name_np)); + LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self); + LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal); + LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, posix_pthread_yield); + LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getthreadid_np); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_ctrdtr.cpp b/src/core/libraries/kernel/threads/thr_ctrdtr.cpp new file mode 100644 index 00000000..98cb9482 --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_ctrdtr.cpp @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/singleton.h" +#include "core/libraries/kernel/threads/threads.h" +#include "core/linker.h" +#include "core/tls.h" + +namespace Libraries::Kernel { + +static constexpr size_t TlsTcbSize = 0x40; +static constexpr size_t TlsTcbAlign = 0x20; + +static std::shared_mutex RtldLock; + +Core::Tcb* TcbCtor(Pthread* thread, int initial) { + ASSERT(initial == 0); + std::scoped_lock lk{RtldLock}; + + auto* linker = Common::Singleton::Instance(); + auto* addr_out = linker->AllocateTlsForThread(false); + ASSERT_MSG(addr_out, "Unable to allocate guest TCB"); + + // Initialize allocated memory and allocate DTV table. + const u32 num_dtvs = linker->MaxTlsIndex(); + const auto static_tls_size = linker->StaticTlsSize(); + auto* dtv_table = new Core::DtvEntry[num_dtvs + 2]; + + // Initialize thread control block + u8* addr = reinterpret_cast(addr_out); + auto* tcb = reinterpret_cast(addr + static_tls_size); + memset(addr_out, 0, static_tls_size); + tcb->tcb_self = tcb; + tcb->tcb_dtv = dtv_table; + + // Dtv[0] is the generation counter. libkernel puts their number into dtv[1] + dtv_table[0].counter = linker->GenerationCounter(); + dtv_table[1].counter = num_dtvs; + + // Copy init image of main module. + auto* module = linker->GetModule(0); + u8* dest = reinterpret_cast(addr + static_tls_size - module->tls.offset); + + if (module->tls.image_virtual_addr != 0) { + const u8* src = reinterpret_cast(module->tls.image_virtual_addr); + memcpy(dest, src, module->tls.init_image_size); + } + + // Initialize DTV entry of main module + ASSERT_MSG(module->tls.modid > 0 && module->tls.modid <= num_dtvs); + tcb->tcb_dtv[module->tls.modid + 1].pointer = dest; + + if (tcb) { + tcb->tcb_thread = thread; + } + return tcb; +} + +void TcbDtor(Core::Tcb* oldtls) { + std::scoped_lock lk{RtldLock}; + auto* dtv_table = oldtls->tcb_dtv; + + auto* linker = Common::Singleton::Instance(); + const u32 max_tls_index = linker->MaxTlsIndex(); + const u32 num_dtvs = dtv_table[1].counter; + ASSERT_MSG(num_dtvs <= max_tls_index, "Out of bounds DTV access"); + + const u32 static_tls_size = linker->StaticTlsSize(); + const u8* tls_base = (const u8*)oldtls - Common::AlignUp(static_tls_size, tcbalign); + + for (int i = 1; i < num_dtvs; i++) { + u8* dtv_ptr = dtv_table[i + 1].pointer; + if (dtv_ptr && (dtv_ptr < tls_base || (const u8*)oldtls < dtv_ptr)) { + bool is_occupied; + + _is_occupied = IsDtvIndexOccupied ? ((ulong)tcb, next); + if (_is_occupied != 0) { + FreeMem(dtv_ptr); + return; + } + (**(code**)(LibcHeapApiPtr + 8))(dtv_ptr); + FreeIfOccupied(tcb, dtv_addr & 0xffffffffffff, mod_index); + dtv[modid + 1] = (ulong) * (ushort*)((long)dtv + modid * 8 + 0xe) << 0x30; + } + } + + delete[] dtv_table; +} + +struct TlsIndex { + u64 ti_module; + u64 ti_offset; +}; + +void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) { + auto* linker = Common::Singleton::Instance(); + return linker->TlsGetAddr(index->ti_module, index->ti_offset); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_exit.cpp b/src/core/libraries/kernel/threads/thr_exit.cpp new file mode 100644 index 00000000..4e1ae80d --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_exit.cpp @@ -0,0 +1,181 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/assert.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/libraries/kernel/threads/threads.h" + +namespace Libraries::Kernel { + +void _thread_cleanupspecific(); + +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, 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(); + + pthread_exit(nullptr); + 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 = 1; + curthread->no_cancel = 1; + curthread->cancel_async = 0; + curthread->cancel_point = 0; + + /* 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) { + free(old); + } + } + + 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, 1); 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) { + Pthread* pthread = (Pthread*)arg; + std::scoped_lock lk{*pthread->lock}; + pthread->joiner = nullptr; + }; + + PthreadCleanup cup{backout_join, pthread, 0}; + curthread->cleanup.push_front(&cup); + + //_thr_cancel_enter(curthread); + + const int 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(curthread, 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, NULL); +} + +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, 1); ret != 0) { + return ret; + } + + /* Check if the thread is already detached or has a joiner. */ + if (True(pthread->flags & ThreadFlags::Detached) || (pthread->joiner != NULL)) { + pthread->lock->unlock(); + return POSIX_EINVAL; + } + + /* Flag the thread as detached. */ + pthread->flags |= ThreadFlags::Detached; + thread_state->TryCollect(g_curthread, pthread); /* thread lock released */ + return 0; +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_mutex.cpp b/src/core/libraries/kernel/threads/thr_mutex.cpp new file mode 100644 index 00000000..908feb8f --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_mutex.cpp @@ -0,0 +1,438 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/scope_exit.h" +#include "common/types.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/threads/threads.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static constexpr u32 MUTEX_ADAPTIVE_SPINS = 2000; +static std::mutex MutxStaticLock; + +#define THR_MUTEX_INITIALIZER ((PthreadMutex*)NULL) +#define THR_ADAPTIVE_MUTEX_INITIALIZER ((PthreadMutex*)1) +#define THR_MUTEX_DESTROYED ((PthreadMutex*)2) + +#define CPU_SPINWAIT __asm__ volatile("pause") + +#define CHECK_AND_INIT_MUTEX \ + if (PthreadMutex* m = *mutex; m <= THR_MUTEX_DESTROYED) [[unlikely]] { \ + if (m == THR_MUTEX_DESTROYED) { \ + return POSIX_EINVAL; \ + } \ + if (s32 ret = InitStatic(g_curthread, mutex); ret) { \ + return ret; \ + } \ + m = *mutex; \ + } + +static constexpr PthreadMutexAttr PthreadMutexattrDefault = { + .m_type = PthreadMutexType::ErrorCheck, .m_protocol = PthreadMutexProt::None, .m_ceiling = 0}; + +static constexpr PthreadMutexAttr PthreadMutexattrAdaptiveDefault = { + .m_type = PthreadMutexType::AdaptiveNp, .m_protocol = PthreadMutexProt::None, .m_ceiling = 0}; + +using CallocFun = void* (*)(size_t, size_t); + +static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, + CallocFun calloc_cb) { + const PthreadMutexAttr* attr; + if (mutex_attr == NULL) { + attr = &PthreadMutexattrDefault; + } else { + attr = mutex_attr; + if (attr->m_type < PthreadMutexType::ErrorCheck || attr->m_type >= PthreadMutexType::Max) { + return POSIX_EINVAL; + } + if (attr->m_protocol > PthreadMutexProt::Protect) { + return POSIX_EINVAL; + } + } + PthreadMutex* pmutex = (PthreadMutex*)calloc(1, sizeof(PthreadMutex)); + if (pmutex == nullptr) { + return POSIX_ENOMEM; + } + + std::construct_at(pmutex); + pmutex->m_flags = PthreadMutexFlags(attr->m_type); + pmutex->m_owner = NULL; + pmutex->m_count = 0; + pmutex->m_spinloops = 0; + pmutex->m_yieldloops = 0; + pmutex->m_protocol = attr->m_protocol; + if (attr->m_type == PthreadMutexType::AdaptiveNp) { + pmutex->m_spinloops = MUTEX_ADAPTIVE_SPINS; + // pmutex->m_yieldloops = _thr_yieldloops; + } + + *mutex = pmutex; + return 0; +} + +static int InitStatic(Pthread* thread, PthreadMutexT* mutex) { + std::scoped_lock lk{MutxStaticLock}; + + if (*mutex == THR_MUTEX_INITIALIZER) { + return MutexInit(mutex, &PthreadMutexattrDefault, calloc); + } else if (*mutex == THR_ADAPTIVE_MUTEX_INITIALIZER) { + return MutexInit(mutex, &PthreadMutexattrAdaptiveDefault, calloc); + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex, + const PthreadMutexAttrT* mutex_attr) { + return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, calloc); +} + +int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) { + PthreadMutexT m = *mutex; + if (m < THR_MUTEX_DESTROYED) { + return 0; + } + if (m == THR_MUTEX_DESTROYED) { + return POSIX_EINVAL; + } + if (m->m_owner != nullptr) { + return EBUSY; + } + *mutex = THR_MUTEX_DESTROYED; + std::destroy_at(m); + free(m); + return 0; +} + +int PthreadMutex::SelfTryLock() { + switch (Type()) { + case PthreadMutexType::ErrorCheck: + case PthreadMutexType::Normal: + return POSIX_EBUSY; + case PthreadMutexType::Recursive: { + /* Increment the lock count: */ + if (m_count + 1 > 0) { + m_count++; + return 0; + } + return POSIX_EAGAIN; + } + default: + return POSIX_EINVAL; + } +} + +int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime) { + switch (Type()) { + case PthreadMutexType::ErrorCheck: + case PthreadMutexType::AdaptiveNp: { + if (abstime) { + if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { + return POSIX_EINVAL; + } else { + std::this_thread::sleep_until(abstime->TimePoint()); + return POSIX_ETIMEDOUT; + } + } + /* + * POSIX specifies that mutexes should return + * EDEADLK if a recursive lock is detected. + */ + return POSIX_EDEADLK; + } + case PthreadMutexType::Normal: { + /* + * What SS2 define as a 'normal' mutex. Intentionally + * deadlock on attempts to get a lock you already own. + */ + if (abstime) { + if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) { + return POSIX_EINVAL; + } else { + std::this_thread::sleep_until(abstime->TimePoint()); + return POSIX_ETIMEDOUT; + } + } + UNREACHABLE_MSG("Mutex deadlock occured"); + return 0; + } + case PthreadMutexType::Recursive: { + /* Increment the lock count: */ + if (m_count + 1 > 0) { + m_count++; + return 0; + } + return POSIX_EAGAIN; + } + default: + return POSIX_EINVAL; + } +} + +int PthreadMutex::Lock(const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + if (m_owner == curthread) { + return SelfLock(abstime); + } + + int ret = 0; + SCOPE_EXIT { + if (ret == 0) { + curthread->Enqueue(this); + } + }; + + /* + * For adaptive mutexes, spin for a bit in the expectation + * that if the application requests this mutex type then + * the lock is likely to be released quickly and it is + * faster than entering the kernel + */ + if (m_protocol == PthreadMutexProt::None) [[likely]] { + int count = m_spinloops; + while (count--) { + if (m_lock.try_lock()) { + return 0; + } + CPU_SPINWAIT; + } + + count = m_yieldloops; + while (count--) { + std::this_thread::yield(); + if (m_lock.try_lock()) { + return 0; + } + } + } + + if (abstime == nullptr) { + m_lock.lock(); + } else if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) [[unlikely]] { + ret = POSIX_EINVAL; + } else { + ret = m_lock.try_lock_until(abstime->TimePoint()) ? 0 : POSIX_ETIMEDOUT; + } + return ret; +} + +int PthreadMutex::TryLock() { + Pthread* curthread = g_curthread; + if (m_owner == curthread) { + return SelfTryLock(); + } + const int ret = m_lock.try_lock() ? 0 : POSIX_EBUSY; + if (ret == 0) { + curthread->Enqueue(this); + } + return ret; +} + +int PS4_SYSV_ABI posix_pthread_mutex_trylock(PthreadMutexT* mutex) { + CHECK_AND_INIT_MUTEX + return (*mutex)->TryLock(); +} + +int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) { + CHECK_AND_INIT_MUTEX + return (*mutex)->Lock(nullptr); +} + +int PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex, + const OrbisKernelTimespec* abstime) { + CHECK_AND_INIT_MUTEX + return (*mutex)->Lock(abstime); +} + +int PthreadMutex::Unlock() { + Pthread* curthread = g_curthread; + /* + * Check if the running thread is not the owner of the mutex. + */ + if (m_owner != curthread) [[unlikely]] { + return POSIX_EPERM; + } + + if (Type() == PthreadMutexType::Recursive && m_count > 0) [[unlikely]] { + m_count--; + } else { + curthread->Dequeue(this); + m_lock.unlock(); + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) { + PthreadMutex* mp = *mutex; + if (mp <= THR_MUTEX_DESTROYED) [[unlikely]] { + if (mp == THR_MUTEX_DESTROYED) { + return POSIX_EINVAL; + } + return POSIX_EPERM; + } + return mp->Unlock(); +} + +int PS4_SYSV_ABI posix_pthread_mutex_getspinloops_np(PthreadMutexT* mutex, int* count) { + CHECK_AND_INIT_MUTEX + *count = (*mutex)->m_spinloops; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_setspinloops_np(PthreadMutexT* mutex, int count) { + CHECK_AND_INIT_MUTEX(*mutex)->m_spinloops = count; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_getyieldloops_np(PthreadMutexT* mutex, int* count) { + CHECK_AND_INIT_MUTEX + *count = (*mutex)->m_yieldloops; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_setyieldloops_np(PthreadMutexT* mutex, int count) { + CHECK_AND_INIT_MUTEX(*mutex)->m_yieldloops = count; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) { + PthreadMutex* m = *mutex; + if (m <= THR_MUTEX_DESTROYED) { + return 0; + } + return m->m_owner == g_curthread; +} + +bool PthreadMutex::IsOwned(Pthread* curthread) const { + if (this <= THR_MUTEX_DESTROYED) [[unlikely]] { + if (this == THR_MUTEX_DESTROYED) { + return POSIX_EINVAL; + } + return POSIX_EPERM; + } + if (m_owner != curthread) { + return POSIX_EPERM; + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) { + PthreadMutexAttrT pattr = (PthreadMutexAttrT)malloc(sizeof(PthreadMutexAttr)); + if (pattr == nullptr) { + return POSIX_ENOMEM; + } + memcpy(pattr, &PthreadMutexattrDefault, sizeof(PthreadMutexAttr)); + *attr = pattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr, + PthreadMutexType kind) { + if (attr == nullptr || *attr == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + (*attr)->m_type = kind; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) { + if (attr == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + return static_cast(attr->m_type); +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) { + if (attr == nullptr || *attr == nullptr || type >= PthreadMutexType::Max) { + return POSIX_EINVAL; + } + (*attr)->m_type = type; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, PthreadMutexType* type) { + if (attr == nullptr || *attr == nullptr || (*attr)->m_type >= PthreadMutexType::Max) { + return POSIX_EINVAL; + } + *type = (*attr)->m_type; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) { + if (attr == nullptr || *attr == nullptr) { + return POSIX_EINVAL; + } + free(*attr); + *attr = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr, + PthreadMutexProt* protocol) { + if (mattr == nullptr || *mattr == nullptr) { + return POSIX_EINVAL; + } + *protocol = (*mattr)->m_protocol; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr, + PthreadMutexProt protocol) { + if (mattr == nullptr || *mattr == nullptr || (protocol < PthreadMutexProt::None) || + (protocol > PthreadMutexProt::Protect)) { + return POSIX_EINVAL; + } + (*mattr)->m_protocol = protocol; + //(*mattr)->m_ceiling = THR_MAX_RR_PRIORITY; + return 0; +} + +void RegisterMutex(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init); + LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); + LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); + LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy); + LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init); + LIB_FUNCTION("mDmgMOGVUqg", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_mutexattr_settype); + LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_mutexattr_setprotocol); + LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_mutexattr_destroy); + LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_trylock); + + // Posix-Kernel + LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); + LIB_FUNCTION("2Z+PpY6CaJg", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); + + // Orbis + LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init)); + LIB_FUNCTION("2Of0f+3mhhE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_destroy)); + LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_init)); + LIB_FUNCTION("smWEktiyyG0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_destroy)); + LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_settype)); + LIB_FUNCTION("1FGvU0i9saQ", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_setprotocol)); + LIB_FUNCTION("9UK1vLZQft4", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_lock)); + LIB_FUNCTION("tn3VlD0hG60", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_unlock)); + LIB_FUNCTION("upoVrzMHFeE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_trylock)); + LIB_FUNCTION("IafI2PxcPnQ", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutex_timedlock)); + LIB_FUNCTION("qH1gXoq71RY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_mutex_init)); + LIB_FUNCTION("n2MMpvU8igI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_mutexattr_init)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_rwlock.cpp b/src/core/libraries/kernel/threads/thr_rwlock.cpp new file mode 100644 index 00000000..95f68590 --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_rwlock.cpp @@ -0,0 +1,313 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/threads/threads.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static std::mutex RwlockStaticLock; + +#define THR_RWLOCK_INITIALIZER ((PthreadRwlock*)NULL) +#define THR_RWLOCK_DESTROYED ((PthreadRwlock*)1) + +#define CHECK_AND_INIT_RWLOCK \ + if (prwlock = (*rwlock); prwlock <= THR_RWLOCK_DESTROYED) [[unlikely]] { \ + if (prwlock == THR_RWLOCK_INITIALIZER) { \ + int ret; \ + ret = InitStatic(g_curthread, rwlock); \ + if (ret) \ + return (ret); \ + } else if (prwlock == THR_RWLOCK_DESTROYED) { \ + return POSIX_EINVAL; \ + } \ + prwlock = *rwlock; \ + } + +static int RwlockInit(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) { + PthreadRwlock* prwlock = (PthreadRwlock*)calloc(1, sizeof(PthreadRwlock)); + if (prwlock == nullptr) { + return POSIX_ENOMEM; + } + std::construct_at(prwlock); + *rwlock = prwlock; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlock_destroy(PthreadRwlockT* rwlock) { + PthreadRwlockT prwlock = *rwlock; + if (prwlock == THR_RWLOCK_INITIALIZER) { + return 0; + } + if (prwlock == THR_RWLOCK_DESTROYED) { + return POSIX_EINVAL; + } + *rwlock = THR_RWLOCK_DESTROYED; + std::destroy_at(prwlock); + free(prwlock); + return 0; +} + +static int InitStatic(Pthread* thread, PthreadRwlockT* rwlock) { + std::scoped_lock lk{RwlockStaticLock}; + if (*rwlock == THR_RWLOCK_INITIALIZER) { + return RwlockInit(rwlock, nullptr); + } + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlock_init(PthreadRwlockT* rwlock, const PthreadRwlockAttrT* attr) { + *rwlock = nullptr; + return RwlockInit(rwlock, attr); +} + +int PthreadRwlock::Rdlock(const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + + /* + * POSIX said the validity of the abstimeout parameter need + * not be checked if the lock can be immediately acquired. + */ + if (lock.try_lock_shared()) { + curthread->rdlock_count++; + return 0; + } + if (abstime && (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0)) [[unlikely]] { + return POSIX_EINVAL; + } + + // Note: On interruption an attempt to relock the mutex is made. + if (abstime != nullptr) { + if (!lock.try_lock_shared_until(abstime->TimePoint())) { + return POSIX_ETIMEDOUT; + } + } else { + lock.lock_shared(); + } + + curthread->rdlock_count++; + return 0; +} + +int PthreadRwlock::Wrlock(const OrbisKernelTimespec* abstime) { + Pthread* curthread = g_curthread; + + /* + * POSIX said the validity of the abstimeout parameter need + * not be checked if the lock can be immediately acquired. + */ + if (lock.try_lock()) { + owner = curthread; + return 0; + } + + if (abstime && (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0)) { + return POSIX_EINVAL; + } + + // Note: On interruption an attempt to relock the mutex is made. + if (abstime != nullptr) { + if (!lock.try_lock_until(abstime->TimePoint())) { + return POSIX_ETIMEDOUT; + } + } else { + lock.lock(); + } + + owner = curthread; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(PthreadRwlockT* rwlock) { + PthreadRwlockT prwlock; + CHECK_AND_INIT_RWLOCK + return prwlock->Rdlock(nullptr); +} + +int PS4_SYSV_ABI posix_pthread_rwlock_timedrdlock(PthreadRwlockT* rwlock, + const OrbisKernelTimespec* abstime) { + PthreadRwlockT prwlock; + CHECK_AND_INIT_RWLOCK + return prwlock->Rdlock(abstime); +} + +int PS4_SYSV_ABI posix_pthread_rwlock_tryrdlock(PthreadRwlockT* rwlock) { + Pthread* curthread = g_curthread; + PthreadRwlockT prwlock; + CHECK_AND_INIT_RWLOCK + + if (!prwlock->lock.try_lock_shared()) { + return POSIX_EBUSY; + } + + curthread->rdlock_count++; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlock_trywrlock(PthreadRwlockT* rwlock) { + Pthread* curthread = g_curthread; + PthreadRwlockT prwlock; + CHECK_AND_INIT_RWLOCK + + if (!prwlock->lock.try_lock()) { + return POSIX_EBUSY; + } + prwlock->owner = curthread; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlock_wrlock(PthreadRwlockT* rwlock) { + PthreadRwlockT prwlock; + CHECK_AND_INIT_RWLOCK + return prwlock->Wrlock(nullptr); +} + +int PS4_SYSV_ABI posix_pthread_rwlock_timedwrlock(PthreadRwlockT* rwlock, + const OrbisKernelTimespec* abstime) { + PthreadRwlockT prwlock; + CHECK_AND_INIT_RWLOCK + return prwlock->Wrlock(abstime); +} + +int PS4_SYSV_ABI posix_pthread_rwlock_unlock(PthreadRwlockT* rwlock) { + Pthread* curthread = g_curthread; + PthreadRwlockT prwlock = *rwlock; + if (prwlock <= THR_RWLOCK_DESTROYED) [[unlikely]] { + return POSIX_EINVAL; + } + + s32 state = prwlock->lock.rw_state; + if (state & URWLOCK_WRITE_OWNER) { + if (prwlock->owner != curthread) [[unlikely]] { + return POSIX_EPERM; + } + prwlock->owner = nullptr; + } + + prwlock->lock.unlock(); + if ((state & URWLOCK_WRITE_OWNER) == 0) { + curthread->rdlock_count--; + } + + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_destroy(PthreadRwlockAttrT* rwlockattr) { + if (rwlockattr == nullptr) { + return POSIX_EINVAL; + } + PthreadRwlockAttrT prwlockattr = *rwlockattr; + if (prwlockattr == nullptr) { + return POSIX_EINVAL; + } + + free(prwlockattr); + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_getpshared(const PthreadRwlockAttrT* rwlockattr, + int* pshared) { + *pshared = (*rwlockattr)->pshared; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_init(PthreadRwlockAttrT* rwlockattr) { + if (rwlockattr == nullptr) { + return POSIX_EINVAL; + } + + PthreadRwlockAttrT prwlockattr = (PthreadRwlockAttrT)malloc(sizeof(PthreadRwlockAttr)); + if (prwlockattr == nullptr) { + return POSIX_ENOMEM; + } + + prwlockattr->pshared = PTHREAD_PROCESS_PRIVATE; + *rwlockattr = prwlockattr; + return 0; +} + +int PS4_SYSV_ABI posix_pthread_rwlockattr_setpshared(PthreadRwlockAttrT* rwlockattr, int pshared) { + /* Only PTHREAD_PROCESS_PRIVATE is supported. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) { + return POSIX_EINVAL; + } + + (*rwlockattr)->pshared = pshared; + return 0; +} + +void RegisterRwlock(Core::Loader::SymbolsResolver* sym) { + // Posix-Kernel + LIB_FUNCTION("1471ajPzxh0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy); + LIB_FUNCTION("ytQULN-nhL4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_init); + LIB_FUNCTION("iGjsr1WAtI0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock); + LIB_FUNCTION("lb8lnYo-o7k", "libkernel", 1, "libkernel", 1, 1, + posix_pthread_rwlock_timedrdlock); + LIB_FUNCTION("9zklzAl9CGM", "libkernel", 1, "libkernel", 1, 1, + posix_pthread_rwlock_timedwrlock); + LIB_FUNCTION("SFxTMOfuCkE", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_tryrdlock); + LIB_FUNCTION("XhWHn6P5R7U", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_trywrlock); + LIB_FUNCTION("EgmLo6EWgso", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_unlock); + LIB_FUNCTION("sIlRvQqsN2Y", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlock_wrlock); + LIB_FUNCTION("qsdmgXjqSgk", "libkernel", 1, "libkernel", 1, 1, + posix_pthread_rwlockattr_destroy); + LIB_FUNCTION("VqEMuCv-qHY", "libkernel", 1, "libkernel", 1, 1, + posix_pthread_rwlockattr_getpshared); + LIB_FUNCTION("xFebsA4YsFI", "libkernel", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init); + LIB_FUNCTION("OuKg+kRDD7U", "libkernel", 1, "libkernel", 1, 1, + posix_pthread_rwlockattr_setpshared); + + // Posix + LIB_FUNCTION("1471ajPzxh0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_destroy); + LIB_FUNCTION("ytQULN-nhL4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_init); + LIB_FUNCTION("iGjsr1WAtI0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_rdlock); + LIB_FUNCTION("lb8lnYo-o7k", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_rwlock_timedrdlock); + LIB_FUNCTION("9zklzAl9CGM", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_rwlock_timedwrlock); + LIB_FUNCTION("SFxTMOfuCkE", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_rwlock_tryrdlock); + LIB_FUNCTION("XhWHn6P5R7U", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_rwlock_trywrlock); + LIB_FUNCTION("EgmLo6EWgso", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_unlock); + LIB_FUNCTION("sIlRvQqsN2Y", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlock_wrlock); + LIB_FUNCTION("qsdmgXjqSgk", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_rwlockattr_destroy); + LIB_FUNCTION("VqEMuCv-qHY", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_rwlockattr_getpshared); + LIB_FUNCTION("xFebsA4YsFI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_rwlockattr_init); + LIB_FUNCTION("OuKg+kRDD7U", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_rwlockattr_setpshared); + + // Orbis + LIB_FUNCTION("i2ifZ3fS2fo", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_destroy)); + LIB_FUNCTION("LcOZBHGqbFk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_getpshared)); + LIB_FUNCTION("yOfGg-I1ZII", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_init)); + LIB_FUNCTION("-ZvQH18j10c", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlockattr_setpshared)); + LIB_FUNCTION("BB+kb08Tl9A", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_destroy)); + LIB_FUNCTION("6ULAa0fq4jA", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_init)); + LIB_FUNCTION("Ox9i0c7L5w0", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_rdlock)); + LIB_FUNCTION("iPtZRWICjrM", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_timedrdlock)); + LIB_FUNCTION("adh--6nIqTk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_timedwrlock)); + LIB_FUNCTION("XD3mDeybCnk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_tryrdlock)); + LIB_FUNCTION("bIHoZCTomsI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_trywrlock)); + LIB_FUNCTION("+L98PIbGttk", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_unlock)); + LIB_FUNCTION("mqdNorrB+gI", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_rwlock_wrlock)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/semaphore.cpp b/src/core/libraries/kernel/threads/thr_sem.cpp similarity index 68% rename from src/core/libraries/kernel/threads/semaphore.cpp rename to src/core/libraries/kernel/threads/thr_sem.cpp index 59099c1b..4eb59b18 100644 --- a/src/core/libraries/kernel/threads/semaphore.cpp +++ b/src/core/libraries/kernel/threads/thr_sem.cpp @@ -4,21 +4,30 @@ #include #include #include -#include - #include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/time_management.h" #include "core/libraries/libs.h" namespace Libraries::Kernel { -class Semaphore { +constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF; + +struct PthreadSem { + explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {} + + std::counting_semaphore semaphore; + std::atomic value; +}; + +class OrbisSem { public: - Semaphore(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) + OrbisSem(s32 init_count, s32 max_count, std::string_view name, bool is_fifo) : name{name}, token_count{init_count}, max_count{max_count}, init_count{init_count}, is_fifo{is_fifo} {} - ~Semaphore() { + ~OrbisSem() { ASSERT(wait_list.empty()); } @@ -163,7 +172,7 @@ public: bool is_fifo; }; -using OrbisKernelSema = Semaphore*; +using OrbisKernelSema = OrbisSem*; s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u32 attr, s32 initCount, s32 maxCount, const void* pOptParam) { @@ -171,7 +180,7 @@ s32 PS4_SYSV_ABI sceKernelCreateSema(OrbisKernelSema* sem, const char* pName, u3 LOG_ERROR(Lib_Kernel, "Semaphore creation parameters are invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; } - *sem = new Semaphore(initCount, maxCount, pName, attr == 1); + *sem = new OrbisSem(initCount, maxCount, pName, attr == 1); return ORBIS_OK; } @@ -214,13 +223,105 @@ int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) { return ORBIS_OK; } -void SemaphoreSymbolsRegister(Core::Loader::SymbolsResolver* sym) { +int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, unsigned int value) { + if (value > ORBIS_KERNEL_SEM_VALUE_MAX) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (sem != nullptr) { + *sem = new PthreadSem(value); + } + return 0; +} + +int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + (*sem)->semaphore.acquire(); + --(*sem)->value; + return 0; +} + +int PS4_SYSV_ABI posix_sem_trywait(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (!(*sem)->semaphore.try_acquire()) { + *__Error() = POSIX_EAGAIN; + return -1; + } + --(*sem)->value; + return 0; +} + +int PS4_SYSV_ABI posix_sem_timedwait(PthreadSem** sem, const OrbisKernelTimespec* t) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (!(*sem)->semaphore.try_acquire_until(t->TimePoint())) { + *__Error() = POSIX_ETIMEDOUT; + return -1; + } + --(*sem)->value; + return 0; +} + +int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if ((*sem)->value == ORBIS_KERNEL_SEM_VALUE_MAX) { + *__Error() = POSIX_EOVERFLOW; + return -1; + } + ++(*sem)->value; + (*sem)->semaphore.release(); + return 0; +} + +int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + delete *sem; + *sem = nullptr; + return 0; +} + +int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) { + if (sem == nullptr || *sem == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + if (sval) { + *sval = (*sem)->value; + } + return 0; +} + +void RegisterSemaphore(Core::Loader::SymbolsResolver* sym) { + // Orbis LIB_FUNCTION("188x57JYp0g", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateSema); LIB_FUNCTION("Zxa0VhQVTsk", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitSema); LIB_FUNCTION("4czppHBiriw", "libkernel", 1, "libkernel", 1, 1, sceKernelSignalSema); LIB_FUNCTION("12wOHk8ywb0", "libkernel", 1, "libkernel", 1, 1, sceKernelPollSema); LIB_FUNCTION("4DM06U2BNEY", "libkernel", 1, "libkernel", 1, 1, sceKernelCancelSema); LIB_FUNCTION("R1Jvn8bSCW8", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteSema); + + // Posix + LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init); + LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait); + LIB_FUNCTION("WBWzsRifCEA", "libScePosix", 1, "libkernel", 1, 1, posix_sem_trywait); + LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait); + LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post); + LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy); + LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_spec.cpp b/src/core/libraries/kernel/threads/thr_spec.cpp new file mode 100644 index 00000000..c5b26ad4 --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_spec.cpp @@ -0,0 +1,171 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/libkernel.h" +#include "core/libraries/kernel/threads/threads.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +static constexpr u32 PthreadKeysMax = 256; +static constexpr u32 PthreadDestructorIterations = 4; + +static std::array ThreadKeytable{}; +static std::mutex KeytableLock; + +int PS4_SYSV_ABI posix_pthread_key_create(PthreadKeyT* key, void (*destructor)(const void*)) { + std::scoped_lock lk{KeytableLock}; + const auto it = std::ranges::find(ThreadKeytable, 0, &PthreadKey::allocated); + if (it != ThreadKeytable.end()) { + it->allocated = 1; + it->destructor = destructor; + it->seqno++; + *key = std::distance(ThreadKeytable.begin(), it); + return 0; + } + return POSIX_EAGAIN; +} + +int PS4_SYSV_ABI posix_pthread_key_delete(PthreadKeyT key) { + if (key >= PthreadKeysMax) { + return POSIX_EINVAL; + } + + std::scoped_lock lk{KeytableLock}; + if (!ThreadKeytable[key].allocated) { + return POSIX_EINVAL; + } + + ThreadKeytable[key].allocated = 0; + return 0; +} + +void _thread_cleanupspecific() { + Pthread* curthread = g_curthread; + void (*destructor)(const void*); + const void* data = NULL; + + if (curthread->specific == nullptr) { + return; + } + + std::unique_lock lk{KeytableLock}; + for (int i = 0; (i < PthreadDestructorIterations) && (curthread->specific_data_count > 0); + i++) { + for (int key = 0; (key < PthreadKeysMax) && (curthread->specific_data_count > 0); key++) { + destructor = nullptr; + + if (ThreadKeytable[key].allocated && (curthread->specific[key].data != nullptr)) { + if (curthread->specific[key].seqno == ThreadKeytable[key].seqno) { + data = curthread->specific[key].data; + destructor = ThreadKeytable[key].destructor; + } + curthread->specific[key].data = nullptr; + curthread->specific_data_count--; + } else if (curthread->specific[key].data != NULL) { + /* + * This can happen if the key is deleted via + * pthread_key_delete without first setting the value + * to NULL in all threads. POSIX says that the + * destructor is not invoked in this case. + */ + curthread->specific[key].data = nullptr; + curthread->specific_data_count--; + } + + /* + * If there is a destructor, call it + * with the key table entry unlocked: + */ + if (destructor != nullptr) { + /* + * Don't hold the lock while calling the + * destructor: + */ + lk.unlock(); + destructor(data); + lk.lock(); + } + } + } + free(curthread->specific); + curthread->specific = nullptr; + ASSERT(curthread->specific_data_count == 0); +} + +int PS4_SYSV_ABI posix_pthread_setspecific(PthreadKeyT key, const void* value) { + int ret = 0; + Pthread* pthread = g_curthread; + + if (!pthread->specific) { + pthread->specific = + (PthreadSpecificElem*)calloc(1, sizeof(PthreadSpecificElem) * PthreadKeysMax); + if (!pthread->specific) { + return POSIX_ENOMEM; + } + } + if (key >= PthreadKeysMax) { + return POSIX_EINVAL; + } + if (!ThreadKeytable[key].allocated) { + return POSIX_EINVAL; + } + + if (pthread->specific[key].data == nullptr) { + if (value != nullptr) { + pthread->specific_data_count++; + } + } else if (value == nullptr) { + pthread->specific_data_count--; + } + pthread->specific[key].data = value; + pthread->specific[key].seqno = ThreadKeytable[key].seqno; + return 0; +} + +const void* PS4_SYSV_ABI posix_pthread_getspecific(PthreadKeyT key) { + Pthread* pthread = g_curthread; + + if (!pthread->specific || key >= PthreadKeysMax) { + return nullptr; + } + + if (ThreadKeytable[key].allocated && + (pthread->specific[key].seqno == ThreadKeytable[key].seqno)) { + return pthread->specific[key].data; + } + + return nullptr; +} + +void _thr_tsd_unload(struct dl_phdr_info* phdr_info) { + std::scoped_lock lk{KeytableLock}; + for (int key = 0; key < PTHREAD_KEYS_MAX; key++) { + if (!ThreadKeytable[key].allocated) { + continue; + } + const auto destructor = ThreadKeytable[key].destructor; + if (destructor != nullptr) { + if (__elf_phdr_match_addr(phdr_info, destructor)) + ThreadKeytable[key].destructor = nullptr; + } + } +} + +void RegisterSpec(Core::Loader::SymbolsResolver* sym) { + // Posix + LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create); + LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific); + LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific); + + // Orbis + LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_key_create)); + LIB_FUNCTION("eoht7mQOCmo", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_getspecific)); + LIB_FUNCTION("+BzXYkqYeLE", "libkernel", 1, "libkernel", 1, 1, + ORBIS(posix_pthread_setspecific)); +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thr_stack.cpp b/src/core/libraries/kernel/threads/thr_stack.cpp new file mode 100644 index 00000000..ec4a956a --- /dev/null +++ b/src/core/libraries/kernel/threads/thr_stack.cpp @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/libraries/kernel/threads/threads.h" +#include "core/memory.h" + +namespace Libraries::Kernel { + +static constexpr size_t RoundUp(size_t size) { + if (size % ThrPageSize != 0) { + size = ((size / ThrPageSize) + 1) * ThrPageSize; + } + return size; +} + +int ThreadState::CreateStack(PthreadAttr* attr) { + if ((attr->stackaddr_attr) != NULL) { + attr->guardsize_attr = 0; + attr->flags |= PthreadAttrFlags::StackUser; + return 0; + } + + VAddr stackaddr; + + /* + * Round up stack size to nearest multiple of _thr_page_size so + * that mmap() * will work. If the stack size is not an even + * multiple, we end up initializing things such that there is + * unused space above the beginning of the stack, so the stack + * sits snugly against its guard. + */ + size_t stacksize = RoundUp(attr->stacksize_attr); + size_t guardsize = RoundUp(attr->guardsize_attr); + + attr->stackaddr_attr = NULL; + attr->flags &= ~PthreadAttrFlags::StackUser; + + /* + * Use the garbage collector lock for synchronization of the + * spare stack lists and allocations from usrstack. + */ + thread_list_lock.lock(); + + /* + * If the stack and guard sizes are default, try to allocate a stack + * from the default-size stack cache: + */ + if (stacksize == ThrStackDefault && guardsize == ThrGuardDefault) { + if (!dstackq.empty()) { + /* Use the spare stack. */ + Stack* spare_stack = dstackq.top(); + dstackq.pop(); + attr->stackaddr_attr = spare_stack->stackaddr; + } + } + /* + * The user specified a non-default stack and/or guard size, so try to + * allocate a stack from the non-default size stack cache, using the + * rounded up stack size (stack_size) in the search: + */ + else { + const auto it = std::ranges::find_if(mstackq, [&](Stack* stack) { + return stack->stacksize == stacksize && stack->guardsize == guardsize; + }); + if (it != mstackq.end()) { + attr->stackaddr_attr = (*it)->stackaddr; + mstackq.erase(it); + } + } + + /* A cached stack was found. Release the lock. */ + if (attr->stackaddr_attr != NULL) { + thread_list_lock.unlock(); + return 0; + } + + /* Allocate a stack from usrstack. */ + if (last_stack == 0) { + last_stack = _usrstack - ThrStackInitial - ThrGuardDefault; + } + + /* Allocate a new stack. */ + stackaddr = last_stack - stacksize - guardsize; + + /* + * Even if stack allocation fails, we don't want to try to + * use this location again, so unconditionally decrement + * last_stack. Under normal operating conditions, the most + * likely reason for an mmap() error is a stack overflow of + * the adjacent thread stack. + */ + last_stack -= (stacksize + guardsize); + + /* Release the lock before mmap'ing it. */ + thread_list_lock.unlock(); + + /* Map the stack and guard page together, and split guard + page from allocated space: */ + auto* memory = Core::Memory::Instance(); + int ret = memory->MapMemory(reinterpret_cast(&stackaddr), stackaddr, + stacksize + guardsize, Core::MemoryProt::CpuReadWrite, + Core::MemoryMapFlags::NoFlags, Core::VMAType::Stack); + ASSERT_MSG(ret == 0, "Unable to map stack memory"); + + ret = memory->Protect(stackaddr, guardsize, Core::MemoryProt::NoAccess); + ASSERT_MSG(ret == 0, "Unable to protect guard page"); + + stackaddr += guardsize; + attr->stackaddr_attr = (void*)stackaddr; + + if (attr->stackaddr_attr != nullptr) { + return 0; + } + return -1; +} + +void ThreadState::FreeStack(PthreadAttr* attr) { + if (!attr || True(attr->flags & PthreadAttrFlags::StackUser) || !attr->stackaddr_attr) { + return; + } + + char* stack_base = (char*)attr->stackaddr_attr; + Stack* spare_stack = (Stack*)(stack_base + attr->stacksize_attr - sizeof(Stack)); + spare_stack->stacksize = RoundUp(attr->stacksize_attr); + spare_stack->guardsize = RoundUp(attr->guardsize_attr); + spare_stack->stackaddr = attr->stackaddr_attr; + + if (spare_stack->stacksize == ThrStackDefault && spare_stack->guardsize == ThrGuardDefault) { + /* Default stack/guard size. */ + dstackq.push(spare_stack); + } else { + /* Non-default stack/guard size. */ + mstackq.push_back(spare_stack); + } + attr->stackaddr_attr = nullptr; +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thread_state.cpp b/src/core/libraries/kernel/threads/thread_state.cpp new file mode 100644 index 00000000..f340952a --- /dev/null +++ b/src/core/libraries/kernel/threads/thread_state.cpp @@ -0,0 +1,136 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/scope_exit.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/kernel/threads/thread_state.h" +#include "core/libraries/kernel/threads/threads.h" +#include "core/tls.h" + +namespace Libraries::Kernel { + +Core::Tcb* TcbCtor(Pthread* thread, int initial); +void TcbDtor(Core::Tcb* oldtls); + +void ThreadState::Collect(Pthread* curthread) { + boost::container::small_vector work_list; + { + std::scoped_lock lk{thread_list_lock}; + for (auto it = gc_list.begin(); it != gc_list.end();) { + Pthread* td = *it; + if (td->tid != TidTerminated) { + it++; + continue; + } + FreeStack(&td->attr); + work_list.push_back(td); + it = gc_list.erase(it); + } + } + for (Pthread* td : work_list) { + Free(curthread, td); + } +} + +void ThreadState::TryCollect(Pthread* curthread, Pthread* thread) { + SCOPE_EXIT { + thread->lock->unlock(); + }; + if (!thread->ShouldCollect()) { + return; + } + + thread->refcount++; + thread->lock->unlock(); + std::scoped_lock lk{thread_list_lock}; + thread->lock->lock(); + thread->refcount--; + if (thread->ShouldCollect()) { + threads.erase(thread); + gc_list.push_back(thread); + } +} + +Pthread* ThreadState::Alloc(Pthread* curthread) { + Pthread* thread = nullptr; + if (curthread != nullptr) { + if (GcNeeded()) { + Collect(curthread); + } + if (!free_threads.empty()) { + std::scoped_lock lk{free_thread_lock}; + thread = free_threads.back(); + free_threads.pop_back(); + } + } + if (thread == nullptr) { + if (total_threads > MaxThreads) { + return nullptr; + } + total_threads.fetch_add(1); + thread = (Pthread*)malloc(sizeof(Pthread)); + std::construct_at(thread); + thread->lock = std::make_unique(); + if (thread == nullptr) { + total_threads.fetch_sub(1); + return nullptr; + } + } + Core::Tcb* tcb = nullptr; + if (curthread != nullptr) { + std::scoped_lock lk{tcb_lock}; + tcb = TcbCtor(thread, 0 /* not initial tls */); + } else { + tcb = TcbCtor(thread, 1 /* initial tls */); + } + if (tcb != nullptr) { + memset(thread, 0, sizeof(*thread)); + thread->tcb = tcb; + // thread->sleepqueue = _sleepq_alloc(); + // thread->wake_addr = _thr_alloc_wake_addr(); + } else { + free(thread); + total_threads.fetch_sub(1); + thread = nullptr; + } + return thread; +} + +void ThreadState::Free(Pthread* curthread, Pthread* thread) { + if (curthread != nullptr) { + std::scoped_lock lk{tcb_lock}; + TcbDtor(thread->tcb); + } else { + TcbDtor(thread->tcb); + } + thread->tcb = NULL; + if (free_threads.size() >= MaxCachedThreads) { + //_sleepq_free(thread->sleepqueue); + //_thr_release_wake_addr(thread->wake_addr); + free(thread); + total_threads.fetch_sub(1); + } else { + std::scoped_lock lk{free_thread_lock}; + free_threads.push_back(thread); + } +} + +int ThreadState::FindThread(Pthread* thread, bool include_dead) { + if (thread == nullptr) { + return POSIX_EINVAL; + } + std::scoped_lock lk{thread_list_lock}; + const auto it = threads.find(thread); + if (it == threads.end()) { + return POSIX_ESRCH; + } + thread->lock->lock(); + if (!include_dead && thread->state == PthreadState::Dead) { + thread->lock->unlock(); + return POSIX_ESRCH; + } + return 0; +} + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/thread_state.h b/src/core/libraries/kernel/threads/thread_state.h new file mode 100644 index 00000000..6c65a537 --- /dev/null +++ b/src/core/libraries/kernel/threads/thread_state.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include "common/singleton.h" +#include "common/types.h" + +namespace Libraries::Kernel { + +struct Pthread; +struct PthreadAttr; + +struct Stack { + size_t stacksize; /* Stack size (rounded up). */ + size_t guardsize; /* Guard size. */ + void* stackaddr; /* Stack address. */ +}; + +struct ThreadState { + static constexpr size_t GcThreshold = 5; + static constexpr size_t MaxThreads = 100000; + static constexpr size_t MaxCachedThreads = 100; + + bool GcNeeded() const noexcept { + return gc_list.size() >= GcThreshold; + } + + void Collect(Pthread* curthread); + + void TryCollect(Pthread* curthread, Pthread* thread); + + Pthread* Alloc(Pthread* curthread); + + void Free(Pthread* curthread, Pthread* thread); + + int FindThread(Pthread* thread, bool include_dead); + + int CreateStack(PthreadAttr* attr); + + void FreeStack(PthreadAttr* attr); + + void Link(Pthread* curthread, Pthread* thread) { + { + std::scoped_lock lk{thread_list_lock}; + threads.insert(thread); + } + active_threads.fetch_add(1); + } + + void Unlink(Pthread* curthread, Pthread* thread) { + { + std::scoped_lock lk{thread_list_lock}; + threads.erase(thread); + } + active_threads.fetch_sub(1); + } + + std::set threads; + std::list free_threads; + std::list gc_list; + std::mutex free_thread_lock; + std::mutex tcb_lock; + std::mutex thread_list_lock; + std::atomic total_threads{}; + std::atomic active_threads{}; + std::stack dstackq; + std::list mstackq; + VAddr last_stack = 0; +}; + +using ThrState = Common::Singleton; + +} // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/threads.h b/src/core/libraries/kernel/threads/threads.h index a3fd354b..bc3fffce 100644 --- a/src/core/libraries/kernel/threads/threads.h +++ b/src/core/libraries/kernel/threads/threads.h @@ -3,7 +3,16 @@ #pragma once -#include "core/libraries/kernel/thread_management.h" +#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; @@ -11,10 +20,267 @@ class SymbolsResolver; namespace Libraries::Kernel { -int PS4_SYSV_ABI scePthreadRwlockattrInit(OrbisPthreadRwlockattr* attr); +struct Pthread; -void SemaphoreSymbolsRegister(Core::Loader::SymbolsResolver* sym); -void RwlockSymbolsRegister(Core::Loader::SymbolsResolver* sym); -void KeySymbolsRegister(Core::Loader::SymbolsResolver* sym); +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; + + PthreadMutexType Type() const noexcept { + return static_cast(m_flags & PthreadMutexFlags::TypeMask); + } + + int SelfTryLock(); + int SelfLock(const OrbisKernelTimespec* abstime); + + int TryLock(); + int Lock(const OrbisKernelTimespec* abstime); + + 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, +}; + +struct PthreadCond { + std::condition_variable_any cond; + u32 has_user_waiters; + u32 has_kern_waiters; + u32 flags; + u32 clock_id; + + int Wait(PthreadMutexT* mutex, const OrbisKernelTimespec* abstime); +}; +using PthreadCondT = PthreadCond*; + +struct PthreadCondAttr { + int c_pshared; + int 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 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; +}; +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, +}; + +constexpr u32 TidTerminated = 1; + +struct Pthread { + static constexpr u32 ThrMagic = 0xd09ba115U; + + std::atomic tid; + std::unique_ptr 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; + boost::intrusive::list mutexq; + boost::intrusive::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; + + 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 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); + +inline void RegisterThreads(Core::Loader::SymbolsResolver* sym) { + RegisterMutex(sym); + RegisterCond(sym); + RegisterRwlock(sym); + RegisterSemaphore(sym); + RegisterSpec(sym); + RegisterThreadAttr(sym); + RegisterThread(sym); +} } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/time_management.h b/src/core/libraries/kernel/time_management.h index c92c30c5..84f68c85 100644 --- a/src/core/libraries/kernel/time_management.h +++ b/src/core/libraries/kernel/time_management.h @@ -3,8 +3,8 @@ #pragma once +#include #include - #include "common/types.h" namespace Common { @@ -30,6 +30,12 @@ struct OrbisKernelTimezone { struct OrbisKernelTimespec { s64 tv_sec; s64 tv_nsec; + + std::chrono::system_clock::time_point TimePoint() const noexcept { + using namespace std::chrono; + const auto duration = seconds{tv_sec} + nanoseconds{tv_nsec}; + return system_clock::time_point{duration}; + } }; struct OrbisTimesec { diff --git a/src/core/libraries/libs.h b/src/core/libraries/libs.h index ea928101..045de78d 100644 --- a/src/core/libraries/libs.h +++ b/src/core/libraries/libs.h @@ -9,43 +9,6 @@ #include "core/loader/elf.h" #include "core/loader/symbols_resolver.h" -template -struct StringLiteral { - constexpr StringLiteral(const char (&str)[N]) { - std::copy_n(str, N, value); - } - - char value[N]; -}; - -template -struct wrapper_impl; - -template -struct wrapper_impl { - static R PS4_SYSV_ABI wrap(Args... args) { - if (std::string_view(name.value) != "scePthreadEqual" && - std::string_view(name.value) != "sceUserServiceGetEvent") { - // LOG_WARNING(Core_Linker, "Function {} called", name.value); - } - if constexpr (std::is_same_v || std::is_same_v) { - const u32 ret = f(args...); - if (ret != 0 && std::string_view(name.value) != "scePthreadEqual") { - LOG_WARNING(Core_Linker, "Function {} returned {:#x}", name.value, ret); - } - return ret; - } - // stuff - return f(args...); - } -}; - -template -constexpr auto wrapper = wrapper_impl::wrap; - -// #define W(foo) wrapper<#foo, decltype(&foo), foo> -#define W(foo) foo - #define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \ { \ Core::Loader::SymbolResolver sr{}; \ @@ -56,7 +19,7 @@ constexpr auto wrapper = wrapper_impl::wrap; sr.module_version_major = moduleVersionMajor; \ sr.module_version_minor = moduleVersionMinor; \ sr.type = Core::Loader::SymbolType::Function; \ - auto func = reinterpret_cast(W(function)); \ + auto func = reinterpret_cast(function); \ sym->AddSymbol(sr, func); \ } diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 2d7865c3..a0886b51 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -326,7 +326,6 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { // Module was just loaded by above code. Allocate TLS block for it. Module* module = m_modules[module_index - 1].get(); const u32 init_image_size = module->tls.init_image_size; - // TODO: Determine if Windows will crash from this u8* dest = reinterpret_cast(ExecuteGuest(heap_api->heap_malloc, module->tls.image_size)); const u8* src = reinterpret_cast(module->tls.image_virtual_addr); @@ -349,7 +348,7 @@ void Linker::EnsureThreadInitialized(bool is_primary) const { }); } -void Linker::InitTlsForThread(bool is_primary) const { +void* Linker::AllocateTlsForThread(bool is_primary) { static constexpr size_t TcbSize = 0x40; static constexpr size_t TlsAllocAlign = 0x20; const size_t total_tls_size = Common::AlignUp(static_tls_size, TlsAllocAlign) + TcbSize; @@ -370,54 +369,12 @@ void Linker::InitTlsForThread(bool is_primary) const { ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread"); } else { if (heap_api) { -#ifndef WIN32 - addr_out = ExecuteGuestWithoutTls(heap_api->heap_malloc, total_tls_size); + addr_out = heap_api->heap_malloc(total_tls_size); } else { addr_out = std::malloc(total_tls_size); -#else - // TODO: Windows tls malloc replacement, refer to rtld_tls_block_malloc - LOG_ERROR(Core_Linker, "TLS user malloc called, using std::malloc"); - addr_out = std::malloc(total_tls_size); - if (!addr_out) { - auto pth_id = pthread_self(); - auto handle = pthread_gethandle(pth_id); - ASSERT_MSG(addr_out, - "Cannot allocate TLS block defined for handle=%x, index=%d size=%d", - handle, pth_id, total_tls_size); - } -#endif } } - - // Initialize allocated memory and allocate DTV table. - const u32 num_dtvs = max_tls_index; - std::memset(addr_out, 0, total_tls_size); - DtvEntry* dtv_table = new DtvEntry[num_dtvs + 2]; - - // Initialize thread control block - u8* addr = reinterpret_cast(addr_out); - Tcb* tcb = reinterpret_cast(addr + static_tls_size); - tcb->tcb_self = tcb; - tcb->tcb_dtv = dtv_table; - - // Dtv[0] is the generation counter. libkernel puts their number into dtv[1] (why?) - dtv_table[0].counter = dtv_generation_counter; - dtv_table[1].counter = num_dtvs; - - // Copy init images to TLS thread blocks and map them to DTV slots. - for (u32 i = 0; i < num_static_modules; i++) { - auto* module = m_modules[i].get(); - if (module->tls.image_size == 0) { - continue; - } - u8* dest = reinterpret_cast(addr + static_tls_size - module->tls.offset); - const u8* src = reinterpret_cast(module->tls.image_virtual_addr); - std::memcpy(dest, src, module->tls.init_image_size); - tcb->tcb_dtv[module->tls.modid + 1].pointer = dest; - } - - // Set pointer to FS base - SetTcbBase(tcb); + return addr_out; } void Linker::DebugDump() { diff --git a/src/core/linker.h b/src/core/linker.h index fe1278d0..216ca33e 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -79,6 +79,18 @@ public: return m_modules.at(index).get(); } + u32 MaxTlsIndex() const { + return max_tls_index; + } + + u32 GenerationCounter() const { + return dtv_generation_counter; + } + + size_t StaticTlsSize() const noexcept { + return static_tls_size; + } + void RelocateAnyImports(Module* m) { Relocate(m); for (auto& module : m_modules) { @@ -98,6 +110,7 @@ public: } void* TlsGetAddr(u64 module_index, u64 offset); + void* AllocateTlsForThread(bool is_primary); s32 LoadModule(const std::filesystem::path& elf_name, bool is_dynamic = false); Module* FindByAddress(VAddr address); diff --git a/src/core/memory.h b/src/core/memory.h index 286f1c97..be2bcb87 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -148,6 +148,13 @@ public: VAddr SystemReservedVirtualBase() noexcept { return impl.SystemReservedVirtualBase(); } + + bool IsValidAddress(const void* addr) const noexcept { + const VAddr virtual_addr = reinterpret_cast(addr); + const auto end_it = std::prev(vma_map.end()); + const VAddr end_addr = end_it->first + end_it->second.size; + return virtual_addr >= vma_map.begin()->first && virtual_addr < end_addr; + } bool TryWriteBacking(void* address, const void* data, u32 num_bytes);