diff --git a/core/libs/prx/libkernel/Pthread/Posix/Common.hpp b/core/libs/prx/libkernel/Pthread/Posix/Common.hpp index b89859ae7e..334842c761 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Common.hpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Common.hpp @@ -20,6 +20,10 @@ inline int ToErrno(int sceResult) { return sceResult == 0 ? 0 : static_cast(static_cast(sceResult) & 0xFFFFu); } +inline int ToSce(int error) { + return error == 0 ? 0 : static_cast(0x80020000u | static_cast(error)); +} + // Converts an absolute deadline on the guest clock into a relative wait, clamped to what the sce layer accepts. inline bool RelativeMicroseconds(int clockId, const KernelTimespec* abstime, KernelUseconds* usec) { if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) return false; diff --git a/core/libs/prx/libkernel/Pthread/Posix/Cond.cpp b/core/libs/prx/libkernel/Pthread/Posix/Cond.cpp index f357cac53a..324ee35216 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Cond.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Cond.cpp @@ -80,4 +80,16 @@ int APS5_VABI pthread_condattr_getpshared_nid_postfix(const PthreadCondattr* att return 0; } +int APS5_VABI pthread_cond_signalto_np_nid_postfix(PthreadCond* cond, Pthread thread) { + return toPosix(scePthreadCondSignalto(cond, thread)); +} + +int APS5_VABI scePthreadCondattrSetpshared(PthreadCondattr* attr, int pshared) { + return PosixThread::ToSce(pthread_condattr_setpshared_nid_postfix(attr, pshared)); +} + +int APS5_VABI scePthreadCondattrGetpshared(const PthreadCondattr* attr, int* pshared) { + return PosixThread::ToSce(pthread_condattr_getpshared_nid_postfix(attr, pshared)); +} + } diff --git a/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp b/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp index 346b447bb8..fbf631b696 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp @@ -39,6 +39,7 @@ void _initializeStatic(PthreadMutex* mutex, const char* funcName) { extern "C" { int APS5_VABI sched_get_priority_max_nid_postfix(int policy); +int* APS5_VABI __error_nid_postfix(); int APS5_VABI pthread_mutex_destroy_nid_postfix(PthreadMutex* mutex) { if (!mutex) throw std::runtime_error("pthread_mutex_destroy: null mutex"); @@ -169,4 +170,51 @@ int APS5_VABI pthread_mutexattr_gettype_nid_postfix(const PthreadMutexattr* attr return 0; } +int APS5_VABI pthread_mutexattr_setkind_np_nid_postfix(PthreadMutexattr* attr, int kind) { + if (!attr || !*attr) { + *__error_nid_postfix() = POSIX_EINVAL; + return -1; + } + if (kind < static_cast(MutexType::ErrorCheck) || kind > static_cast(MutexType::Adaptive)) + throw std::runtime_error("pthread_mutexattr_setkind_np: kind " + std::to_string(kind) + " is not a mutex type, and pthread_mutex_init does not reject it with EINVAL"); + (*attr)->type = static_cast(kind); + return 0; +} + +int APS5_VABI pthread_mutexattr_getkind_np_nid_postfix(PthreadMutexattr attr) { + if (!attr) { + *__error_nid_postfix() = POSIX_EINVAL; + return -1; + } + return static_cast(attr->type); +} + +int APS5_VABI scePthreadMutexattrGettype(const PthreadMutexattr* attr, int* type) { + return PosixThread::ToSce(pthread_mutexattr_gettype_nid_postfix(attr, type)); +} + +int APS5_VABI scePthreadMutexattrSetprotocol(PthreadMutexattr* attr, int protocol) { + return PosixThread::ToSce(pthread_mutexattr_setprotocol_nid_postfix(attr, protocol)); +} + +int APS5_VABI scePthreadMutexattrGetprotocol(const PthreadMutexattr* attr, int* protocol) { + return PosixThread::ToSce(pthread_mutexattr_getprotocol_nid_postfix(attr, protocol)); +} + +int APS5_VABI scePthreadMutexattrSetprioceiling(PthreadMutexattr* attr, int prioceiling) { + return PosixThread::ToSce(pthread_mutexattr_setprioceiling_nid_postfix(attr, prioceiling)); +} + +int APS5_VABI scePthreadMutexattrGetprioceiling(const PthreadMutexattr* attr, int* prioceiling) { + return PosixThread::ToSce(pthread_mutexattr_getprioceiling_nid_postfix(attr, prioceiling)); +} + +int APS5_VABI scePthreadMutexattrSetpshared(PthreadMutexattr* attr, int pshared) { + return PosixThread::ToSce(pthread_mutexattr_setpshared_nid_postfix(attr, pshared)); +} + +int APS5_VABI scePthreadMutexattrGetpshared(const PthreadMutexattr* attr, int* pshared) { + return PosixThread::ToSce(pthread_mutexattr_getpshared_nid_postfix(attr, pshared)); +} + } diff --git a/core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp b/core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp index 203af9f0f4..a15fe61fb0 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp @@ -70,6 +70,25 @@ int APS5_VABI pthread_rwlockattr_setpshared_nid_postfix(PthreadRwlockattr* attr, return 0; } +int APS5_VABI pthread_rwlockattr_gettype_np_nid_postfix(const PthreadRwlockattr* attr, int* type) { + if (!attr || !*attr) throw std::runtime_error("pthread_rwlockattr_gettype_np: null attr"); + if (!type) throw std::runtime_error("pthread_rwlockattr_gettype_np: null type"); + *type = (*attr)->type; + return 0; +} + +int APS5_VABI scePthreadRwlockattrGettype(const PthreadRwlockattr* attr, int* type) { + return PosixThread::ToSce(pthread_rwlockattr_gettype_np_nid_postfix(attr, type)); +} + +int APS5_VABI scePthreadRwlockattrSetpshared(PthreadRwlockattr* attr, int pshared) { + return PosixThread::ToSce(pthread_rwlockattr_setpshared_nid_postfix(attr, pshared)); +} + +int APS5_VABI scePthreadRwlockattrGetpshared(const PthreadRwlockattr* attr, int* pshared) { + return PosixThread::ToSce(pthread_rwlockattr_getpshared_nid_postfix(attr, pshared)); +} + int APS5_VABI pthread_rwlock_rdlock_nid_postfix(PthreadRwlock* rwlock) { initializeStatic(rwlock, __func__); return toPosix(scePthreadRwlockRdlock(rwlock)); diff --git a/core/libs/prx/libkernel/Pthread/src/Mutex.cpp b/core/libs/prx/libkernel/Pthread/src/Mutex.cpp index d7bc4013f8..00df97d84e 100644 --- a/core/libs/prx/libkernel/Pthread/src/Mutex.cpp +++ b/core/libs/prx/libkernel/Pthread/src/Mutex.cpp @@ -149,16 +149,6 @@ int APS5_VABI scePthreadMutexattrSettype(PthreadMutexattr* attr, int type) { return 0; } -int APS5_VABI scePthreadMutexattrSetprotocol(PthreadMutexattr* attr, int protocol) { - if (!attr || !*attr) - return SCE_KERNEL_ERROR_EINVAL; - constexpr int PrioNone = 0; - constexpr int PrioProtect = 2; - if (protocol < PrioNone || protocol > PrioProtect) - return SCE_KERNEL_ERROR_EINVAL; - return 0; -} - int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char*) { if (!mutex) throw std::invalid_argument("Mutex pointer is null"); diff --git a/core/libs/tests/GuestCondTimedwait.cpp b/core/libs/tests/GuestCondTimedwait.cpp index 434d257d2d..27a91e95e7 100644 --- a/core/libs/tests/GuestCondTimedwait.cpp +++ b/core/libs/tests/GuestCondTimedwait.cpp @@ -20,6 +20,8 @@ int APS5_VABI pthread_cond_init_nid_postfix(PthreadCond* cond, const PthreadCond int APS5_VABI pthread_cond_destroy_nid_postfix(PthreadCond* cond); int APS5_VABI pthread_cond_timedwait_nid_postfix(PthreadCond* cond, PthreadMutex* mutex, const KernelTimespec* abstime); int APS5_VABI pthread_cond_reltimedwait_np_nid_postfix(PthreadCond* cond, PthreadMutex* mutex, unsigned int usec); +int APS5_VABI pthread_cond_signalto_np_nid_postfix(PthreadCond* cond, Pthread thread); +Pthread APS5_VABI scePthreadSelf(); int APS5_VABI __cxa_atexit_nid_postfix(void (APS5_VABI *)(void*), void*, void*); void APS5_VABI __pthread_cxa_finalize_nid_postfix(void*); unsigned int APS5_VABI sceKernelSleep(unsigned int seconds); @@ -55,6 +57,7 @@ struct Signalled { PthreadCond cond = nullptr; bool ready = false; int trylock = SCE_OK; + Pthread target = nullptr; }; static void* APS5_VABI Trylocker(void* arg) { @@ -72,6 +75,15 @@ static void* APS5_VABI Signaller(void* arg) { return nullptr; } +static void* APS5_VABI SignallerTo(void* arg) { + auto& signalled = *static_cast(arg); + Require(scePthreadMutexLock(&signalled.mutex) == SCE_OK); + signalled.ready = true; + Require(pthread_cond_signalto_np_nid_postfix(&signalled.cond, signalled.target) == 0); + Require(scePthreadMutexUnlock(&signalled.mutex) == SCE_OK); + return nullptr; +} + static void RequireHeldByCaller(Signalled& signalled) { Pthread thread = nullptr; Require(scePthreadCreate(&thread, nullptr, Trylocker, &signalled, nullptr) == SCE_OK); @@ -108,8 +120,29 @@ static void RelativeWait() { Require(scePthreadMutexDestroy(&signalled.mutex) == SCE_OK); } +static void SignalTo() { + Signalled signalled; + signalled.target = scePthreadSelf(); + Require(scePthreadMutexInit(&signalled.mutex, nullptr, nullptr) == SCE_OK); + Require(scePthreadCondInit(&signalled.cond, nullptr, nullptr) == SCE_OK); + Require(pthread_cond_signalto_np_nid_postfix(&signalled.cond, signalled.target) == 0); + Require(scePthreadMutexLock(&signalled.mutex) == SCE_OK); + Pthread thread = nullptr; + Require(scePthreadCreate(&thread, nullptr, SignallerTo, &signalled, nullptr) == SCE_OK); + const auto waitStart = std::chrono::steady_clock::now(); + while (!signalled.ready) + Require(pthread_cond_reltimedwait_np_nid_postfix(&signalled.cond, &signalled.mutex, 10000000) == 0); + Require(std::chrono::steady_clock::now() - waitStart < std::chrono::seconds(5)); + Require(scePthreadMutexUnlock(&signalled.mutex) == SCE_OK); + Require(scePthreadJoin(thread, nullptr) == SCE_OK); + Require(scePthreadCondDestroy(&signalled.cond) == SCE_OK); + Require(pthread_cond_signalto_np_nid_postfix(&signalled.cond, signalled.target) == 22); + Require(scePthreadMutexDestroy(&signalled.mutex) == SCE_OK); +} + int main() { RelativeWait(); + SignalTo(); Context context; PthreadCond cond = nullptr; diff --git a/core/libs/tests/GuestPthreadCondattr.cpp b/core/libs/tests/GuestPthreadCondattr.cpp index 237ac4374e..a05d017c80 100644 --- a/core/libs/tests/GuestPthreadCondattr.cpp +++ b/core/libs/tests/GuestPthreadCondattr.cpp @@ -10,6 +10,8 @@ int APS5_VABI scePthreadCondattrSetclock(PthreadCondattr* attr, KernelClockid cl int APS5_VABI pthread_condattr_setclock_nid_postfix(PthreadCondattr* attr, KernelClockid clockId); int APS5_VABI pthread_condattr_setpshared_nid_postfix(PthreadCondattr* attr, int pshared); int APS5_VABI pthread_condattr_getpshared_nid_postfix(const PthreadCondattr* attr, int* pshared); +int APS5_VABI scePthreadCondattrSetpshared(PthreadCondattr* attr, int pshared); +int APS5_VABI scePthreadCondattrGetpshared(const PthreadCondattr* attr, int* pshared); } static constexpr int SCE_OK = 0; @@ -31,6 +33,8 @@ int main() { Require(pthread_condattr_setpshared_nid_postfix(&unset, 0) == GuestEinval); Require(pthread_condattr_getpshared_nid_postfix(nullptr, &sharing) == GuestEinval && sharing == -1); Require(pthread_condattr_getpshared_nid_postfix(&unset, &sharing) == GuestEinval && sharing == -1); + Require(scePthreadCondattrSetpshared(&unset, 0) == SceEinval); + Require(scePthreadCondattrGetpshared(nullptr, &sharing) == SceEinval && sharing == -1); PthreadCondattr attr = nullptr; Require(scePthreadCondattrInit(&attr) == SCE_OK); @@ -38,6 +42,9 @@ int main() { Require(scePthreadCondattrSetclock(&attr, clock) == SCE_OK); Require(pthread_condattr_setclock_nid_postfix(&attr, clock) == 0); } + Require(scePthreadCondattrSetpshared(&attr, 1) == SceEinval); + Require(scePthreadCondattrSetpshared(&attr, 0) == SCE_OK); + Require(scePthreadCondattrGetpshared(&attr, &sharing) == SCE_OK && sharing == 0); Require(pthread_condattr_getpshared_nid_postfix(&attr, &sharing) == 0 && sharing == 0); for (const int invalid : {1, 2, -1}) { Require(pthread_condattr_setpshared_nid_postfix(&attr, invalid) == GuestEinval); diff --git a/core/libs/tests/GuestPthreadMutexattr.cpp b/core/libs/tests/GuestPthreadMutexattr.cpp index 48dd291a5b..859dc770e1 100644 --- a/core/libs/tests/GuestPthreadMutexattr.cpp +++ b/core/libs/tests/GuestPthreadMutexattr.cpp @@ -15,6 +15,19 @@ int APS5_VABI pthread_mutexattr_setprioceiling_nid_postfix(PthreadMutexattr* att int APS5_VABI pthread_mutexattr_getprioceiling_nid_postfix(const PthreadMutexattr* attr, int* prioceiling); int APS5_VABI pthread_mutexattr_settype_nid_postfix(PthreadMutexattr* attr, int type); int APS5_VABI pthread_mutexattr_gettype_nid_postfix(const PthreadMutexattr* attr, int* type); +int APS5_VABI pthread_mutexattr_setkind_np_nid_postfix(PthreadMutexattr* attr, int kind); +int APS5_VABI pthread_mutexattr_getkind_np_nid_postfix(PthreadMutexattr attr); +int APS5_VABI scePthreadMutexattrInit(PthreadMutexattr* attr); +int APS5_VABI scePthreadMutexattrDestroy(PthreadMutexattr* attr); +int APS5_VABI scePthreadMutexattrSettype(PthreadMutexattr* attr, int type); +int APS5_VABI scePthreadMutexattrGettype(const PthreadMutexattr* attr, int* type); +int APS5_VABI scePthreadMutexattrSetprotocol(PthreadMutexattr* attr, int protocol); +int APS5_VABI scePthreadMutexattrGetprotocol(const PthreadMutexattr* attr, int* protocol); +int APS5_VABI scePthreadMutexattrSetprioceiling(PthreadMutexattr* attr, int prioceiling); +int APS5_VABI scePthreadMutexattrGetprioceiling(const PthreadMutexattr* attr, int* prioceiling); +int APS5_VABI scePthreadMutexattrSetpshared(PthreadMutexattr* attr, int pshared); +int APS5_VABI scePthreadMutexattrGetpshared(const PthreadMutexattr* attr, int* pshared); +int* APS5_VABI __error_nid_postfix(); int APS5_VABI pthread_mutex_init_nid_postfix(PthreadMutex* mutex, const PthreadMutexattr* attr); int APS5_VABI pthread_mutex_lock_nid_postfix(PthreadMutex* mutex); int APS5_VABI pthread_mutex_unlock_nid_postfix(PthreadMutex* mutex); @@ -26,6 +39,8 @@ int APS5_VABI sched_get_priority_max_nid_postfix(int policy); } static constexpr int GUEST_EINVAL = 22; +static constexpr int SCE_OK = 0; +static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast(0x80020016u); static constexpr int PRIO_NONE = 0; static constexpr int PRIO_INHERIT = 1; static constexpr int PRIO_PROTECT = 2; @@ -80,6 +95,78 @@ static void CheckType() { Require(pthread_mutexattr_gettype_nid_postfix(nullptr, &type) == GUEST_EINVAL && type == -1); } +static void CheckKind() { + PthreadMutexattr attr = nullptr; + *__error_nid_postfix() = 0; + Require(pthread_mutexattr_getkind_np_nid_postfix(nullptr) == -1 && *__error_nid_postfix() == GUEST_EINVAL); + *__error_nid_postfix() = 0; + Require(pthread_mutexattr_setkind_np_nid_postfix(nullptr, 2) == -1 && *__error_nid_postfix() == GUEST_EINVAL); + *__error_nid_postfix() = 0; + Require(pthread_mutexattr_setkind_np_nid_postfix(&attr, 2) == -1 && *__error_nid_postfix() == GUEST_EINVAL); + Require(pthread_mutexattr_init_nid_postfix(&attr) == 0); + Require(pthread_mutexattr_getkind_np_nid_postfix(attr) == 1); + int type = -1; + for (const int kind : {2, 3, 4, 1}) { + Require(pthread_mutexattr_setkind_np_nid_postfix(&attr, kind) == 0); + Require(pthread_mutexattr_getkind_np_nid_postfix(attr) == kind); + Require(pthread_mutexattr_gettype_nid_postfix(&attr, &type) == 0 && type == kind); + } + Require(pthread_mutexattr_settype_nid_postfix(&attr, 2) == 0); + Require(pthread_mutexattr_getkind_np_nid_postfix(attr) == 2); + for (const int invalid : {0, 5, -1}) + Require(Throws([&] { pthread_mutexattr_setkind_np_nid_postfix(&attr, invalid); })); + Require(pthread_mutexattr_getkind_np_nid_postfix(attr) == 2); + PthreadMutex mutex = nullptr; + Require(pthread_mutex_init_nid_postfix(&mutex, &attr) == 0); + Require(pthread_mutex_lock_nid_postfix(&mutex) == 0); + Require(pthread_mutex_lock_nid_postfix(&mutex) == 0); + Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0); + Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0); + Require(pthread_mutex_destroy_nid_postfix(&mutex) == 0); + Require(pthread_mutexattr_destroy_nid_postfix(&attr) == 0); + *__error_nid_postfix() = 0; + Require(pthread_mutexattr_setkind_np_nid_postfix(&attr, 2) == -1 && *__error_nid_postfix() == GUEST_EINVAL); +} + +static void CheckSceAccessors() { + PthreadMutexattr attr = nullptr; + int value = -1; + Require(scePthreadMutexattrGettype(nullptr, &value) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrGettype(&attr, &value) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrSetprotocol(&attr, PRIO_NONE) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrGetprotocol(&attr, &value) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrSetprioceiling(&attr, 300) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrGetprioceiling(&attr, &value) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrSetpshared(nullptr, 0) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrGetpshared(&attr, &value) == SCE_KERNEL_ERROR_EINVAL); + Require(value == -1); + Require(scePthreadMutexattrInit(&attr) == SCE_OK); + Require(scePthreadMutexattrGettype(&attr, &value) == SCE_OK && value == 1); + Require(scePthreadMutexattrSettype(&attr, 3) == SCE_OK); + Require(scePthreadMutexattrGettype(&attr, &value) == SCE_OK && value == 3); + Require(Throws([&] { scePthreadMutexattrGettype(&attr, nullptr); })); + Require(scePthreadMutexattrGetprotocol(&attr, &value) == SCE_OK && value == PRIO_NONE); + for (const int invalid : {-1, 3}) + Require(scePthreadMutexattrSetprotocol(&attr, invalid) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrGetprotocol(&attr, &value) == SCE_OK && value == PRIO_NONE); + Require(scePthreadMutexattrSetprotocol(&attr, PRIO_INHERIT) == SCE_OK); + Require(scePthreadMutexattrGetprotocol(&attr, &value) == SCE_OK && value == PRIO_INHERIT); + Require(scePthreadMutexattrSetprioceiling(&attr, 300) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrGetprioceiling(&attr, &value) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrSetprotocol(&attr, PRIO_PROTECT) == SCE_OK); + Require(scePthreadMutexattrGetprioceiling(&attr, &value) == SCE_OK && value == sched_get_priority_max_nid_postfix(SCHED_RR)); + Require(scePthreadMutexattrSetprioceiling(&attr, 300) == SCE_OK); + Require(scePthreadMutexattrGetprioceiling(&attr, &value) == SCE_OK && value == 300); + int protocol = -1; + Require(pthread_mutexattr_getprotocol_nid_postfix(&attr, &protocol) == 0 && protocol == PRIO_PROTECT); + Require(scePthreadMutexattrGetpshared(&attr, &value) == SCE_OK && value == 0); + Require(scePthreadMutexattrSetpshared(&attr, 1) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadMutexattrSetpshared(&attr, 0) == SCE_OK); + Require(scePthreadMutexattrGetpshared(&attr, &value) == SCE_OK && value == 0); + Require(scePthreadMutexattrDestroy(&attr) == SCE_OK); + Require(scePthreadMutexattrGettype(&attr, &value) == SCE_KERNEL_ERROR_EINVAL); +} + static void CheckProcessSharing() { PthreadMutexattr attr = nullptr; int sharing = -1; @@ -127,6 +214,8 @@ static void CheckInvalidPriorityAttribute(PthreadMutexattr* attr) { int main() { CheckType(); + CheckKind(); + CheckSceAccessors(); CheckProcessSharing(); CheckTimedlockInvalidDeadline(); PthreadMutexattr attr = nullptr; diff --git a/core/libs/tests/GuestPthreadRwlock.cpp b/core/libs/tests/GuestPthreadRwlock.cpp index 91c219a1cb..352d614b67 100644 --- a/core/libs/tests/GuestPthreadRwlock.cpp +++ b/core/libs/tests/GuestPthreadRwlock.cpp @@ -16,6 +16,11 @@ int APS5_VABI pthread_rwlockattr_init_nid_postfix(PthreadRwlockattr* attr); int APS5_VABI pthread_rwlockattr_destroy_nid_postfix(PthreadRwlockattr* attr); int APS5_VABI pthread_rwlockattr_getpshared_nid_postfix(const PthreadRwlockattr* attr, int* pshared); int APS5_VABI pthread_rwlockattr_setpshared_nid_postfix(PthreadRwlockattr* attr, int pshared); +int APS5_VABI pthread_rwlockattr_gettype_np_nid_postfix(const PthreadRwlockattr* attr, int* type); +int APS5_VABI scePthreadRwlockattrSettype(PthreadRwlockattr* attr, int type); +int APS5_VABI scePthreadRwlockattrGettype(const PthreadRwlockattr* attr, int* type); +int APS5_VABI scePthreadRwlockattrSetpshared(PthreadRwlockattr* attr, int pshared); +int APS5_VABI scePthreadRwlockattrGetpshared(const PthreadRwlockattr* attr, int* pshared); int APS5_VABI pthread_rwlock_rdlock_nid_postfix(PthreadRwlock* rwlock); int APS5_VABI pthread_rwlock_wrlock_nid_postfix(PthreadRwlock* rwlock); int APS5_VABI pthread_rwlock_unlock_nid_postfix(PthreadRwlock* rwlock); @@ -397,8 +402,32 @@ static void CheckAttrProcessSharing() { Require(pthread_rwlockattr_destroy_nid_postfix(&attr) == 0); } +static void CheckAttrType() { + PthreadRwlockattr attr = nullptr; + Require(pthread_rwlockattr_init_nid_postfix(&attr) == 0); + int type = -1; + Require(pthread_rwlockattr_gettype_np_nid_postfix(&attr, &type) == 0 && type == 0); + Require(scePthreadRwlockattrGettype(&attr, &type) == SCE_OK && type == 0); + for (const int set : {1, 2, 0}) { + Require(scePthreadRwlockattrSettype(&attr, set) == SCE_OK); + Require(pthread_rwlockattr_gettype_np_nid_postfix(&attr, &type) == 0 && type == set); + Require(scePthreadRwlockattrGettype(&attr, &type) == SCE_OK && type == set); + } + Require(Throws([&] { pthread_rwlockattr_gettype_np_nid_postfix(&attr, nullptr); })); + int pshared = -1; + Require(scePthreadRwlockattrGetpshared(&attr, &pshared) == SCE_OK && pshared == PROCESS_PRIVATE); + Require(scePthreadRwlockattrSetpshared(&attr, 1) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadRwlockattrSetpshared(&attr, PROCESS_PRIVATE) == SCE_OK); + Require(pthread_rwlockattr_destroy_nid_postfix(&attr) == 0); + PthreadRwlockattr unset = nullptr; + Require(Throws([&] { pthread_rwlockattr_gettype_np_nid_postfix(nullptr, &type); })); + Require(Throws([&] { pthread_rwlockattr_gettype_np_nid_postfix(&unset, &type); })); + Require(Throws([&] { scePthreadRwlockattrGettype(&unset, &type); })); +} + int main() { CheckAttrProcessSharing(); + CheckAttrType(); CheckWaitingWriterHoldsBackNewReaders(); CheckConcurrentFirstWrlockExcludes(); CheckDeadlinesBeyondTheClockRange(); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index a17c1c6a15..d1079614fb 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -3,7 +3,7 @@ ### Silent stubs - [sceKernelGetSocPowerConsumption](../../../core/libs/prx/libkernel/System/src/ThermalSensors.cpp) (libkernel) - returns `EINVAL` and writes nothing, as `sceKernelGetSocSensorTemperature` does when the host has no sensor: the host's package power is not read (Linux RAPL `energy_uj` is readable by root only, and Windows has no general source). The prototype is not documented. PS5LM Plus (PPSA99581) passes a zeroed 128-byte buffer and, on success, reads its first 32-bit word as milliwatts, accepting 1000 to 350000; on failure it shows 0 W -- [scePthreadMutexattrSetprotocol](../../../core/libs/prx/libkernel/Pthread/src/Mutex.cpp) (libkernel) - accepts 0 to 2 as a PS5 (firmware 13.60) does and returns `EINVAL` otherwise, but the protocol is not stored or applied: priority inheritance and protection are not modelled. On the console a null or uninitialized mutex attribute returns `EINVAL` from the SCE attribute calls, `scePthreadMutexInit` takes an uninitialized attribute as the default one, and a destroyed mutex returns `EINVAL` from `scePthreadMutexDestroy`; a null mutex pointer crashed the payload and still throws +- [scePthreadMutexattrSetprotocol, scePthreadMutexattrGetprotocol, scePthreadMutexattrSetprioceiling, scePthreadMutexattrGetprioceiling](../../../core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp) (libkernel) - `scePthreadMutexattrSetprotocol` accepts 0 to 2 as a PS5 (firmware 13.60) does and returns `EINVAL` otherwise, but the protocol is not applied: priority inheritance and protection are not modelled. On the console a null or uninitialized mutex attribute returns `EINVAL` from the SCE attribute calls, `scePthreadMutexInit` takes an uninitialized attribute as the default one, and a destroyed mutex returns `EINVAL` from `scePthreadMutexDestroy`; a null mutex pointer crashed the payload and still throws - [pthread_mutexattr_setprotocol, pthread_mutexattr_getprotocol, pthread_mutexattr_setprioceiling, pthread_mutexattr_getprioceiling](../../../core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp) (libkernel) - the protocol and the priority ceiling are stored and reported as in [FreeBSD 11 libthr](https://github.com/freebsd/freebsd-src/blob/releng/11.0/lib/libthr/thread/thr_mutexattr.c#L192-L245) (the ceiling is set or read only under `PTHREAD_PRIO_PROTECT`, and setting a protocol resets it to the highest `SCHED_RR` priority). A null, uninitialized or destroyed attribute returns `EINVAL` before the other arguments are checked; this error behavior is unverified on the console. Mutexes do not apply the protocol or ceiling: a priority-inherit or priority-protect mutex locks like a normal one and does not change its owner's priority - [sceCoredumpSetUserDataType](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) (libkernel, NID `Uxqkdta7wEg`) - `(int type)` follows the PS5 12.00 `libkernel.sprx` at 0x138B0: the sign-extended `type` goes to syscall 601 (command 29, third argument 0), and the raw result comes back with no SCE code mapping, so a kernel error is -1 with `errno` set. Which types the kernel accepts is not measured. No dump is produced here, so every type returns 0. Palworld (PPSA23126, #2598) and Hello Neighbor (PPSA35154, discussion #6991) import it; #4144 left it throwing because no signature was known - [sceKernelGetMainSocId and sceKernelGetCpumode](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) both return 0, and [sceKernelGetCpuFrequency](../../../core/libs/prx/libkernel/Time/Time.cpp) returns the host TSC frequency measured for `sceKernelGetTscFrequency`, not the console CPU clock. The PS5's SoC id and CPU mode are unknown; shadPS4's PS4 values are [0x710f10/0x740f30 and 0, 2 or 5 from the param.sfo CPU-mode flags](https://github.com/shadps4-emu/shadPS4/blob/e0a466753a1a/src/core/libraries/kernel/process.cpp). PPSA99203 only logs them (`[console] main SoC id: result=%#x out=%#x`, `CPU: %llu Hz, cpumode %d`), passing a pointer that `sceKernelGetMainSocId` leaves untouched @@ -234,6 +234,9 @@ - [pthread_spin_lock](../../../core/libs/prx/libkernel/Pthread/Posix/Spinlock.cpp) (libkernel) - a lock taken again by the thread that holds it throws here. On a PS5 (firmware 12.70) that call had not returned after 2 s, as FreeBSD 11.4 libthr [`thr_pspinlock.c`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_pspinlock.c) spins forever - [pthread_mutex_getspinloops_np, pthread_mutex_setspinloops_np, pthread_mutex_getyieldloops_np, pthread_mutex_setyieldloops_np and their scePthreadMutex counterparts](../../../core/libs/prx/libkernel/Pthread/src/Mutex.cpp) (libkernel) store and report the counts, but host mutexes do not spin or yield by them. The defaults follow FreeBSD 11.4's [`mutex_init_body`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_mutex.c#L236-L272) without `LIBPTHREAD_SPINLOOPS` and `LIBPTHREAD_YIELDLOOPS`: 2000 spins for an adaptive mutex, 0 otherwise; the console's defaults are unverified. Setting the yield count of a destroyed mutex returns 0 as FreeBSD's [`__pthread_mutex_setyieldloops_np`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_mutex.c#L1125-L1135) does; the console's result is unverified - [pthread_rwlockattr_getpshared, pthread_rwlockattr_setpshared](../../../core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp) (libkernel) - `pthread_rwlockattr_destroy` clears the attribute here, so a later get, or a set of 0, throws. On a PS5 (firmware 12.70) destroy leaves the attribute set; a get afterwards returned 0 with a stale value and a set of 0 returned 0 +- [pthread_mutexattr_setkind_np](../../../core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp) (libkernel) throws for a kind outside 1 to 4. FreeBSD 11.4's [`_pthread_mutexattr_setkind_np`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_mutexattr.c#L108-L120) stores any kind and [`mutex_check_attr`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_mutex.c#L207-L218) then makes `pthread_mutex_init` fail with `EINVAL`, but `pthread_mutex_init` here does not check the type +- [pthread_cond_signalto_np, scePthreadCondSignalto](../../../core/libs/prx/libkernel/Pthread/src/Cond.cpp) (libkernel) wake every waiter of the condition variable, not only the given thread, and do not check the thread; the other waiters see a spurious wakeup. Which waiters the console wakes, and its result for a thread that is not waiting, are unverified +- [pthread_rwlockattr_gettype_np, scePthreadRwlockattrGettype](../../../core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp) (libkernel) report the type stored by `scePthreadRwlockattrSettype`, which accepts any value and which read-write locks do not apply; the console's valid types and their effect are unverified. A null or uninitialized attribute throws, as for `pthread_rwlockattr_getpshared` - [sceKernelGetEventError](../../../core/libs/prx/libkernel/Equeue/src/EventAccessors.cpp) (libkernel) throws for `fflags` above 0xFFFF, where OR and addition of 0x80020000 differ and the console was not measured. Otherwise it returns 0 for `fflags` 0 and `0x80020000 | fflags`, whatever `flags` and `data` hold, as [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/kernel/ps4_queue.pas#L643) does and a PS5 (firmware 13.40, measured 2026-10-19 in a payload process) answered: `{flags 0x4000, fflags 0, data 22}`, `{0x4000, 0, -1}`, `{0x4000, 0, 0x80020016}` and `{0, 0, 0}` gave 0, `{0x4000, 5, 0}` and `{0x4000, 5, 22}` 0x80020005, `{0, 7, 22}` 0x80020007, `{0x8000, 3, 9}` 0x80020003. Video-out events carry their count in `fflags`, so they get 0x80020001 and up; only events built by the caller were measured. Closes when: `fflags` above 0xFFFF is measured - [pthread_cond_reltimedwait_np](../../../core/libs/prx/libkernel/Pthread/Posix/Cond.cpp) (libkernel) takes `(cond, mutex, usec)`, a relative timeout in microseconds, and is `scePthreadCondTimedwait` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadCondTimedwait` ([`condvar.cpp`](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/kernel/threads/condvar.cpp#L229-L234)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `sem_reltimedwait_np`. Unverified on the console - [pthread_mutex_reltimedlock_np](../../../core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp) (libkernel) takes `(mutex, usec)`, a relative timeout in microseconds, and is `scePthreadMutexTimedlock` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadMutexTimedlock` ([`mutex.cpp`](https://github.com/shadps4-emu/shadPS4/blob/7c960eae136b956cf4c1d72aacd2426b650330df/src/core/libraries/kernel/threads/mutex.cpp#L284-L287)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `pthread_cond_reltimedwait_np`, so a count of 2^32 microseconds or more would be truncated if the console reads 64 bits. Unverified on the console