Skip to content

fix(libkernel): keep host fault signals available on macOS - #8477

Merged
oneandonlydean merged 6 commits into
boykopovar:mainfrom
ycarogabriel-lgtm:codex/macos-host-fault-signals
Nov 2, 2026
Merged

oneandonlydean merged 6 commits into
boykopovar:mainfrom
ycarogabriel-lgtm:codex/macos-host-fault-signals

Conversation

@ycarogabriel-lgtm

@ycarogabriel-lgtm ycarogabriel-lgtm commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What

Keep synchronous host fault signals deliverable while scePthreadCreate creates a guest thread, and unblock them when the guest thread starts on macOS. The guest signal mask remains emulated by libkernel. This prevents a guest-created thread under Rosetta from inheriting a host mask that hides SIGBUS/SIGSEGV write-watch faults.

The focused test checks that the creator's host mask is restored and the guest worker can receive fault signals. This extracts the issue #1 finding by @Cloud-Multiplayer from the former integration PR #7332. No title run is claimed for this branch.

Tested

  • Merged current main through f2d874c1fd in adefe80eea; resolved the Thread.cpp conflict while preserving both Windows pending-exception delivery and the macOS fault-signal mask behavior.
  • python3 tools/check_conventions.py --base origin/main: passed.
  • ctest --test-dir build-mac -R '^public_headers$' -j1 --output-on-failure: passed.
  • The M4 Pro, M1 Pro and M2 Pro integrated macOS test reports below confirm the focused regression test and related pthread/write-watch paths pass with this change.
  • A standalone macOS build against current main remains blocked by existing host portability gaps: pthread_getattr_np and pthread_getcpuclockid in Thread.cpp, st_atim/st_mtim/st_ctim in NativeStat.cpp, MFD_CLOEXEC in SharedMemory.cpp, and sys/vfs.h in Stdio.cpp. These errors prevent building the focused test on this base; the test has not been rerun locally after the merge.
  • Linux and Windows were not tested locally. No GPU code changed.

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; silent stubs are listed in technical debt
  • No notes, investigation .md files or images added to the repository
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none for this isolated signal-mask fix
  • Fixes: none
  • Unblocks: write-watch progress in Stray on the integrated macOS build reported by @imellag (a later depth-bounds limitation remains)
  • GPUs tested: n/a
  • AI-assisted: yes

Rosetta may swallow or retry write-watch faults when a guest thread inherits a blocked host SIGBUS or SIGSEGV mask. Keep synchronous fault signals deliverable during thread creation and after startup while preserving the creator mask. Add a macOS regression test for both behaviors.

Based on Cloud-Multiplayer’s write-watch investigation in boykopovar#1.
Keep the focused host-signal change current with main while preserving its one-topic diff.
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 3986 core/libs/prx/libkernel/Pthread/src/Thread.cpp
PR 4563 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/Pthread/src/Thread.cpp
PR 5021 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/Pthread/src/Thread.cpp
PR 6936 core/libs/prx/libkernel/CMakeLists.txt
PR 7149 core/libs/prx/libkernel/Pthread/src/Thread.cpp
PR 7192 core/libs/prx/libkernel/CMakeLists.txt
PR 7804 core/libs/prx/libkernel/Pthread/src/Thread.cpp
PR 8592 core/libs/prx/libkernel/Pthread/src/Thread.cpp
PR 9019 core/libs/prx/libkernel/CMakeLists.txt
PR 9156 core/libs/prx/libkernel/CMakeLists.txt
PR 9293 core/libs/prx/libkernel/CMakeLists.txt
PR 9297 core/libs/prx/libkernel/CMakeLists.txt
PR 9302 core/libs/prx/libkernel/CMakeLists.txt
PR 9383 core/libs/prx/libkernel/CMakeLists.txt
PR 9389 core/libs/prx/libkernel/CMakeLists.txt
PR 9557 core/libs/prx/libkernel/CMakeLists.txt
PR 9566 core/libs/prx/libkernel/CMakeLists.txt
PR 9938 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/Pthread/src/Thread.cpp
PR 9947 core/libs/prx/libkernel/CMakeLists.txt
PR 11625 core/libs/prx/libkernel/CMakeLists.txt
PR 11831 core/libs/prx/libkernel/CMakeLists.txt
PR 11896 core/libs/prx/libkernel/CMakeLists.txt
PR 11939 core/libs/prx/libkernel/CMakeLists.txt
PR 12544 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/Pthread/src/Thread.cpp
PR 12642 core/libs/prx/libkernel/Pthread/src/Thread.cpp
PR 14299 core/libs/prx/libkernel/CMakeLists.txt

@imellag

