Merge pull request #10385 from alvaroelpob/feat/libkernel-pthread-setname

feat(libkernel): implement pthread_mutex_setname_np, pthread_rwlock_setname_np and pthread_barrier_setname_np
This commit is contained in:
Dean authored and GitHub committed 2026-10-27 10:54:00 +13:00
commit ad1caa840c
10 files changed
+137 -18

No files matched your search

+10
View File
@@ -538,6 +538,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_pthread_barrier PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_pthread_setname_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadSetname.cpp)
target_include_directories(guest_pthread_setname_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_pthread_setname_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_pthread_setname_tests)
add_test(NAME guest_pthread_setname COMMAND guest_pthread_setname_tests)
set_tests_properties(guest_pthread_setname PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_pthread_setname PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_kernel_is_stack_tests ${LIBS_INCLUDE_DIR}/tests/GuestKernelIsStack.cpp)
target_include_directories(guest_kernel_is_stack_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_kernel_is_stack_tests PRIVATE libkernel libc)
@@ -91,4 +91,17 @@ int APS5_VABI pthread_barrier_destroy_nid_postfix(PthreadBarrierPrivate** barrie
return 0;
}
int APS5_VABI pthread_barrier_setname_np_nid_postfix(PthreadBarrierPrivate** barrier, const char* name) {
if (!barrier) throw std::invalid_argument("pthread_barrier_setname_np: null barrier");
if (!*barrier) throw std::invalid_argument("pthread_barrier_setname_np: barrier is not initialized or was destroyed");
auto& value = **barrier;
std::lock_guard lock(value.mutex);
if (value.named) return 35;
if (name) {
value.name = name;
value.named = true;
}
return 0;
}
}
@@ -47,6 +47,8 @@ struct PthreadMutexPrivate {
int _count;
int _spinloops = 0;
int _yieldloops = 0;
std::string _name;
bool _named = false;
PthreadMutexPrivate() : _type(MutexType::ErrorCheck), _owner(std::thread::id{}), _count(0) {}
@@ -66,6 +68,8 @@ struct PthreadRwlockPrivate {
std::atomic<std::thread::id> _writer;
std::atomic<int> _readers{0};
int _waitingWriters = 0;
std::string _name;
bool _named = false;
};
struct PthreadCondattrPrivate {
@@ -1,3 +1,4 @@
#include "prx/libkernel/include/Api.hpp"
#include "prx/libkernel/Pthread/include/Mutex.hpp"
#include "prx/libkernel/include/Api.hpp"
#include "../include/Pthread.hpp"
@@ -21,6 +22,7 @@ constexpr int sceDeadlock = static_cast<int>(0x8002000bu);
constexpr int sceBusy = static_cast<int>(0x80020010u);
constexpr int sceTimedOut = static_cast<int>(0x8002003cu);
constexpr std::int32_t posixFailure = -1;
constexpr int posixAgain = 35;
std::mutex& InitializationMutex() {
static std::mutex mutex;
return mutex;
@@ -282,4 +284,20 @@ int APS5_VABI scePthreadMutexSetyieldloops(PthreadMutex* mutex, int count) {
return 0;
}
int APS5_VABI pthread_mutex_setname_np_nid_postfix(PthreadMutex* mutex, const char* name) {
if (!mutex)
throw std::invalid_argument("pthread_mutex_setname_np: null mutex");
std::lock_guard lock(InitializationMutex());
const auto current = std::atomic_ref<PthreadMutex>(*mutex).load(std::memory_order_acquire);
if (!isInitializedMutex(current))
throw std::invalid_argument("pthread_mutex_setname_np: mutex is statically initialized or destroyed");
if (current->_named)
return posixAgain;
if (name) {
current->_name = name;
current->_named = true;
}
return 0;
}
}
@@ -1,3 +1,4 @@
#include "prx/libkernel/include/Api.hpp"
#include "prx/libkernel/Pthread/include/Rwlock.hpp"
#include "../include/Pthread.hpp"
#include "../include/Rwlock.hpp"
@@ -17,6 +18,8 @@ static constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = 0x8002003C;
static constexpr int SCE_KERNEL_ERROR_EINVAL = 0x80020016;
static constexpr int FIRST_STORED_RWLOCK_TYPE = 1;
static constexpr int LAST_STORED_RWLOCK_TYPE = 2;
static constexpr int POSIX_EINVAL = 22;
static constexpr int POSIX_EAGAIN = 35;
static PthreadRwlockPrivate* const DestroyedRwlock = reinterpret_cast<PthreadRwlockPrivate*>(std::uintptr_t{1});
@@ -188,4 +191,17 @@ int APS5_VABI scePthreadRwlockattrSettype(PthreadRwlockattr* attr, int type) {
return SCE_OK;
}
int APS5_VABI pthread_rwlock_setname_np_nid_postfix(PthreadRwlock* rwlock, const char* name) {
if (!rwlock) throw std::runtime_error("pthread_rwlock_setname_np: null rwlock");
PthreadRwlock current = std::atomic_ref<PthreadRwlock>(*rwlock).load(std::memory_order_acquire);
if (current == nullptr) throw std::runtime_error("pthread_rwlock_setname_np: statically initialized rwlock");
if (current == DestroyedRwlock) throw std::runtime_error("pthread_rwlock_setname_np: destroyed rwlock");
std::lock_guard state(current->_state);
if (current->_named) return POSIX_EAGAIN;
if (!name) return POSIX_EINVAL;
current->_name = name;
current->_named = true;
return SCE_OK;
}
}
+6
View File
@@ -10,6 +10,7 @@
#include <condition_variable>
#include <atomic>
#include <thread>
#include <string>
struct KernelIovec {
void* base;
@@ -116,6 +117,8 @@ struct PthreadBarrierPrivate {
unsigned arrived = 0;
unsigned active = 0;
std::uint64_t generation = 0;
std::string name;
bool named = false;
};
struct PthreadBarrierattrPrivate {
@@ -414,6 +417,7 @@ int APS5_VABI pthread_barrierattr_setpshared_nid_postfix(PthreadBarrierattrPriva
int APS5_VABI pthread_barrier_init_nid_postfix(PthreadBarrierPrivate** barrier, PthreadBarrierattrPrivate* const* attr, unsigned count);
int APS5_VABI pthread_barrier_wait_nid_postfix(PthreadBarrierPrivate** barrier);
int APS5_VABI pthread_barrier_destroy_nid_postfix(PthreadBarrierPrivate** barrier);
int APS5_VABI pthread_barrier_setname_np_nid_postfix(PthreadBarrierPrivate** barrier, const char* name);
int APS5_VABI pthread_cond_destroy_nid_postfix(PthreadCond* cond);
int APS5_VABI pthread_cond_broadcast_nid_postfix(PthreadCond* cond);
int APS5_VABI pthread_cond_init_nid_postfix(PthreadCond* cond, const PthreadCondattr* attr);
@@ -440,6 +444,7 @@ int APS5_VABI pthread_mutex_getspinloops_np_nid_postfix(PthreadMutex* mutex, int
int APS5_VABI pthread_mutex_setspinloops_np_nid_postfix(PthreadMutex* mutex, int count);
int APS5_VABI pthread_mutex_getyieldloops_np_nid_postfix(PthreadMutex* mutex, int* count);
int APS5_VABI pthread_mutex_setyieldloops_np_nid_postfix(PthreadMutex* mutex, int count);
int APS5_VABI pthread_mutex_setname_np_nid_postfix(PthreadMutex* mutex, const char* name);
int APS5_VABI pthread_mutexattr_destroy_nid_postfix(PthreadMutexattr* attr);
int APS5_VABI pthread_mutexattr_init_nid_postfix(PthreadMutexattr* attr);
int APS5_VABI pthread_mutexattr_getpshared_nid_postfix(const PthreadMutexattr* attr, int* pshared);
@@ -466,6 +471,7 @@ int APS5_VABI pthread_rwlock_tryrdlock_nid_postfix(PthreadRwlock* rwlock);
int APS5_VABI pthread_rwlock_trywrlock_nid_postfix(PthreadRwlock* rwlock);
int APS5_VABI pthread_rwlock_unlock_nid_postfix(PthreadRwlock* rwlock);
int APS5_VABI pthread_rwlock_wrlock_nid_postfix(PthreadRwlock* rwlock);
int APS5_VABI pthread_rwlock_setname_np_nid_postfix(PthreadRwlock* rwlock, const char* name);
int APS5_VABI sem_init_nid_postfix(void* sem, int pshared, unsigned int value);
int APS5_VABI sem_destroy_nid_postfix(void* sem);
int APS5_VABI sem_wait_nid_postfix(void* sem);
@@ -291,11 +291,6 @@ int APS5_VABI pthread_attr_setstackaddr_nid_postfix(void) {
return 0;
}
int APS5_VABI pthread_barrier_setname_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI pthread_get_specificarray_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -326,11 +321,6 @@ int APS5_VABI pthread_mutex_init_for_mono_nid_postfix(void) {
return 0;
}
int APS5_VABI pthread_mutex_setname_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI pthread_mutex_setprioceiling_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -362,11 +352,6 @@ int APS5_VABI pthread_resume_user_context_np_nid_postfix(void) {
return 0;
}
int APS5_VABI pthread_rwlock_setname_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI pthread_set_user_context_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
@@ -144,7 +144,6 @@ int APS5_VABI pthread_attr_getsolosched_np_nid_postfix(void);
int APS5_VABI pthread_attr_setaffinity_np_nid_postfix(void);
int APS5_VABI pthread_attr_setcreatesuspend_np_nid_postfix(PthreadAttr* attr);
int APS5_VABI pthread_attr_setstackaddr_nid_postfix(void);
int APS5_VABI pthread_barrier_setname_np_nid_postfix(void);
int APS5_VABI pthread_get_specificarray_np_nid_postfix(void);
int APS5_VABI pthread_get_user_context_np_nid_postfix(void);
int APS5_VABI pthread_getstack_np_nid_postfix(void);
@@ -152,14 +151,12 @@ int APS5_VABI pthread_main_np_nid_postfix(void);
int APS5_VABI pthread_multi_np_nid_postfix(void);
int APS5_VABI pthread_mutex_getprioceiling_nid_postfix(void);
int APS5_VABI pthread_mutex_init_for_mono_nid_postfix(void);
int APS5_VABI pthread_mutex_setname_np_nid_postfix(void);
int APS5_VABI pthread_mutex_setprioceiling_nid_postfix(void);
int APS5_VABI pthread_mutexattr_getgen_np_nid_postfix(void);
int APS5_VABI pthread_mutexattr_setgen_np_nid_postfix(void);
void APS5_VABI pthread_resume_all_np_nid_postfix(void);
int APS5_VABI pthread_resume_np_nid_postfix(Pthread thread);
int APS5_VABI pthread_resume_user_context_np_nid_postfix(void);
int APS5_VABI pthread_rwlock_setname_np_nid_postfix(void);
int APS5_VABI pthread_set_user_context_np_nid_postfix(void);
int APS5_VABI pthread_single_np_nid_postfix(void);
int APS5_VABI pthread_suspend_all_np_nid_postfix(void);
+67
View File
@@ -0,0 +1,67 @@
#include "prx/libkernel/include/Api.hpp"
#include "SceTypes.hpp"
#include "prx/libkernel/Pthread/include/Pthread.hpp"
#include <cstdio>
#include <cstdlib>
#include <source_location>
#include <stdexcept>
static constexpr int GuestEagain = 35;
static void Require(bool condition, std::source_location location = std::source_location::current()) {
if (condition) return;
std::fprintf(stderr, "pthread setname check failed at line %u\n", location.line());
std::abort();
}
template<typename TAction> static void RequireThrows(TAction action, std::source_location location = std::source_location::current()) {
try { action(); } catch (const std::exception&) { return; }
Require(false, location);
}
static void TestMutex() {
PthreadMutex mutex = nullptr;
Require(pthread_mutex_init_nid_postfix(&mutex, nullptr) == 0);
Require(pthread_mutex_setname_np_nid_postfix(&mutex, "first") == 0 && mutex->_name == "first");
Require(pthread_mutex_setname_np_nid_postfix(&mutex, "second") == GuestEagain && mutex->_name == "first");
Require(pthread_mutex_lock_nid_postfix(&mutex) == 0);
Require(pthread_mutex_setname_np_nid_postfix(&mutex, "held") == GuestEagain && mutex->_name == "first");
Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0);
RequireThrows([] { pthread_mutex_setname_np_nid_postfix(nullptr, "mutex"); });
Require(pthread_mutex_destroy_nid_postfix(&mutex) == 0);
RequireThrows([&] { pthread_mutex_setname_np_nid_postfix(&mutex, "destroyed"); });
}
static void TestRwlock() {
PthreadRwlock rwlock = nullptr;
Require(pthread_rwlock_init_nid_postfix(&rwlock, nullptr) == 0);
Require(pthread_rwlock_setname_np_nid_postfix(&rwlock, "first") == 0 && rwlock->_name == "first");
Require(pthread_rwlock_rdlock_nid_postfix(&rwlock) == 0);
Require(pthread_rwlock_setname_np_nid_postfix(&rwlock, "read") == GuestEagain && rwlock->_name == "first");
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);
RequireThrows([] { pthread_rwlock_setname_np_nid_postfix(nullptr, "rwlock"); });
Require(pthread_rwlock_destroy_nid_postfix(&rwlock) == 0);
RequireThrows([&] { pthread_rwlock_setname_np_nid_postfix(&rwlock, "destroyed"); });
PthreadRwlock unset = nullptr;
RequireThrows([&] { pthread_rwlock_setname_np_nid_postfix(&unset, "static"); });
}
static void TestBarrier() {
PthreadBarrierPrivate* barrier = nullptr;
Require(pthread_barrier_init_nid_postfix(&barrier, nullptr, 1) == 0);
Require(pthread_barrier_setname_np_nid_postfix(&barrier, "first") == 0 && barrier->name == "first");
Require(pthread_barrier_wait_nid_postfix(&barrier) == -1);
Require(pthread_barrier_setname_np_nid_postfix(&barrier, "second") == GuestEagain && barrier->name == "first");
RequireThrows([] { pthread_barrier_setname_np_nid_postfix(nullptr, "barrier"); });
Require(pthread_barrier_destroy_nid_postfix(&barrier) == 0);
RequireThrows([&] { pthread_barrier_setname_np_nid_postfix(&barrier, "destroyed"); });
PthreadBarrierPrivate* unset = nullptr;
RequireThrows([&] { pthread_barrier_setname_np_nid_postfix(&unset, "unset"); });
}
int main() {
TestMutex();
TestRwlock();
TestBarrier();
std::puts("pthread setname tests passed");
}
+3
View File
@@ -288,6 +288,9 @@
- [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`
- [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_setname_np](../../../core/libs/prx/libkernel/Pthread/src/Mutex.cpp) (libkernel) - the console passes `name` to the kernel's `namedobj_create` and stores the returned id instead of the name; here the name is copied and never read back. Once named, a mutex returns `EAGAIN` (35) for every later call and keeps its name, as [firmware 13.42's module run on a PC](https://github.com/boykopovar/AnyPS5/pull/10385#issuecomment-6415603047) does. The kernel's handling of a null `name` is unmeasured: here it returns 0 and leaves the mutex unnamed. A null mutex pointer faults in the module and throws here; a statically initialized or destroyed mutex also throws, and the module's result for those is unmeasured. The module's separate branch for a field holding `0x5eaf00d`, which copies the name into the object, is known only from disassembly and is not reproduced
- [pthread_rwlock_setname_np](../../../core/libs/prx/libkernel/Pthread/src/Rwlock.cpp) (libkernel) - the console passes `name` to the kernel's `namedobj_create` and stores the returned id instead of the name; here the name is copied and never read back. Once named, a lock returns `EAGAIN` (35) for every later call and keeps its name, as [firmware 13.42's module run on a PC](https://github.com/boykopovar/AnyPS5/pull/10385#issuecomment-6415603047) does. The kernel's handling of a null `name` is unmeasured: here it returns `EINVAL`, following [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/91e6afa69b0a64b0afaa0281b80f8fbb0cd88136/src/core/libraries/kernel/threads/rwlock.cpp#L79-L87). A null lock pointer, a statically initialized lock and a destroyed one fault in the module and throw here
- [pthread_barrier_setname_np](../../../core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp) (libkernel) - the console passes `name` to the kernel's `namedobj_create` and stores the returned id instead of the name; here the name is copied and never read back. Once named, a barrier returns `EAGAIN` (35) for every later call and keeps its name, as [firmware 13.42's module run on a PC](https://github.com/boykopovar/AnyPS5/pull/10385#issuecomment-6415603047) does. The kernel's handling of a null `name` is unmeasured: here it returns 0 and leaves the barrier unnamed. A null barrier pointer, an unset barrier and a destroyed one fault in the module and throw here
- [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
- [sysctl kern.arandom](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) serves FreeBSD 11's MIB {1, 37} (CTL_KERN, KERN_ARANDOM) and the "kern.arandom" sysctlbyname lookup through std::random_device, returning min(*oldLength, 256) random bytes. Following FreeBSD's sysctl_kern_arnd, a null buffer reports min(*oldLength, 256) without writing, a null length returns 0 without writing, and requests with a new value return EPERM (1) as the node is read-only. Cryptographic quality depends on the host's random device implementation.
- [pthread_atfork](../../../core/libs/prx/libkernel/Pthread/Posix/Atfork.cpp) (libkernel) appends the three handlers to a process-wide list and returns 0, or `ENOMEM` when the entry cannot be allocated, as FreeBSD 11.4's [`_pthread_atfork`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_fork.c#L78-L100) does; null handlers are accepted. The handlers never run: `fork` throws and `vfork` is absent, so no child process is created. Handlers of a module that is unloaded stay in the list, where FreeBSD's `__pthread_cxa_finalize` removes them. PPSA97358 has a source-level call from the constructor `rcu_init` ([rcu.c](https://github.com/ZiZc3/XPSemu/blob/8eec07b4e31f66dce8e0b05c1ca1281af303e933/util/rcu.c#L488-L495)), but current-main runtime reachability is unverified