mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-22 04:24:44 +00:00
kernel: Refactor thread functions
This commit is contained in:
parent
a0ed0bb0d2
commit
7a6809e6b7
25 changed files with 2592 additions and 1842 deletions
|
@ -191,10 +191,14 @@ set(KERNEL_LIB
|
|||
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
|
||||
|
@ -718,6 +722,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()
|
||||
|
||||
|
@ -838,4 +847,4 @@ if (UNIX AND NOT APPLE)
|
|||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
|
@ -17,17 +17,28 @@ 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 <class F, F f>
|
||||
struct WrapperImpl;
|
||||
|
||||
template <class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
|
||||
struct WrapperImpl<PS4_SYSV_ABI R (*)(Args...), f> {
|
||||
static R PS4_SYSV_ABI wrap(Args... args) {
|
||||
u32 ret = f(args...);
|
||||
if (ret != 0) {
|
||||
ret += SCE_KERNEL_ERROR_UNKNOWN;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
template <class F, F f>
|
||||
constexpr auto OrbisWrapper = WrapperImpl<F, f>::wrap;
|
||||
|
||||
#define ORBIS(func) OrbisWrapper<decltype(&func), func>
|
||||
|
||||
int* PS4_SYSV_ABI __Error();
|
||||
|
||||
void LibKernel_Register(Core::Loader::SymbolsResolver* sym);
|
||||
void RegisterKernel(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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<OrbisPthreadKey>(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
|
|
@ -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<VAddr>(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
|
266
src/core/libraries/kernel/threads/thr_attr.cpp
Normal file
266
src/core/libraries/kernel/threads/thr_attr.cpp
Normal file
|
@ -0,0 +1,266 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/libraries/kernel/threads/threads.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
#include "core/libraries/kernel/threads/thread_state.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<PthreadPrio, 3> 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
|
43
src/core/libraries/kernel/threads/thr_clean.cpp
Normal file
43
src/core/libraries/kernel/threads/thr_clean.cpp
Normal file
|
@ -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
|
229
src/core/libraries/kernel/threads/thr_cond.cpp
Normal file
229
src/core/libraries/kernel/threads/thr_cond.cpp
Normal file
|
@ -0,0 +1,229 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/threads/threads.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/kernel/libkernel.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;
|
||||
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<ClockId>((*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<int>(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
|
107
src/core/libraries/kernel/threads/thr_create.cpp
Normal file
107
src/core/libraries/kernel/threads/thr_create.cpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
// 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/threads/threads.h"
|
||||
#include "core/libraries/kernel/threads/thread_state.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
extern PthreadAttr PthreadAttrDefault;
|
||||
|
||||
using PthreadEntryFunc = void *(*)(void *);
|
||||
|
||||
void PS4_SYSV_ABI posix_pthread_exit(void *status);
|
||||
|
||||
static void RunThread(Pthread* curthread) {
|
||||
g_curthread = curthread;
|
||||
|
||||
/* Run the current thread's start routine with argument: */
|
||||
posix_pthread_exit(curthread->start_routine(curthread->arg));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
92
src/core/libraries/kernel/threads/thr_ctrdtr.cpp
Normal file
92
src/core/libraries/kernel/threads/thr_ctrdtr.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/alignment.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<Core::Linker>::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<u8*>(addr_out);
|
||||
auto* tcb = reinterpret_cast<Core::Tcb*>(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<u8*>(addr + static_tls_size - module->tls.offset);
|
||||
|
||||
if (module->tls.image_virtual_addr != 0) {
|
||||
const u8* src = reinterpret_cast<const u8*>(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<Core::Linker>::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;
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
159
src/core/libraries/kernel/threads/thr_exit.cpp
Normal file
159
src/core/libraries/kernel/threads/thr_exit.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <pthread.h>
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/threads/threads.h"
|
||||
#include "core/libraries/kernel/threads/thread_state.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);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
433
src/core/libraries/kernel/threads/thr_mutex.cpp
Normal file
433
src/core/libraries/kernel/threads/thr_mutex.cpp
Normal file
|
@ -0,0 +1,433 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/kernel/threads/threads.h"
|
||||
#include "core/libraries/kernel/libkernel.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;
|
||||
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<int>(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
|
298
src/core/libraries/kernel/threads/thr_rwlock.cpp
Normal file
298
src/core/libraries/kernel/threads/thr_rwlock.cpp
Normal file
|
@ -0,0 +1,298 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/kernel/threads/threads.h"
|
||||
#include "core/libraries/kernel/libkernel.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;
|
||||
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
|
|
@ -4,21 +4,30 @@
|
|||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
#include "core/libraries/kernel/time_management.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<ORBIS_KERNEL_SEM_VALUE_MAX> semaphore;
|
||||
std::atomic<s32> 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
|
168
src/core/libraries/kernel/threads/thr_spec.cpp
Normal file
168
src/core/libraries/kernel/threads/thr_spec.cpp
Normal file
|
@ -0,0 +1,168 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/kernel/threads/threads.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/kernel/libkernel.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static constexpr u32 PthreadKeysMax = 256;
|
||||
static constexpr u32 PthreadDestructorIterations = 4;
|
||||
|
||||
static std::array<PthreadKey, PthreadKeysMax> 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
|
143
src/core/libraries/kernel/threads/thr_stack.cpp
Normal file
143
src/core/libraries/kernel/threads/thr_stack.cpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/kernel/threads/threads.h"
|
||||
#include "core/libraries/kernel/threads/thread_state.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 == _thr_guard_default) {
|
||||
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 -
|
||||
_thr_guard_default;
|
||||
}
|
||||
|
||||
/* 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<void**>(&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 == _thr_guard_default) {
|
||||
/* 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
|
134
src/core/libraries/kernel/threads/thread_state.cpp
Normal file
134
src/core/libraries/kernel/threads/thread_state.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/libraries/kernel/threads/thread_state.h"
|
||||
#include "core/libraries/kernel/threads/threads.h"
|
||||
#include "core/libraries/error_codes.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<Pthread*, 8> 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<std::mutex>();
|
||||
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
|
79
src/core/libraries/kernel/threads/thread_state.h
Normal file
79
src/core/libraries/kernel/threads/thread_state.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include "common/types.h"
|
||||
#include "common/singleton.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<Pthread*> threads;
|
||||
std::list<Pthread*> free_threads;
|
||||
std::list<Pthread*> gc_list;
|
||||
std::mutex free_thread_lock;
|
||||
std::mutex tcb_lock;
|
||||
std::mutex thread_list_lock;
|
||||
std::atomic<s32> total_threads{};
|
||||
std::atomic<s32> active_threads{};
|
||||
std::stack<Stack*> dstackq;
|
||||
std::list<Stack*> mstackq;
|
||||
VAddr last_stack = 0;
|
||||
};
|
||||
|
||||
using ThrState = Common::Singleton<ThreadState>;
|
||||
|
||||
} // namespace Libraries::Kernel
|
|
@ -3,7 +3,16 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <forward_list>
|
||||
#include <shared_mutex>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
|
||||
#include "common/enum.h"
|
||||
#include "core/libraries/kernel/time_management.h"
|
||||
#include "core/tls.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
|
@ -11,10 +20,247 @@ 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<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
|
||||
|
||||
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<PthreadMutexType>(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;
|
||||
|
||||
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 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<long> tid;
|
||||
std::unique_ptr<std::mutex> lock;
|
||||
u32 cycle;
|
||||
int locklevel;
|
||||
int critical_count;
|
||||
int sigblock;
|
||||
int refcount;
|
||||
void PS4_SYSV_ABI *(*start_routine)(void *);
|
||||
void* arg;
|
||||
PthreadAttr attr;
|
||||
bool cancel_enable;
|
||||
bool cancel_pending;
|
||||
bool cancel_point;
|
||||
bool no_cancel;
|
||||
bool cancel_async;
|
||||
bool cancelling;
|
||||
sigset_t sigmask;
|
||||
bool unblock_sigcancel;
|
||||
bool in_sigsuspend;
|
||||
bool force_exit;
|
||||
PthreadState state;
|
||||
int error;
|
||||
Pthread* joiner;
|
||||
ThreadFlags flags;
|
||||
ThreadListFlags tlflags;
|
||||
boost::intrusive::list<PthreadMutex> mutexq;
|
||||
boost::intrusive::list<PthreadMutex> pp_mutexq;
|
||||
void* ret;
|
||||
PthreadSpecificElem* specific;
|
||||
int specific_data_count;
|
||||
int rdlock_count;
|
||||
int rtld_bits;
|
||||
Core::Tcb* tcb;
|
||||
std::forward_list<PthreadCleanup*> cleanup;
|
||||
u32 pad[27];
|
||||
u32 magic;
|
||||
int report_events;
|
||||
int event_mask;
|
||||
std::string name;
|
||||
|
||||
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);
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
|
@ -26,6 +26,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 {
|
||||
|
|
|
@ -9,43 +9,6 @@
|
|||
#include "core/loader/elf.h"
|
||||
#include "core/loader/symbols_resolver.h"
|
||||
|
||||
template <size_t N>
|
||||
struct StringLiteral {
|
||||
constexpr StringLiteral(const char (&str)[N]) {
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
|
||||
char value[N];
|
||||
};
|
||||
|
||||
template <StringLiteral name, class F, F f>
|
||||
struct wrapper_impl;
|
||||
|
||||
template <StringLiteral name, class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
|
||||
struct wrapper_impl<name, PS4_SYSV_ABI R (*)(Args...), f> {
|
||||
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<R, s32> || std::is_same_v<R, u32>) {
|
||||
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 <StringLiteral name, class F, F f>
|
||||
constexpr auto wrapper = wrapper_impl<name, F, f>::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<name, F, f>::wrap;
|
|||
sr.module_version_major = moduleVersionMajor; \
|
||||
sr.module_version_minor = moduleVersionMinor; \
|
||||
sr.type = Core::Loader::SymbolType::Function; \
|
||||
auto func = reinterpret_cast<u64>(W(function)); \
|
||||
auto func = reinterpret_cast<u64>(function); \
|
||||
sym->AddSymbol(sr, func); \
|
||||
}
|
||||
|
||||
|
|
|
@ -321,7 +321,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<u8*>(heap_api->heap_malloc(module->tls.image_size));
|
||||
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
|
||||
std::memcpy(dest, src, init_image_size);
|
||||
|
@ -343,7 +342,7 @@ void Linker::EnsureThreadInitialized(bool is_primary) {
|
|||
});
|
||||
}
|
||||
|
||||
void Linker::InitTlsForThread(bool is_primary) {
|
||||
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;
|
||||
|
@ -364,54 +363,12 @@ void Linker::InitTlsForThread(bool is_primary) {
|
|||
ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread");
|
||||
} else {
|
||||
if (heap_api) {
|
||||
#ifndef WIN32
|
||||
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<u8*>(addr_out);
|
||||
Tcb* tcb = reinterpret_cast<Tcb*>(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<u8*>(addr + static_tls_size - module->tls.offset);
|
||||
const u8* src = reinterpret_cast<const u8*>(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() {
|
||||
|
|
|
@ -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);
|
||||
|
@ -118,7 +131,6 @@ public:
|
|||
private:
|
||||
const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l);
|
||||
void EnsureThreadInitialized(bool is_primary = false);
|
||||
void InitTlsForThread(bool is_primary);
|
||||
|
||||
MemoryManager* memory;
|
||||
std::mutex mutex;
|
||||
|
|
|
@ -149,6 +149,13 @@ public:
|
|||
return impl.SystemReservedVirtualBase();
|
||||
}
|
||||
|
||||
bool IsValidAddress(const void* addr) const noexcept {
|
||||
const VAddr virtual_addr = reinterpret_cast<VAddr>(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;
|
||||
}
|
||||
|
||||
void SetupMemoryRegions(u64 flexible_size);
|
||||
|
||||
PAddr PoolExpand(PAddr search_start, PAddr search_end, size_t size, u64 alignment);
|
||||
|
|
Loading…
Add table
Reference in a new issue