imellag commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Ran the new test standalone on Apple silicon, since the description says it could not run on this branch alone.

Machine: Apple M4 Pro (24 GB), macOS 27.0.1 (26A434), x86-64 under Rosetta 2, Apple clang 21.0.0, CMake 4.4.4, Ninja 1.13.2, Release, CMAKE_OSX_ARCHITECTURES=x86_64, no Vulkan loader.

Base: main does not build the libraries on macOS, so I used mugurc's ci/macos-series-2 at 53db0badb, plus the three code commits of #7937 (c6f44a028, ca0068033, 4e5dba2db: without them DirectMemory/Jit.cpp stops on MFD_CLOEXEC), plus this PR's f22b57083 cherry-picked (applies cleanly).

guest_pthread_host_fault_mask full suite (ctest -j6, 1649 tests)
with f22b57083 passed 11/11 17 failed
Thread.cpp reverted to the base, test kept aborted 5/5 19 failed: the same 17, plus guest_pthread_host_fault_mask and precise_wait

The 17 fail without the fix too (and again when rerun one by one with it), so they come from the base, not from this change. precise_wait is a timing test that is flaky on this host under load.

The 17 failures common to both runs

guest_math (does not compile: libc++ has no std::cyl_bessel_j/std::cyl_neumann), guest_math_exact, guest_standard_streams, agc_write_tracking ("a block the import window did not touch reads as written"), agc_performance_overlay_statistics, guest_sce_net, guest_ssl_transport_kernel, guest_ssl_transport_net, guest_memory_lifetime, guest_filesystem, guest_host_standard_error, guest_itimer (SIGALRM delivery to a guest handler not implemented), guest_shm_open, guest_poll, guest_socket_pair, guest_kernel_app_archive_mmap, guest_flexible_memory_declared.

Not checked: a title with and without the fix (Planet of Lana stops on this base at a pixel shader that reads barycentrics, before any write-watch path matters), Linux and Windows (the change is __APPLE__ only), and the GPU suite without the fix (with it, 761 of 807 AGC and shader tests pass on MoltenVK 1.4.2, but I have no run without it to compare).

AI-assisted: yes

@imellag

imellag commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

A title that needs this fix: Stray (PPSA02100, v01.002.000, Unreal Engine 4) hangs twice on macOS without it, and gets past both with only f22b570 added.

Machine: Apple M4 Pro (24 GB), macOS 27.0.1, x86-64 under Rosetta 2, MoltenVK 1.4.2 (Vulkan SDK 1.4.363.0).
Build: #8658 (0a6c0fd) + #9193 + #9263 + the macOS RTLD_GLOBAL loader change now in #8658, Release.

without f22b570 with f22b570
1st stop, about 10 s in hangs at 0% CPU: the main guest thread is in RegisterShader → VulkanDevice → PrepareImportWatch → decideImportWatch, during the import write-protection probe probe finishes: host imports resolve write protection: no (0 of 4 scratch pages ...)
2nd stop, with the probe skipped (APS5_WRITE_WATCH_IMPORTS=unwatch), after about 3,400 shaders are prepared hangs at 0% CPU: the packet worker is in Driver::preparePacketMemory → GuestMemory::Write → storeOwn, inside the store to guest memory no hang
result no further progress in 120 s runs on to the next, unrelated stop: device does not support the depth bounds test (MoltenVK has no depthBounds)

Both hangs are a plain store to a write-watched page that never returns, which is what the description explains: the threads inherit a mask with SIGSEGV/SIGBUS blocked, so the fault is never delivered. Each hang reproduced in two runs; the stacks are from sample. God of War (PPSA28997) and Planet of Lana (PPSA19450) reach gameplay on the same stack without this fix, so Stray is the first title I have that depends on it.

Not checked: the depth bounds stop and anything after it, Linux and Windows, the full CTest suite.

AI-assisted: yes

@maximilianfv

Copy link
Copy Markdown
Contributor

Tested on Apple M1 Pro, macOS 27.0.1 (x86_64 build under Rosetta 2)

Applied at 9ea069639 to mugurc's macOS series (27e968974) with other open macOS fixes. "Without" keeps this PR's new test and leaves out only the Thread.cpp change.

guest_pthread_host_fault_mask other guest_pthread*, *write_watch*, guest_raise_exception* (39 tests)
Without the Thread.cpp change Subprocess aborted: Require at GuestPthreadHostFaultMask.cpp:17 in CheckMask (a fault signal is blocked in the guest thread's host mask) Passed
With this PR Passed Passed

AI-assisted: yes (Claude Code applied the PR, ran the tests and wrote this report).

@iamthehimansh

Copy link
Copy Markdown
Contributor

Tested on an Apple M2 Pro, macOS, x86_64 Release under Rosetta 2, MoltenVK 1.4.2, applied on top of #8658's feat/macos-port (27abf7e4b) together with #8477, #9263, #9746 and #9406. I built the whole tree and ran the full suite (1,962 tests), then ran any new failure 3x on its own with each PR alone.

AI-assisted: yes (Cana)

Keep the focused host-signal change reviewable against current main while preserving the upstream thread and test changes. The standalone macOS build still depends on the macOS libc foundation.
@maximilianfv

Copy link
Copy Markdown
Contributor

Tested on Apple M1 Pro, macOS 27.0.1 (x86_64 build under Rosetta 2)

Retest at fc9716dad. Since my report at 9ea069639, the only new commit merges main through 137a8fab89. The PR's own changes, git diff 137a8fab89 fc9716dad against git diff 30fda0a03 9ea069639, add and remove the same lines, so the code is unchanged.

Applied at fc9716dad to mugurc's macOS series (27e968974) merged with main at 1e20650e84, with other open macOS fixes, then toggled only this PR. "Without" keeps this PR's new test and leaves out only the Thread.cpp change.

guest_pthread_host_fault_mask other guest_pthread*, *write_watch*, guest_raise_exception* (39 tests)
Without the Thread.cpp change Subprocess aborted (3 of 3 runs): Require at GuestPthreadHostFaultMask.cpp:17 in CheckMask, called from StartNativeThread (a fault signal is blocked in the guest thread's host mask) Passed
With this PR Passed (5 of 5 runs) Passed

AI-assisted: yes (Claude Code applied the PR, ran the tests and wrote this report).

@jotitadev jotitadev 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.

Tested head fc9716dad merged with current main on macOS. The fix works, but after the merge the PR's test makes public_headers fail.

Machine: Apple M2 Max (32 GB), macOS 27.2 (26B5112a), Apple clang 21.0.0, x86_64 Release under Rosetta 2 (-DCMAKE_OSX_ARCHITECTURES=x86_64, Ninja, BUILD_TESTING=ON, Python 3.9 from Xcode).
Base: main b986c11a34 does not build the libraries on macOS by itself, so the tree is that main plus the open macOS build PRs (#10366, #7631, #10659, #10660, #11249, #11358, #10669, #10552, #7804, #10676, #11447, #11437) and #11554, plus #11705 (applied locally). Only this PR is toggled, with git merge --no-commit on top.

GuestPthreadHostFaultMask.cpp declares scePthreadCreate and scePthreadJoin itself. Since 35cf8d5 (canonical headers, 2026-10-24, after this branch's last main merge 137a8fab89), core/libs/prx/libkernel/include/Api.hpp declares both, and the public_headers audit allows only one declaration:

scePthreadCreate: expected exactly one header declaration; found 2: core/libs/prx/libkernel/include/Api.hpp:559, core/libs/tests/GuestPthreadHostFaultMask.cpp:7
scePthreadJoin: expected exactly one header declaration; found 2: core/libs/prx/libkernel/include/Api.hpp:560, core/libs/tests/GuestPthreadHostFaultMask.cpp:8

The audit reads the source tree, so it should fail on every host, not only macOS. #9263 hit the same thing after it merged, and main fixed it in 039408c. The same change works here:

+#include "prx/libkernel/include/Api.hpp"
 #include "SceTypes.hpp"
 #include <csignal>
 #include <cstdlib>
 #include <pthread.h>
 
-extern "C" {
-int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name);
-int APS5_VABI scePthreadJoin(Pthread thread, void** retval);
-}

With that change, public_headers and the 64 public_headers_compile_* batches pass (65/65), and guest_pthread_host_fault_mask passes 3/3. I ran this in the tree above with this PR, #10876 and #9263 (plus 039408c) merged together, and in that tree the non-GPU suite (1374 tests) has no new failures against the base apart from the 65 public_headers* tests, which pass with the change. The merge itself is clean.

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

The public header audit now finds duplicate declarations when this test redeclares the pthread APIs. Include the canonical libkernel header so the test follows the current API and public_headers remains valid.
Update the host-fault signal change to current main while preserving the Windows pending-exception delivery path and macOS unblocked fault signals.
@ycarogabriel-lgtm
ycarogabriel-lgtm marked this pull request as ready for review November 1, 2026 17:15
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 1, 2026
@oneandonlydean oneandonlydean self-assigned this Nov 2, 2026
@oneandonlydean
oneandonlydean merged commit 4b1076c into boykopovar:main Nov 2, 2026
12 of 13 checks passed
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.

6 participants