Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ int APS5_VABI pthread_rwlock_timedwrlock_nid_postfix(PthreadRwlock* rwlock, cons
return toPosix(scePthreadRwlockTimedwrlock(rwlock, usec));
}

int APS5_VABI pthread_rwlock_reltimedrdlock_np_nid_postfix(PthreadRwlock* rwlock, KernelUseconds usec) {
initializeStatic(rwlock, __func__);
return toPosix(scePthreadRwlockTimedrdlock(rwlock, usec));
}

int APS5_VABI pthread_rwlock_reltimedwrlock_np_nid_postfix(PthreadRwlock* rwlock, KernelUseconds usec) {
initializeStatic(rwlock, __func__);
return toPosix(scePthreadRwlockTimedwrlock(rwlock, usec));
}

int APS5_VABI pthread_rwlock_tryrdlock_nid_postfix(PthreadRwlock* rwlock) {
initializeStatic(rwlock, __func__);
return toPosix(scePthreadRwlockTryrdlock(rwlock));
Expand Down
10 changes: 0 additions & 10 deletions core/libs/prx/libkernel_sys/Export.Discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,16 +463,6 @@ int APS5_VABI pthread_resume_user_context_np_nid_postfix(void) {
return 0;
}

int APS5_VABI pthread_rwlock_reltimedrdlock_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}

int APS5_VABI pthread_rwlock_reltimedwrlock_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}

int APS5_VABI pthread_rwlock_setname_np_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
Expand Down
49 changes: 49 additions & 0 deletions core/libs/tests/GuestPthreadRwlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ 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_timedrdlock_nid_postfix(PthreadRwlock* rwlock, const KernelTimespec* abstime);
int APS5_VABI pthread_rwlock_timedwrlock_nid_postfix(PthreadRwlock* rwlock, const KernelTimespec* abstime);
int APS5_VABI pthread_rwlock_reltimedrdlock_np_nid_postfix(PthreadRwlock* rwlock, KernelUseconds usec);
int APS5_VABI pthread_rwlock_reltimedwrlock_np_nid_postfix(PthreadRwlock* rwlock, KernelUseconds usec);
int APS5_VABI scePthreadRwlockInit(PthreadRwlock* rwlock, const PthreadRwlockattr* attr, const char* name);
int APS5_VABI scePthreadRwlockDestroy(PthreadRwlock* rwlock);
int APS5_VABI scePthreadRwlockRdlock(PthreadRwlock* rwlock);
Expand Down Expand Up @@ -312,6 +314,52 @@ static void CheckWaitingWriterHoldsBackNewReaders() {
Require(pthread_rwlock_destroy_nid_postfix(&recursive) == 0);
}

using RelativeLock = int (APS5_VABI *)(PthreadRwlock*, KernelUseconds);

static void ExpectRelativeTimeout(RelativeLock lock, PthreadRwlock* rwlock) {
const auto start = std::chrono::steady_clock::now();
Require(lock(rwlock, 20000) == GUEST_ETIMEDOUT);
Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(15));
Require(lock(rwlock, 0) == GUEST_ETIMEDOUT);
}

static void CheckRelativeTimeouts() {
PthreadRwlock fresh[2] = {};
Require(pthread_rwlock_reltimedrdlock_np_nid_postfix(&fresh[0], 0) == 0);
Require(pthread_rwlock_reltimedwrlock_np_nid_postfix(&fresh[1], 0) == 0);
for (auto& lock : fresh) {
Require(lock != nullptr && lock != DestroyedRwlock);
Require(pthread_rwlock_unlock_nid_postfix(&lock) == 0);
Require(pthread_rwlock_destroy_nid_postfix(&lock) == 0);
Require(pthread_rwlock_reltimedrdlock_np_nid_postfix(&lock, 0) == GUEST_EINVAL);
Require(pthread_rwlock_reltimedwrlock_np_nid_postfix(&lock, 0) == GUEST_EINVAL);
}

PthreadRwlock rwlock = nullptr;
Require(pthread_rwlock_reltimedwrlock_np_nid_postfix(&rwlock, 5000000) == 0);
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);

Holder reader{&rwlock, false};
Start(reader);
Require(pthread_rwlock_reltimedrdlock_np_nid_postfix(&rwlock, 0) == 0);
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);
ExpectRelativeTimeout(pthread_rwlock_reltimedwrlock_np_nid_postfix, &rwlock);
reader.release.store(true);
Require(pthread_rwlock_reltimedwrlock_np_nid_postfix(&rwlock, 5000000) == 0);
Require(scePthreadJoin(reader.thread, nullptr) == SCE_OK);
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);

Holder writer{&rwlock, true};
Start(writer);
ExpectRelativeTimeout(pthread_rwlock_reltimedrdlock_np_nid_postfix, &rwlock);
ExpectRelativeTimeout(pthread_rwlock_reltimedwrlock_np_nid_postfix, &rwlock);
writer.release.store(true);
Require(pthread_rwlock_reltimedrdlock_np_nid_postfix(&rwlock, 5000000) == 0);
Require(scePthreadJoin(writer.thread, nullptr) == SCE_OK);
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);
Require(pthread_rwlock_destroy_nid_postfix(&rwlock) == 0);
}

