Skip to content

feat(libkernel): implement pthread_attr scope, inheritsched and stackaddr getters - #1912

Closed
TheAggie wants to merge 1 commit into
boykopovar:mainfrom
TheAggie:feat/libkernel-pthread-attr-scope-stack
Closed

TheAggie wants to merge 1 commit into
boykopovar:mainfrom
TheAggie:feat/libkernel-pthread-attr-scope-stack

Conversation

@TheAggie

@TheAggie TheAggie commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

What

Four POSIX attribute functions were not exported:

  • pthread_attr_getscope and pthread_attr_setscope keep the contention scope. It is system scope by default, as in FreeBSD's libthr. Only process (0) and system (2) scope are accepted; anything else is EINVAL.
  • pthread_attr_getinheritsched and pthread_attr_getstackaddr report the attribute's existing fields.

A null attribute or a null output pointer is EINVAL. The scope is stored on the attribute (Pthread.hpp); it does not change how threads are created.

ccleste (PPSA59322) imports these.

Test (guest_pthread_attr): inheritsched reads its default and a set value; scope defaults to system, reads back process scope after a set, and rejects 1 without changing; stackaddr is null by default; null outputs and a null attribute return EINVAL.

Tested

Windows 11 Pro (26200), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release.

  • The full suite (489 tests) passes on current main with this PR and four other small PRs of mine merged together (SCHED_OTHER priority range, MAP_ALIGNED_SUPER, the compatible pad vibration mode, empty-PT_TLS stack-guard reads, pthread_attr scope/getters). It was run from the generated CTestTestfile.cmake commands because antivirus blocks ctest.exe on this machine.
  • tools/check_conventions.py --base origin/main passes on this branch alone.

Not tested on Linux or macOS.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

🤖 Generated with Claude Code

…addr getters

pthread_attr_getscope and pthread_attr_setscope keep the contention scope,
system by default as in FreeBSD's libthr, and accept only process and system
scope. pthread_attr_getinheritsched and pthread_attr_getstackaddr report the
attribute's fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Progress report (f145139 → 886fd8b)

📈 System libraries: 84.78% (+0.02%, +4 implemented functions)

✅ 4 implemented
Library Name
libkernel pthread_attr_getinheritsched_nid_postfix
libkernel pthread_attr_getscope_nid_postfix
libkernel pthread_attr_getstackaddr_nid_postfix
libkernel pthread_attr_setscope_nid_postfix

@klosixgaming-arch klosixgaming-arch left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, correct addition of the missing pthread_attr accessors. Each getter validates both the attr handle and the out-pointer (EINVAL otherwise), matching the existing pthread_attr_* style in this file, and pthread_attr_setscope correctly accepts only PTHREAD_SCOPE_SELF/PTHREAD_SCOPE_SYSTEM. The new _scope field defaults to PTHREAD_SCOPE_SYSTEM (2), the POSIX default. The PosixAttributes test covers the happy path, the EINVAL paths (null out-pointer, null attr), and scope validation (0 and 2 accepted, 1 rejected).

Written with AI assistance.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Closing: ccleste (PPSA59322) stops at shm_open on main, before these attribute calls. Reopen when one is a title's next stop on main, with the log line.

@Amoenus

Amoenus commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Additional Windows validation of PR head 886fd8b47ce1da343b134b572fb060d697ac153e applied locally to main 57f96fd2b36563e8380c603a92b1ef0d9d3a6aa4.

Windows 11 Enterprise 26200, WinLibs r7 MinGW-w64 GCC 15.2.0, Release, BUILD_TESTING=ON. Full system-library build passed; existing guest_pthread_attr and guest_pthread_stack passed 5/5 runs each. No full-suite, Linux, or console result is claimed.

An additional scope-query probe failed 5/5: create a thread with explicit system scope (2); set the destination attribute to process scope (0); query that thread with pthread_attr_get_np; read pthread_attr_getscope. It reports the old destination value 0. scePthreadAttrGet also leaves the destination scope at 0. Both queries and both reads return success. The worker is kept alive until both queries finish, then joined.

expected=2 posix_query=0 posix_read=0 posix_scope=0 sce_query=0 sce_read=0 sce_scope=0 joined=0

This is a limitation of combining the proposed new scope field with existing query paths, not a regression claim against main where that scope export is absent. For the POSIX query, FreeBSD 11 thr_attr.c copies the thread's attribute state into the destination. Its setters accept both scope values, and thr_create.c inherits scope when inherited scheduling is selected. These are source references, not measurements on PS5.

Probe source, linked to the locally built unpatched libkernel/libc with the repository's Windows unwind setup (-fno-asynchronous-unwind-tables, then .eh_frame renamed to .ehfram):

#include "SceTypes.hpp"
#include <atomic>
#include <cstdio>
#include <thread>

