From 2b4cb67755301cc29c11d6420d6ea3d4c6cf12e2 Mon Sep 17 00:00:00 2001 From: MarcZX10 <67475268+MarcZX10@users.noreply.github.com> Date: Sun, 25 Oct 2026 02:43:25 +0000 Subject: [PATCH] test(libkernel): query a detached thread after its handle was released --- core/libs/prx/libkernel/CMakeLists.txt | 10 +++ .../tests/GuestPthreadHandleAfterExit.cpp | 71 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 core/libs/tests/GuestPthreadHandleAfterExit.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 4d6c818cfb..ea642fc837 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -738,6 +738,16 @@ if(BUILD_TESTING) set_tests_properties(guest_pthread_join_detach PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_pthread_handle_after_exit_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadHandleAfterExit.cpp) + target_include_directories(guest_pthread_handle_after_exit_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_pthread_handle_after_exit_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_pthread_handle_after_exit_tests) + add_test(NAME guest_pthread_handle_after_exit COMMAND guest_pthread_handle_after_exit_tests) + set_tests_properties(guest_pthread_handle_after_exit PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_pthread_handle_after_exit PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_pthread_attr_errors_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadAttrErrors.cpp) target_include_directories(guest_pthread_attr_errors_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_pthread_attr_errors_tests PRIVATE libkernel libc) diff --git a/core/libs/tests/GuestPthreadHandleAfterExit.cpp b/core/libs/tests/GuestPthreadHandleAfterExit.cpp new file mode 100644 index 0000000000..411680441f --- /dev/null +++ b/core/libs/tests/GuestPthreadHandleAfterExit.cpp @@ -0,0 +1,71 @@ +#include "prx/libkernel/include/Api.hpp" +#include "prx/libkernel/Pthread/include/Pthread.hpp" +#include "prx/libkernel/KernelErrors.hpp" +#include +#include +#include +#include + +static constexpr int SCE_OK = 0; + +static void Require(bool condition) { if (!condition) std::abort(); } + +struct Worker { + std::atomic started{false}; + std::atomic release{false}; +}; + +static void* APS5_VABI Wait(void* opaque) { + auto* worker = static_cast(opaque); + worker->started.store(true); + while (!worker->release.load()) std::this_thread::yield(); + return nullptr; +} + +static void RequireAlive(Pthread thread) { + char name[32]; + int priority = 0; + KernelCpumask mask = 0; + Require(scePthreadGetname(thread, name) == SCE_OK); + Require(scePthreadGetprio(thread, &priority) == SCE_OK); + Require(scePthreadGetaffinity(thread, &mask) == SCE_OK && mask != 0); + Require(scePthreadSetaffinity(thread, mask) == SCE_OK); + Require(scePthreadRename(thread, "renamed") == SCE_OK); +} + +static void RequireEnded(Pthread thread) { + char name[32]; + int priority = 0; + KernelCpumask mask = 0; + Require(scePthreadGetname(thread, name) == SCE_KERNEL_ERROR_ESRCH); + Require(scePthreadGetprio(thread, &priority) == SCE_KERNEL_ERROR_ESRCH); + Require(scePthreadGetaffinity(thread, &mask) == SCE_KERNEL_ERROR_ESRCH); + Require(scePthreadSetaffinity(thread, 1) == SCE_KERNEL_ERROR_ESRCH); + Require(scePthreadRename(thread, "renamed") == SCE_KERNEL_ERROR_ESRCH); +} + +static void WaitUntilReleased(Pthread thread) { + const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); + int priority = 0; + while (scePthreadGetprio(thread, &priority) != SCE_KERNEL_ERROR_ESRCH) { + Require(std::chrono::steady_clock::now() < deadline); + std::this_thread::yield(); + } +} + +int main() { + RequireAlive(scePthreadSelf()); + + for (int round = 0; round < 20; ++round) { + Worker detached; + Pthread detachedThread = nullptr; + Require(scePthreadCreate(&detachedThread, nullptr, Wait, &detached, "detached") == SCE_OK); + while (!detached.started.load()) std::this_thread::yield(); + RequireAlive(detachedThread); + Require(scePthreadDetach(detachedThread) == SCE_OK); + detached.release.store(true); + WaitUntilReleased(detachedThread); + RequireEnded(detachedThread); + } + return 0; +}