template <typename Call>
static bool Throws(Call call) {
try {
Expand Down Expand Up @@ -354,6 +402,7 @@ int main() {
CheckWaitingWriterHoldsBackNewReaders();
CheckConcurrentFirstWrlockExcludes();
CheckDeadlinesBeyondTheClockRange();
CheckRelativeTimeouts();
const KernelTimespec invalid{0, NANOS_PER_SECOND};
PthreadRwlock fresh[4] = {};
Require(pthread_rwlock_tryrdlock_nid_postfix(&fresh[0]) == 0);
Expand Down
1 change: 1 addition & 0 deletions docs/dev/debt/libkernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
- [scePthreadAttr setters and getters](../../../core/libs/prx/libkernel/Pthread/src/Attr.cpp) (libkernel) - measured on a PS5 (firmware 13.60): a null or uninitialized attribute returns `EINVAL`, as does a null output of the stack size, detach state and scheduling parameter getters; a policy other than 1 and 3, an inheritance other than 0 and 4, or a priority outside 256 to 767 (or a null parameter) returns `ENOTSUP` (`0x8002002D`); a detach state other than 0 and 1 and a stack size below 16384 return `EINVAL`; any affinity mask is accepted. A null attribute pointer to `scePthreadAttrInit` and a null output of `scePthreadAttrGetaffinity` crashed the payload, so they still throw. A new attribute holds a 64 KiB stack size on the console and 1 MiB here, which the host code that runs on guest threads needs. `scePthreadAttrGetschedpolicy` and `scePthreadAttrGetinheritsched` were not measured and follow [FreeBSD 11.4 libthr](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libthr/thread/thr_attr.c#L216-L262); a null output of `scePthreadAttrGetinheritsched` throws, since libthr writes through it unchecked
- [scePthreadAttrGet](../../../core/libs/prx/libkernel/Pthread/src/Attr.cpp) and [pthread_attr_get_np](../../../core/libs/prx/libkernel/Pthread/Posix/Attr.cpp) (libkernel) - the returned scheduling policy is unmeasured on a console. Both report the recorded guest policy, following [FreeBSD 11.4 libthr](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libthr/thread/thr_attr.c#L134-L164)
- [pthread_getprio](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) is unmeasured on the console. NID `aO+WS2xWuo4` is named in the firmware 3.20 [export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libkernel.c). Its return and errno contract follows FreeBSD 11 [thr_getprio.c](https://github.com/freebsd/freebsd-src/blob/671fb7df97631020c42ce55527dc49fffe0656b7/lib/libthr/thread/thr_getprio.c): return the recorded guest priority, or -1 with `ESRCH` for an unknown or finished thread. The live-thread registry lock protects the read from a concurrent join. This reports guest scheduler state, not the host scheduling priority.
- [pthread_rwlock_reltimedrdlock_np, pthread_rwlock_reltimedwrlock_np](../../../core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp) (libkernel) take `(rwlock, usec)`, a relative timeout in microseconds, and are `scePthreadRwlockTimedrdlock` and `scePthreadRwlockTimedwrlock` with POSIX error numbers, so they return what those return (`ETIMEDOUT` when the time runs out, at once for 0 while another thread holds the lock, and `EINVAL` for a destroyed lock); a zero lock is initialized as by `pthread_rwlock_rdlock`. FreeBSD 11.4 libthr has no such functions; the signature comes only from shadPS4, which registers one function for each NID and its `scePthreadRwlockTimed*` twin ([`rwlock.cpp`](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/kernel/threads/rwlock.cpp#L167-L179), 32-bit `usec`). Unverified on the console
- [sceKernelGetAvailableCpumask](../../../core/libs/prx/libkernel/Pthread/src/Thread.cpp) (libkernel) - returns the default thread affinity, 0x1FFF (CPUs 0 to 12), as KytyPS5 does. Titles pin threads within it (PPSA02664 passes 0x1FFB, Hades II PPSA36082 0x3 and 0x3F); whether CPU 13 is available to a title is not confirmed
- [sceKernelGetCurrentCpu](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - guest affinities are not applied to host threads, so the host CPU index is mapped into the calling thread's affinity clipped to `sceKernelGetAvailableCpumask` (the available mask when they do not overlap): host CPU n reports the (n modulo the CPU count)-th CPU of that mask, so a thread pinned to one CPU reports that CPU, as a FreeBSD `cpuset` guarantees on the console. Which CPU the console reports for a thread allowed on several is not known; shadPS4 returns 0
- [pthread_attr_setsolosched_np](../../../core/libs/prx/libkernel/Pthread/Posix/Attr.cpp) (libkernel) takes `(attr, int)` and stores the value through `scePthreadAttrSetsolosched`, returning `EINVAL` (22) for a null or uninitialized attribute like the other POSIX attribute setters. Only the name is public (aerolib `2+pVfgiEd7A`); the signature and behaviour follow the SCE twin, which [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/947627d10d4ef4b68ba3c0189aab6c368e70a8d2/src/kernel/pthread.cpp#L2188-L2199) and [sharpemu](https://github.com/sharpemu/sharpemu/blob/a2983692930564815a77f81e74c151268f5b935f/src/SharpEmu.Libs/Kernel/KernelPthreadExtendedCompatExports.cs#L891-L913) store as an `int` without a range check, and [PS5PCEM](https://github.com/iStark/PS5PCEM/blob/71de7681f06c5db59a1120a3bb554c86780c3f5f/src/hle/libs/kernel_threading.zig#L1396-L1398) implements this alias over it. On a PS5 (firmware 13.60) only 0 and 16 are accepted; every other value returns `ENOTSUP` (45, `0x8002002D` from the SCE call) and keeps the stored one, and the SCE calls return `EINVAL` for a null or uninitialized attribute. PPSA15878 passes 16. Threads created from the attribute are not scheduled differently; what solo scheduling does on the console is unknown. Returnal (PPSA01285 v03.000.005) stopped on it with `what(): pthread_attr_setsolosched_np_nid_postfix not implemented` (#2872, #1155); LEGO Horizon Adventures (PPSA14632, #734) and Palworld (PPSA23126, #2598) import it
Expand Down
Loading