Skip to content

fix(libScePad): make the open handle tables safe for concurrent opens - #12232

Merged
oneandonlydean merged 3 commits into
boykopovar:mainfrom
MarcZX10:fix/pad-guard-open-handle-tables
Nov 2, 2026
Merged

oneandonlydean merged 3 commits into
boykopovar:mainfrom
MarcZX10:fix/pad-guard-open-handle-tables

Conversation

@MarcZX10

Copy link
Copy Markdown
Contributor

What

g_opened, g_everOpened, g_remoteOpened and g_specialUsers in libScePad/Export.cpp are plain globals. scePadRead, scePadGetHandle and KnownHandle read them on one guest thread while scePadOpen and scePadClose write them on another, which is a data race. The special handle slot was also claimed with a check followed by a store, so two threads opening special pads for different users could pick the same slot and one user was silently lost.

The tables are atomics now and a special slot is claimed with a compare and exchange, so each concurrent open gets its own slot.

Two commits: the fix, then the test (CheckConcurrentSpecialOpens in GuestPad.cpp: 200 rounds in which eight threads are released together and open special pads for eight users; they must get eight distinct handles, which are then closed).

Tested

Linux x86-64, a throwaway archive build with the host CPU gate patched. guest_pad passes. The collision needs a particular interleaving, so the new test can pass on the old code by luck; it is deterministic with the compare and exchange. Not run under ThreadSanitizer. tools/check_conventions.py --base upstream/main and git diff --check pass.

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 under Silent stubs in their library's technical debt file
  • 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
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 28, 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 5038 core/libs/prx/libScePad/Export.cpp
PR 8260 core/libs/prx/libScePad/Export.cpp
core/libs/tests/GuestPad.cpp
PR 10112 core/libs/prx/libScePad/Export.cpp
PR 12308 core/libs/prx/libScePad/Export.cpp
PR 12309 core/libs/prx/libScePad/Export.cpp
PR 12310 core/libs/prx/libScePad/Export.cpp
PR 12311 core/libs/prx/libScePad/Export.cpp
PR 12312 core/libs/prx/libScePad/Export.cpp

@JJenkx

JJenkx commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

A Windows run, since the description lists none. Head 4345f233e2 was merged onto main 82d5f01f28 with no conflicts. Setup: Windows 11 Enterprise 10.0.22621, WinLibs GCC 15.2.0 UCRT r7, Ninja, Release, BUILD_TESTING=ON.

libs builds, and guest_pad, including this PR's GuestPad.cpp checks, passes in each of 3 runs.

AI-assisted: yes (Claude Code).

@B-Howell

Copy link
Copy Markdown
Contributor

Results in the project's real build on Windows and Linux, with controls. The description reports a throwaway archive build on Linux only.

Head 4345f233e2d41b3c6d947d56a696b98a7e11e7ff, merged the way CI does, alone in the tree.

Windows: Windows 11 Pro 26300, AMD Ryzen 7 7800X3D, WinLibs GCC 15.2.0 r7, Ninja Release, BUILD_TESTING=ON, libs rebuilt, on main adbcdcdd58d767ce47cd711232560a97efcaa732 (clean).

  • ctest -R '^guest_pad$' --repeat until-fail:10: passes all 10 runs (94.7 s total).
  • Control: libScePad/Export.cpp restored from main, test kept: aborts (0xc0000409, FATAL: abort() (SIGABRT)) on the first run.

Linux: Ubuntu 22.04 under WSL2, GCC 11.4.0, Ninja Release, on main ee5e24729cdf8c457221a673a088a02321f206f7 (clean).

  • The same command passes all 10 runs (50.2 s total).
  • The same control aborts (Subprocess aborted) on the first run.

On both hosts the new concurrent-open check failed against the old code on its first run here. So it caught the collision deterministically on these machines, not only by luck.

Not tested: ThreadSanitizer, macOS, any title.

AI-assisted (Claude Code).

@maskym

maskym commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Windows result for head 4345f233e2, merged into main 87b810a1db (the PR's Tested section names no Windows run).

Host: Windows 11 26300, Intel i7-12700K, WinLibs GCC 15.2.0 (x86_64-ucrt-posix-seh, r6), Release, BUILD_TESTING=ON, Ninja. Build clean.

The tree also carried the open PRs #12290, #12288, #12285, #12269, #12265, #12206, #12201, #12199, #12197, #12165, #12146, all merged cleanly, so the run is a joint one; the tests of this PR are guest_pad (4.84 s), guest_pad_vibration_mode (0.05 s), all passed (every test registered from the tree's changed test files: 38/38). Not run on a PS5.

AI-assisted: yes (Claude Code), the runs and the numbers above are from this machine.

g_opened, g_everOpened, g_remoteOpened and g_specialUsers are plain globals
that scePadRead, scePadGetHandle and KnownHandle read on one guest thread
while scePadOpen and scePadClose write them on another, which is a data race.
The special handle slot was also claimed with a check followed by a store, so
two threads opening special pads for different users could pick the same slot
and one user was silently lost.

The tables are atomics now and a special slot is claimed with a compare and
exchange, so each concurrent open gets its own slot.
Each of 200 rounds releases eight threads together; they have to receive eight
distinct handles, which are then closed.
@MarcZX10
MarcZX10 force-pushed the fix/pad-guard-open-handle-tables branch from 4345f23 to 02c1c76 Compare October 28, 2026 22:33
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 28, 2026
# Conflicts:
#	core/libs/tests/GuestPad.cpp
@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head c4b304d merged onto main 291e02d · tested 2026-11-01 14:43 UTC · 9 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 866 passed · 4 failed (0 new, 3 also fail on main, 1 unstable on main) · 3 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 873 of 3,155 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (426 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s, ⚠️ agc_driver_lazy_resolved_abi 33.9 s, ⚠️ public_headers 31.2 s, ⚠️ agc_driver_depth_clear_hle 31.2 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 3 of the last 4 main runs
Failing on main too (3, not counted against this PR)
agc_driver_image_table
agc_driver_native_depth_bda_clear
agc_driver_native_multisample_depth
Validation IDs already present on main (5 IDs in 364 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (364 tests): agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, agc_driver_bda_span_reads, agc_driver_bindless_images, and 356 more
VUID-vkCmdCopyImage-srcImage-01548 (6 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (6 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (1 test): agc_driver_prepared_exports
VUID-VkShaderModuleCreateInfo-pCode-08737 (1 test): agc_driver_image_sample_derivatives
Skipped (3)
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
Reproduce
git fetch origin c4b304d1bab86d8e3dfa34593b3dbdd6f23fe790 291e02d4877784d03d346962af51713b9df63b91
git checkout 291e02d4877784d03d346962af51713b9df63b91 && git merge --no-ff c4b304d1bab86d8e3dfa34593b3dbdd6f23fe790
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 1, 2026
@oneandonlydean
oneandonlydean merged commit 0ac3db1 into boykopovar:main Nov 2, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants