From 91bfd6a18c1642c7ef8255b4cc487310e77e69da Mon Sep 17 00:00:00 2001 From: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Date: Sun, 11 Oct 2026 14:59:12 +0530 Subject: [PATCH] feat(libkernel): implement pthread_attr_getinheritsched, pthread_attr_getscope and pthread_attr_setscope --- .../libs/prx/libkernel/Pthread/Posix/Attr.cpp | 23 ++++ .../prx/libkernel/Pthread/Posix/Common.hpp | 1 + .../prx/libkernel/Pthread/include/Pthread.hpp | 6 + core/libs/prx/libkernel/Pthread/src/Attr.cpp | 1 + .../libs/prx/libkernel/Pthread/src/Thread.cpp | 6 + core/libs/tests/GuestPthreadAttr.cpp | 107 ++++++++++++++++++ docs/dev/TechnicalDebt.md | 1 + 7 files changed, 145 insertions(+) diff --git a/core/libs/prx/libkernel/Pthread/Posix/Attr.cpp b/core/libs/prx/libkernel/Pthread/Posix/Attr.cpp index 0a889b3fd..89488dc78 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Attr.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Attr.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" #include "../include/Pthread.hpp" @@ -33,6 +35,7 @@ int APS5_VABI pthread_attr_get_np_nid_postfix(Pthread thread, PthreadAttr* attr) p->_schedpriority = thread->priority.load(std::memory_order_relaxed); p->_affinity = thread->affinity.load(std::memory_order_relaxed); p->_affinityExplicit = true; + p->_scope = thread->scope; return 0; } @@ -48,6 +51,13 @@ int APS5_VABI pthread_attr_getguardsize_nid_postfix(const PthreadAttr* attr, siz return 0; } +int APS5_VABI pthread_attr_getinheritsched_nid_postfix(const PthreadAttr* attr, int* inherit_sched) { + if (!Valid(attr)) return PosixThread::GUEST_EINVAL; + if (!inherit_sched) throw std::runtime_error(std::string(__func__) + ": null output"); + *inherit_sched = (*attr)->_inheritsched; + return 0; +} + int APS5_VABI pthread_attr_getschedparam_nid_postfix(const PthreadAttr* attr, KernelSchedParam* param) { if (!Valid(attr) || !param) return PosixThread::GUEST_EINVAL; param->sched_priority = (*attr)->_schedpriority; @@ -60,6 +70,12 @@ int APS5_VABI pthread_attr_getschedpolicy_nid_postfix(const PthreadAttr* attr, i return 0; } +int APS5_VABI pthread_attr_getscope_nid_postfix(const PthreadAttr* attr, int* scope) { + if (!Valid(attr) || !scope) return PosixThread::GUEST_EINVAL; + *scope = (*attr)->_scope; + return 0; +} + int APS5_VABI pthread_attr_getstack_nid_postfix(const PthreadAttr* __restrict attr, void** __restrict stack_addr, size_t* __restrict stack_size) { if (!Valid(attr) || !stack_addr || !stack_size) return PosixThread::GUEST_EINVAL; *stack_addr = (*attr)->stackAddress; @@ -92,6 +108,7 @@ int APS5_VABI pthread_attr_setguardsize_nid_postfix(PthreadAttr* attr, size_t gu int APS5_VABI pthread_attr_setinheritsched_nid_postfix(PthreadAttr* attr, int inherit_sched) { if (!Valid(attr)) return PosixThread::GUEST_EINVAL; + if (inherit_sched != GUEST_INHERIT_SCHED && inherit_sched != GUEST_EXPLICIT_SCHED) return PosixThread::GUEST_ENOTSUP; (*attr)->_inheritsched = inherit_sched; return 0; } @@ -108,6 +125,12 @@ int APS5_VABI pthread_attr_setschedpolicy_nid_postfix(PthreadAttr* attr, int pol return 0; } +int APS5_VABI pthread_attr_setscope_nid_postfix(PthreadAttr* attr, int scope) { + if (!Valid(attr) || (scope != GUEST_SCOPE_PROCESS && scope != GUEST_SCOPE_SYSTEM)) return PosixThread::GUEST_EINVAL; + (*attr)->_scope = scope; + return 0; +} + int APS5_VABI pthread_attr_setstacksize_nid_postfix(PthreadAttr* attr, size_t stack_size) { if (!Valid(attr)) return PosixThread::GUEST_EINVAL; return PosixThread::ToErrno(scePthreadAttrSetstacksize(attr, stack_size)); diff --git a/core/libs/prx/libkernel/Pthread/Posix/Common.hpp b/core/libs/prx/libkernel/Pthread/Posix/Common.hpp index cf95f707e..dca57d9df 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Common.hpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Common.hpp @@ -13,6 +13,7 @@ namespace PosixThread { constexpr int GUEST_ESRCH = 3; constexpr int GUEST_EFAULT = 14; constexpr int GUEST_EINVAL = 22; +constexpr int GUEST_ENOTSUP = 45; constexpr int GUEST_ETIMEDOUT = 60; inline int ToErrno(int sceResult) { diff --git a/core/libs/prx/libkernel/Pthread/include/Pthread.hpp b/core/libs/prx/libkernel/Pthread/include/Pthread.hpp index f2df5c9b2..dfb04b5b3 100644 --- a/core/libs/prx/libkernel/Pthread/include/Pthread.hpp +++ b/core/libs/prx/libkernel/Pthread/include/Pthread.hpp @@ -83,6 +83,10 @@ static constexpr int DEFAULT_THREAD_PRIORITY = 700; static constexpr std::uint32_t CPU_CLOCK_BIT = 0x80000000u; static constexpr std::uint32_t CPU_CLOCK_PROCESS_BIT = 0x40000000u; static constexpr std::uint32_t CPU_CLOCK_ID_MASK = ~(CPU_CLOCK_BIT | CPU_CLOCK_PROCESS_BIT); +static constexpr int GUEST_EXPLICIT_SCHED = 0; +static constexpr int GUEST_INHERIT_SCHED = 4; +static constexpr int GUEST_SCOPE_PROCESS = 0; +static constexpr int GUEST_SCOPE_SYSTEM = 2; inline int GuestCpuFromHost(unsigned hostCpu, KernelCpumask threadAffinity) { KernelCpumask mask = threadAffinity & DEFAULT_THREAD_AFFINITY; @@ -102,6 +106,7 @@ struct PthreadAttrPrivate { bool _affinityExplicit = false; std::size_t _guardsize = 0x1000; int _solosched = 0; + int _scope = GUEST_SCOPE_SYSTEM; }; inline std::atomic nextThreadId{100000}; @@ -123,6 +128,7 @@ struct PthreadPrivate { std::atomic waitCount{0}; std::atomic affinity{DEFAULT_THREAD_AFFINITY}; std::atomic priority{DEFAULT_THREAD_PRIORITY}; + int scope = GUEST_SCOPE_SYSTEM; int cpuClockThread = 0; #ifndef _WIN32 clockid_t hostCpuClock{}; diff --git a/core/libs/prx/libkernel/Pthread/src/Attr.cpp b/core/libs/prx/libkernel/Pthread/src/Attr.cpp index ff5cba730..7b5b70e1d 100644 --- a/core/libs/prx/libkernel/Pthread/src/Attr.cpp +++ b/core/libs/prx/libkernel/Pthread/src/Attr.cpp @@ -84,6 +84,7 @@ int APS5_VABI scePthreadAttrGet(Pthread thread, PthreadAttr* attr) { (*attr)->_inheritsched = 4; (*attr)->_affinity = thread->affinity.load(std::memory_order_relaxed); (*attr)->_affinityExplicit = true; + (*attr)->_scope = thread->scope; return SCE_OK; } diff --git a/core/libs/prx/libkernel/Pthread/src/Thread.cpp b/core/libs/prx/libkernel/Pthread/src/Thread.cpp index 11400bdd2..b02675d7a 100644 --- a/core/libs/prx/libkernel/Pthread/src/Thread.cpp +++ b/core/libs/prx/libkernel/Pthread/src/Thread.cpp @@ -529,6 +529,12 @@ int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, Pthread p->stackSize = attr ? (*attr)->_stacksize : DEFAULT_STACK_SIZE; if (attr) { p->priority.store((*attr)->_schedpriority, std::memory_order_relaxed); + if ((*attr)->_inheritsched != GUEST_INHERIT_SCHED) + p->scope = (*attr)->_scope; + else if (CurrentGuestState().current) + p->scope = CurrentGuestState().current->scope; + } else if (CurrentGuestState().current) { + p->scope = CurrentGuestState().current->scope; } if (name) p->name = name; std::promise start; diff --git a/core/libs/tests/GuestPthreadAttr.cpp b/core/libs/tests/GuestPthreadAttr.cpp index ee24e0000..97c22b9a0 100644 --- a/core/libs/tests/GuestPthreadAttr.cpp +++ b/core/libs/tests/GuestPthreadAttr.cpp @@ -22,11 +22,19 @@ int APS5_VABI scePthreadAttrGetdetachstate(const PthreadAttr* attr, int* state); int APS5_VABI scePthreadAttrGetsolosched(const PthreadAttr* attr, int* solosched); int APS5_VABI pthread_attr_setstacksize_nid_postfix(PthreadAttr* attr, std::size_t stackSize); int APS5_VABI pthread_attr_setsolosched_np_nid_postfix(PthreadAttr* attr, int solosched); +int APS5_VABI pthread_attr_getinheritsched_nid_postfix(const PthreadAttr* attr, int* inherit_sched); +int APS5_VABI pthread_attr_setinheritsched_nid_postfix(PthreadAttr* attr, int inherit_sched); +int APS5_VABI pthread_attr_getscope_nid_postfix(const PthreadAttr* attr, int* scope); +int APS5_VABI pthread_attr_setscope_nid_postfix(PthreadAttr* attr, int scope); +int APS5_VABI pthread_attr_get_np_nid_postfix(Pthread thread, PthreadAttr* attr); } static constexpr int SCE_OK = 0; static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast(0x80020016); static constexpr int EXPLICIT_SCHED = 0; +static constexpr int INHERIT_SCHED = 4; +static constexpr int SCOPE_PROCESS = 0; +static constexpr int SCOPE_SYSTEM = 2; static constexpr int DETACH_JOINABLE = 0; static constexpr int CREATION_PRIORITY = 256; static constexpr int UPDATED_PRIORITY = 767; @@ -35,6 +43,7 @@ static constexpr KernelCpumask UPDATED_AFFINITY = 0x1000; static constexpr std::size_t STACK_SIZE = 2u << 20; static constexpr std::size_t MIN_STACK_SIZE = 16384; static constexpr int GUEST_EINVAL = 22; +static constexpr int GUEST_ENOTSUP = 45; static void Require(bool value) { if (!value) std::abort(); } @@ -98,9 +107,107 @@ static void* APS5_VABI Worker(void* arg) { return nullptr; } +static void CheckSchedulingAttributes() { + int inherit = -1; + int scope = -1; + Require(pthread_attr_getinheritsched_nid_postfix(nullptr, &inherit) == GUEST_EINVAL); + Require(pthread_attr_setinheritsched_nid_postfix(nullptr, EXPLICIT_SCHED) == GUEST_EINVAL); + Require(pthread_attr_getscope_nid_postfix(nullptr, &scope) == GUEST_EINVAL); + Require(pthread_attr_setscope_nid_postfix(nullptr, SCOPE_SYSTEM) == GUEST_EINVAL); + + PthreadAttr attr = nullptr; + Require(pthread_attr_getinheritsched_nid_postfix(&attr, &inherit) == GUEST_EINVAL); + Require(pthread_attr_setinheritsched_nid_postfix(&attr, EXPLICIT_SCHED) == GUEST_EINVAL); + Require(pthread_attr_getscope_nid_postfix(&attr, &scope) == GUEST_EINVAL); + Require(pthread_attr_setscope_nid_postfix(&attr, SCOPE_SYSTEM) == GUEST_EINVAL); + + Require(scePthreadAttrInit(&attr) == SCE_OK); + Require(pthread_attr_getinheritsched_nid_postfix(&attr, &inherit) == 0 && inherit == INHERIT_SCHED); + Require(pthread_attr_setinheritsched_nid_postfix(&attr, 1) == GUEST_ENOTSUP); + Require(pthread_attr_setinheritsched_nid_postfix(&attr, -1) == GUEST_ENOTSUP); + Require(pthread_attr_setinheritsched_nid_postfix(&attr, EXPLICIT_SCHED) == 0); + Require(pthread_attr_getinheritsched_nid_postfix(&attr, &inherit) == 0 && inherit == EXPLICIT_SCHED); + Require(pthread_attr_setinheritsched_nid_postfix(&attr, INHERIT_SCHED) == 0); + Require(pthread_attr_getinheritsched_nid_postfix(&attr, &inherit) == 0 && inherit == INHERIT_SCHED); + + Require(pthread_attr_getscope_nid_postfix(&attr, &scope) == 0 && scope == SCOPE_SYSTEM); + Require(pthread_attr_setscope_nid_postfix(&attr, 1) == GUEST_EINVAL); + Require(pthread_attr_setscope_nid_postfix(&attr, 3) == GUEST_EINVAL); + Require(pthread_attr_setscope_nid_postfix(&attr, -1) == GUEST_EINVAL); + Require(pthread_attr_setscope_nid_postfix(&attr, SCOPE_PROCESS) == 0); + Require(pthread_attr_getscope_nid_postfix(&attr, &scope) == 0 && scope == SCOPE_PROCESS); + Require(pthread_attr_setscope_nid_postfix(&attr, SCOPE_SYSTEM) == 0); + Require(pthread_attr_getscope_nid_postfix(&attr, &scope) == 0 && scope == SCOPE_SYSTEM); + + Require(pthread_attr_getscope_nid_postfix(&attr, nullptr) == GUEST_EINVAL); + + bool threw = false; + try { + pthread_attr_getinheritsched_nid_postfix(&attr, nullptr); + } catch (const std::exception&) { + threw = true; + } + Require(threw); + + Require(scePthreadAttrDestroy(&attr) == SCE_OK); + Require(pthread_attr_getinheritsched_nid_postfix(&attr, &inherit) == GUEST_EINVAL); + Require(pthread_attr_setinheritsched_nid_postfix(&attr, EXPLICIT_SCHED) == GUEST_EINVAL); + Require(pthread_attr_getscope_nid_postfix(&attr, &scope) == GUEST_EINVAL); + Require(pthread_attr_setscope_nid_postfix(&attr, SCOPE_SYSTEM) == GUEST_EINVAL); +} + +static void CheckThreadScope() { + PthreadAttr attr = nullptr; + Require(scePthreadAttrInit(&attr) == SCE_OK); + Require(pthread_attr_setinheritsched_nid_postfix(&attr, EXPLICIT_SCHED) == 0); + Require(pthread_attr_setscope_nid_postfix(&attr, SCOPE_PROCESS) == 0); + + std::promise release; + auto released = release.get_future(); + Pthread thread = nullptr; + Require(scePthreadCreate(&thread, &attr, Worker, &released, nullptr) == SCE_OK); + Require(scePthreadAttrDestroy(&attr) == SCE_OK); + + PthreadAttr queryAttr = nullptr; + Require(scePthreadAttrInit(&queryAttr) == SCE_OK); + Require(scePthreadAttrGet(thread, &queryAttr) == SCE_OK); + int scope = -1; + Require(pthread_attr_getscope_nid_postfix(&queryAttr, &scope) == 0 && scope == SCOPE_PROCESS); + + Require(pthread_attr_get_np_nid_postfix(thread, &queryAttr) == 0); + Require(pthread_attr_getscope_nid_postfix(&queryAttr, &scope) == 0 && scope == SCOPE_PROCESS); + Require(scePthreadAttrDestroy(&queryAttr) == SCE_OK); + + release.set_value(); + Require(scePthreadJoin(thread, nullptr) == SCE_OK); + + Require(scePthreadAttrInit(&attr) == SCE_OK); + Require(pthread_attr_setinheritsched_nid_postfix(&attr, INHERIT_SCHED) == 0); + Require(pthread_attr_setscope_nid_postfix(&attr, SCOPE_PROCESS) == 0); + + std::promise releaseInherited; + auto releasedInherited = releaseInherited.get_future(); + Pthread inheritedThread = nullptr; + Require(scePthreadCreate(&inheritedThread, &attr, Worker, &releasedInherited, nullptr) == SCE_OK); + Require(scePthreadAttrDestroy(&attr) == SCE_OK); + + Require(scePthreadAttrInit(&queryAttr) == SCE_OK); + Require(scePthreadAttrGet(inheritedThread, &queryAttr) == SCE_OK); + scope = -1; + Require(pthread_attr_getscope_nid_postfix(&queryAttr, &scope) == 0 && scope == SCOPE_SYSTEM); + Require(pthread_attr_get_np_nid_postfix(inheritedThread, &queryAttr) == 0); + Require(pthread_attr_getscope_nid_postfix(&queryAttr, &scope) == 0 && scope == SCOPE_SYSTEM); + Require(scePthreadAttrDestroy(&queryAttr) == SCE_OK); + + releaseInherited.set_value(); + Require(scePthreadJoin(inheritedThread, nullptr) == SCE_OK); +} + int main() { CheckStackSizeLimit(); CheckSoloSched(); + CheckSchedulingAttributes(); + CheckThreadScope(); PthreadAttr attr = nullptr; Require(scePthreadAttrInit(&attr) == SCE_OK); Require(scePthreadAttrSetinheritsched(&attr, EXPLICIT_SCHED) == SCE_OK); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index fbf9f8e45..90e0dc5a6 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -386,6 +386,7 @@ Throughout the project, every function at every stage either **does exactly what - [realpath](../../core/libs/prx/libc/src/RealPath.cpp) (libc) follows FreeBSD 11's [`realpath.c`](https://github.com/freebsd/freebsd-src/blob/stable/11/lib/libc/stdlib/realpath.c) except for symlinks. The console's guest filesystem has none, so a host symlink under the guest root is followed for existence and type but not expanded, and `..` drops the previous component lexically, as `Resolve` does for every guest path. Through a host link `linkdir -> sub/deep`, `linkdir/../x` gives `x` beside the link, not beside its target. - [nan and nanf](../../core/libs/prx/libc/src/Math.cpp) (libc) return fixed quiet NaN values (`0x7fc00000` and `0x7ff8000000000000`), ignoring the tag argument as observed in the console's `libc.prx` (both exports load a global constant via `mov rax,[rip+..]; vmovsd xmm0,[rax]; ret`); VLC (PPSA85300) stops on nanf at start-up after strsep. - [Draw input snapshots](../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp) (libSceAgcDriver): `PrepareDrawBindings` caps read-only input buffer snapshots at 64 MiB (overridable via `APS5_DRAW_SNAPSHOT_MAX_MIB`). Larger inputs (streaming heaps, bindless pools) or draws exceeding the cumulative budget skip the per-draw snapshot and bind in place, avoiding multi-gigabyte CPU copy stalls (such as Ratchet & Clank: Rift Apart PPSA01474 stalling at frame ~380 copying 36 GB every 5 s). If the title rewrites those large ranges on the CPU before GPU completion, the in-place binding observes the modification, as verified by `drawSnapshotCapTests`. +- [pthread_attr_getinheritsched, pthread_attr_setinheritsched, pthread_attr_getscope, pthread_attr_setscope](../../core/libs/prx/libkernel/Pthread/Posix/Attr.cpp) (libkernel) follow FreeBSD 11 libthr ([thr_attr.c](https://github.com/freebsd/freebsd-src/blob/5ed7eb0d97ba4436218e810f61bd059acba984c2/lib/libthr/thread/thr_attr.c), [thr_create.c](https://github.com/freebsd/freebsd-src/blob/5ed7eb0d97ba4436218e810f61bd059acba984c2/lib/libthr/thread/thr_create.c)), not measured on the console: the scope defaults to `PTHREAD_SCOPE_SYSTEM` (2), only it and `PTHREAD_SCOPE_PROCESS` (0) are accepted and other values return `EINVAL`, and an inherit value other than `PTHREAD_INHERIT_SCHED` (4) or `PTHREAD_EXPLICIT_SCHED` (0) returns `ENOTSUP`. A null output of `pthread_attr_getinheritsched` throws, since libthr writes through it unchecked. A thread keeps the scope it was created with, taken from the creating thread under `PTHREAD_INHERIT_SCHED`, and `pthread_attr_get_np` and `scePthreadAttrGet` report it, but every guest thread is a host thread at system scope, so process scope changes nothing - [SPIRV-Tools optimization](../../core/shader/recompiler/SpirvBackend/src/SpirvOptimizer.cpp) skips CCP and Simplification for modules with storage-only 8- or 16-bit types because the pinned folder produces illegal narrow constants. This also disables unrelated constant folding; the restricted full pipeline mirrors the pinned performance recipe. - [SRT signed comparison](../../core/shader/recompiler/Optimization/src/SrtWalker/SrtEvaluator.cpp) evaluates `SLessThan32` as signed 32-bit less-than, matching the existing SPIR-V backend and [SPIR-V `OpSLessThan`](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpSLessThan). Signed boundary descriptor expressions are tested on the host; native RDNA2/PS5 execution and title behavior remain unverified. - On macOS the [write watch](../../core/libs/prx/libc/src/GuestWriteWatch.cpp) write-protects watched pages and records the first write to each from a SIGBUS/SIGSEGV handler, because macOS has no counterpart to `userfaultfd` write-protect or `MEM_WRITE_WATCH`. The protection is real, so: