From 14f5572fca3596674afd65edbcc45a5b8a523f46 Mon Sep 17 00:00:00 2001 From: hamb3r Date: Sat, 10 Oct 2026 03:06:48 +0300 Subject: [PATCH] test(libkernel): cover exception delivery during mutex wait --- core/libs/prx/libkernel/CMakeLists.txt | 7 +++ core/libs/tests/GuestMutexRaiseException.cpp | 52 ++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 core/libs/tests/GuestMutexRaiseException.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 2ceb0cfa1..47d77a0b7 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -659,5 +659,12 @@ if(BUILD_TESTING) if(WIN32) set_tests_properties(guest_sandbox_random_word PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") + add_test_executable(guest_mutex_raise_exception_tests ${LIBS_INCLUDE_DIR}/tests/GuestMutexRaiseException.cpp) + target_include_directories(guest_mutex_raise_exception_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_mutex_raise_exception_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_mutex_raise_exception_tests) + add_test(NAME guest_mutex_raise_exception COMMAND guest_mutex_raise_exception_tests) + set_tests_properties(guest_mutex_raise_exception PROPERTIES TIMEOUT 20 ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") endif() endif() diff --git a/core/libs/tests/GuestMutexRaiseException.cpp b/core/libs/tests/GuestMutexRaiseException.cpp new file mode 100644 index 000000000..ecdd471b4 --- /dev/null +++ b/core/libs/tests/GuestMutexRaiseException.cpp @@ -0,0 +1,52 @@ +#include "SceTypes.hpp" +#include +#include +#include +#include +#include + +extern "C" { +int APS5_VABI scePthreadCreate(Pthread*, const PthreadAttr*, PthreadEntry, void*, const char*); +int APS5_VABI scePthreadJoin(Pthread, void**); +int APS5_VABI scePthreadMutexInit(PthreadMutex*, const PthreadMutexattr*, const char*); +int APS5_VABI scePthreadMutexDestroy(PthreadMutex*); +int APS5_VABI scePthreadMutexLock(PthreadMutex*); +int APS5_VABI scePthreadMutexUnlock(PthreadMutex*); +int APS5_VABI sceKernelInstallExceptionHandler(int, void*); +int APS5_VABI sceKernelRemoveExceptionHandler(int); +int APS5_VABI sceKernelRaiseException(Pthread, int); +} + +static constexpr int SigUsr1 = 30; +static void Require(bool value) { if (!value) std::abort(); } +static std::atomic deliveries{0}; +static void APS5_VABI Handler(int, void*) { deliveries.fetch_add(1); } + +struct Context { PthreadMutex mutex = nullptr; std::atomic started{false}; std::atomic acquired{false}; }; +static void* APS5_VABI Worker(void* opaque) { + auto& context = *static_cast(opaque); + context.started.store(true); + Require(scePthreadMutexLock(&context.mutex) == 0); + context.acquired.store(true); + Require(scePthreadMutexUnlock(&context.mutex) == 0); + return nullptr; +} + +int main() { + Require(sceKernelInstallExceptionHandler(SigUsr1, reinterpret_cast(&Handler)) == 0); + Context context; + Require(scePthreadMutexInit(&context.mutex, nullptr, nullptr) == 0); + Require(scePthreadMutexLock(&context.mutex) == 0); + Pthread worker = nullptr; + Require(scePthreadCreate(&worker, nullptr, Worker, &context, "mutex wait") == 0); + while (!context.started.load()) std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + Require(sceKernelRaiseException(worker, SigUsr1) == 0); + for (int i = 0; i < 5000 && deliveries.load() == 0; ++i) std::this_thread::sleep_for(std::chrono::milliseconds(1)); + Require(deliveries.load() == 1); + Require(scePthreadMutexUnlock(&context.mutex) == 0); + Require(scePthreadJoin(worker, nullptr) == 0); + Require(context.acquired.load()); + Require(scePthreadMutexDestroy(&context.mutex) == 0); + Require(sceKernelRemoveExceptionHandler(SigUsr1) == 0); +} \ No newline at end of file