extern "C" {
int APS5_VABI pthread_attr_init_nid_postfix(PthreadAttr*);
int APS5_VABI pthread_attr_destroy_nid_postfix(PthreadAttr*);
int APS5_VABI pthread_attr_setscope_nid_postfix(PthreadAttr*, int);
int APS5_VABI pthread_attr_getscope_nid_postfix(const PthreadAttr*, int*);
int APS5_VABI pthread_attr_setinheritsched_nid_postfix(PthreadAttr*, int);
int APS5_VABI pthread_create_nid_postfix(Pthread*, const PthreadAttr*, PthreadEntry, void*);
int APS5_VABI pthread_join_nid_postfix(Pthread, void**);
int APS5_VABI pthread_attr_get_np_nid_postfix(Pthread, PthreadAttr*);
int APS5_VABI scePthreadAttrGet(Pthread, PthreadAttr*);
}

static void* APS5_VABI Worker(void* argument) {
    auto& release = *static_cast<std::atomic<bool>*>(argument);
    while (!release.load()) std::this_thread::yield();
    return nullptr;
}

int main() {
    PthreadAttr created = nullptr;
    PthreadAttr queried = nullptr;
    if (pthread_attr_init_nid_postfix(&created) != 0 || pthread_attr_init_nid_postfix(&queried) != 0) return 10;
    if (pthread_attr_setscope_nid_postfix(&created, 2) != 0 || pthread_attr_setinheritsched_nid_postfix(&created, 0) != 0) return 11;
    std::atomic<bool> release{false};
    Pthread thread = nullptr;
    if (pthread_create_nid_postfix(&thread, &created, Worker, &release) != 0) return 12;
    int posix = -1;
    int sce = -1;
    if (pthread_attr_setscope_nid_postfix(&queried, 0) != 0) return 13;
    const int posixResult = pthread_attr_get_np_nid_postfix(thread, &queried);
    const int posixRead = pthread_attr_getscope_nid_postfix(&queried, &posix);
    if (pthread_attr_setscope_nid_postfix(&queried, 0) != 0) return 14;
    const int sceResult = scePthreadAttrGet(thread, &queried);
    const int sceRead = pthread_attr_getscope_nid_postfix(&queried, &sce);
    release.store(true);
    const int joined = pthread_join_nid_postfix(thread, nullptr);
    pthread_attr_destroy_nid_postfix(&created);
    pthread_attr_destroy_nid_postfix(&queried);
    std::printf("expected=2 posix_query=%d posix_read=%d posix_scope=%d sce_query=%d sce_read=%d sce_scope=%d joined=%d\n", posixResult, posixRead, posix, sceResult, sceRead, sce, joined);
    return posixResult == 0 && posixRead == 0 && posix == 2 && sceResult == 0 && sceRead == 0 && sce == 2 && joined == 0 ? 0 : 1;
}

In a separate local diagnostic change, I reset queried scope to system scope and explicitly throw on thread creation requesting process scope with explicit scheduling. Only system-scope threads are represented in that local model; inherited scheduling therefore inherits system scope. This does not implement process-scope scheduling or establish PS5 scope semantics.

The expanded behavioral test checks non-null stack-address readback, null arguments, invalid scope values, stale destination scope replacement in both query paths, inherited scope, and the exact unsupported-process-scope exception with no worker creation or errno mutation. Full system libraries built; guest_pthread_attr, guest_pthread_stack, and guest_thread_self each passed 5/5, and the independent query probe changed to 5/5 pass. Six separate mutation controls (each query update removed, constant scope/stack/inheritance getters, and removed process-scope guard) were all caught; restored code passed again. Conventions and test-registration checks passed. This is diagnostic work kept local, not a new PR or a complete scheduling implementation.

PPSA99902 reaches the missing scope import only with a stack of other local fixes/placeholders. It is not the title's next stop on unchanged main, so this is not a request to reopen under the first-stop rule. At the time of the original validation, no guest entry or gameplay had been reached.

AI-assisted investigation and local changes: Codex.

Follow-up (2026-10-10): integrating the attribute proposal plus the local scope-query correction into the broader diagnostic stack reached actual guest entry with unused-filter=2. One run of local commit 6469b3cd4cc63c74508e0f1a9320851161d8a10a and four runs of the following link-placeholder stack 6947bf8db4bd07ae1f661471ae80ef39e9d9a52d all stopped at the same null read through the libc strcmp wrapper. The traced immediate call is strcmp("Project-A.exe", nullptr); the null's origin is unconfirmed. This conditional result includes other local changes and does not show that this proposal alone starts the title. No rendering or gameplay verified. The unchanged-main first-stop condition remains unmet. Details are being added to the existing startup tracking issue #1578.

@babyhuey

Copy link
Copy Markdown
Contributor

@TheAggie pthread_attr_getscope is now the next stop for three catalog titles on main c6ca798, with _umtx_op (#1482) merged (Linux, catalog builds with sce_module/libc.prx unwrapped as #2605 requires), each with app.elf: symbol lookup error: app.elf: undefined symbol: e2G+cdEkOmU:

  • ccleste (PPSA59322)
  • RommPS (PPSA76677)
  • Quake3e (PPSA99303)

All three go on to pthread_attr_getstackaddr (DxmIMUQ-wXY) once getscope is in, and this PR has both. The head (886fd8b) now conflicts with main in GuestPthreadAttr.cpp.

@pushkarreddyy opened #3377 an hour ago with getscope, getinheritsched and setscope; intake closed it. On 3377's head merged onto c6ca798, guest_pthread_attr passes 5/5 and the three titles reach pthread_attr_getstackaddr, so that one alone doesn't get them past.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants