diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 907294a56..cbcba3520 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -115,6 +115,16 @@ if(BUILD_TESTING) set_tests_properties(guest_once PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_pthread_barrier_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadBarrier.cpp) + target_include_directories(guest_pthread_barrier_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_pthread_barrier_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_pthread_barrier_tests) + add_test(NAME guest_pthread_barrier COMMAND guest_pthread_barrier_tests) + set_tests_properties(guest_pthread_barrier PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_pthread_barrier PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_thread_self_tests ${LIBS_INCLUDE_DIR}/tests/GuestThreadSelf.cpp) target_include_directories(guest_thread_self_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_thread_self_tests PRIVATE libkernel libc) diff --git a/core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp b/core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp index 723fd1267..1c363ef32 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include struct PthreadBarrierPrivate { std::mutex mutex; @@ -13,11 +15,54 @@ struct PthreadBarrierPrivate { std::uint64_t generation = 0; }; +namespace { + +constexpr int PROCESS_PRIVATE = 0; +constexpr int PROCESS_SHARED = 1; + +} + +struct PthreadBarrierattrPrivate { + int pshared = PROCESS_PRIVATE; +}; + extern "C" { -int APS5_VABI pthread_barrier_init_nid_postfix(PthreadBarrierPrivate** barrier, const void* attr, unsigned count) { +int APS5_VABI pthread_barrierattr_init_nid_postfix(PthreadBarrierattrPrivate** attr) { + if (!attr) return 22; + try { + *attr = new PthreadBarrierattrPrivate; + return 0; + } catch (const std::bad_alloc&) { + return 12; + } +} + +int APS5_VABI pthread_barrierattr_destroy_nid_postfix(PthreadBarrierattrPrivate** attr) { + if (!attr || !*attr) return 22; + delete *attr; + *attr = nullptr; + return 0; +} + +int APS5_VABI pthread_barrierattr_getpshared_nid_postfix(PthreadBarrierattrPrivate* const* attr, int* pshared) { + if (!attr || !*attr) return 22; + if (!pshared) APS5_INVALID_ARG_EX; + *pshared = (*attr)->pshared; + return 0; +} + +int APS5_VABI pthread_barrierattr_setpshared_nid_postfix(PthreadBarrierattrPrivate** attr, int pshared) { + if (!attr || !*attr) return 22; + if (pshared == PROCESS_SHARED) NotImplemented_nid_no_patch("pthread_barrierattr_setpshared: PTHREAD_PROCESS_SHARED"); + if (pshared != PROCESS_PRIVATE) return 22; + (*attr)->pshared = pshared; + return 0; +} + +int APS5_VABI pthread_barrier_init_nid_postfix(PthreadBarrierPrivate** barrier, PthreadBarrierattrPrivate* const* attr, unsigned count) { + (void)attr; if (!barrier || count == 0) return 22; - if (attr) NotImplemented_nid_no_patch("pthread_barrier_init: non-default attributes"); try { auto* value = new PthreadBarrierPrivate; value->count = count; diff --git a/core/libs/tests/GuestPthreadBarrier.cpp b/core/libs/tests/GuestPthreadBarrier.cpp new file mode 100644 index 000000000..d7286b7ee --- /dev/null +++ b/core/libs/tests/GuestPthreadBarrier.cpp @@ -0,0 +1,109 @@ +#include "SceTypes.hpp" +#include +#include +#include +#include + +struct GuestBarrier; +struct GuestBarrierattr; + +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); +int APS5_VABI pthread_barrierattr_init_nid_postfix(GuestBarrierattr** attr); +int APS5_VABI pthread_barrierattr_destroy_nid_postfix(GuestBarrierattr** attr); +int APS5_VABI pthread_barrierattr_getpshared_nid_postfix(GuestBarrierattr* const* attr, int* pshared); +int APS5_VABI pthread_barrierattr_setpshared_nid_postfix(GuestBarrierattr** attr, int pshared); +int APS5_VABI pthread_barrier_init_nid_postfix(GuestBarrier** barrier, GuestBarrierattr* const* attr, unsigned count); +int APS5_VABI pthread_barrier_wait_nid_postfix(GuestBarrier** barrier); +int APS5_VABI pthread_barrier_destroy_nid_postfix(GuestBarrier** barrier); +} + +static constexpr int GUEST_EINVAL = 22; +static constexpr int PROCESS_PRIVATE = 0; +static constexpr int PROCESS_SHARED = 1; +static constexpr int BARRIER_SERIAL_THREAD = -1; +static constexpr unsigned WAITERS = 4; + +static void Require(bool value) { if (!value) std::abort(); } + +struct Context { + GuestBarrier* barrier = nullptr; + std::atomic serial{0}; + std::atomic released{0}; +}; + +static void* APS5_VABI Waiter(void* arg) { + auto& context = *static_cast(arg); + const int result = pthread_barrier_wait_nid_postfix(&context.barrier); + if (result == BARRIER_SERIAL_THREAD) ++context.serial; + else if (result == 0) ++context.released; + else std::abort(); + return nullptr; +} + +static void RunWaiters(Context& context) { + std::array threads{}; + for (auto& thread : threads) Require(scePthreadCreate(&thread, nullptr, Waiter, &context, nullptr) == 0); + for (auto& thread : threads) Require(scePthreadJoin(thread, nullptr) == 0); + Require(context.serial == 1 && context.released == static_cast(WAITERS) - 1); +} + +int main() { + GuestBarrierattr* missing = nullptr; + int pshared = -1; + Require(pthread_barrierattr_init_nid_postfix(nullptr) == GUEST_EINVAL); + Require(pthread_barrierattr_destroy_nid_postfix(nullptr) == GUEST_EINVAL); + Require(pthread_barrierattr_destroy_nid_postfix(&missing) == GUEST_EINVAL); + Require(pthread_barrierattr_getpshared_nid_postfix(nullptr, &pshared) == GUEST_EINVAL); + Require(pthread_barrierattr_getpshared_nid_postfix(&missing, &pshared) == GUEST_EINVAL); + Require(pthread_barrierattr_setpshared_nid_postfix(nullptr, PROCESS_PRIVATE) == GUEST_EINVAL); + Require(pthread_barrierattr_setpshared_nid_postfix(&missing, PROCESS_PRIVATE) == GUEST_EINVAL); + Require(pshared == -1); + + GuestBarrierattr* attr = nullptr; + Require(pthread_barrierattr_init_nid_postfix(&attr) == 0 && attr != nullptr); + Require(pthread_barrierattr_getpshared_nid_postfix(&attr, &pshared) == 0 && pshared == PROCESS_PRIVATE); + Require(pthread_barrierattr_setpshared_nid_postfix(&attr, 2) == GUEST_EINVAL); + Require(pthread_barrierattr_setpshared_nid_postfix(&attr, -1) == GUEST_EINVAL); + bool rejected = false; + try { pthread_barrierattr_setpshared_nid_postfix(&attr, PROCESS_SHARED); } + catch (const std::runtime_error&) { rejected = true; } + Require(rejected); + Require(pthread_barrierattr_setpshared_nid_postfix(&attr, PROCESS_PRIVATE) == 0); + pshared = -1; + Require(pthread_barrierattr_getpshared_nid_postfix(&attr, &pshared) == 0 && pshared == PROCESS_PRIVATE); + rejected = false; + try { pthread_barrierattr_getpshared_nid_postfix(&attr, nullptr); } + catch (const std::invalid_argument&) { rejected = true; } + Require(rejected); + + GuestBarrier* invalid = nullptr; + Require(pthread_barrier_init_nid_postfix(nullptr, &attr, 1) == GUEST_EINVAL); + Require(pthread_barrier_init_nid_postfix(&invalid, &attr, 0) == GUEST_EINVAL && invalid == nullptr); + + Context withAttr; + Require(pthread_barrier_init_nid_postfix(&withAttr.barrier, &attr, WAITERS) == 0 && withAttr.barrier != nullptr); + Require(pthread_barrierattr_destroy_nid_postfix(&attr) == 0 && attr == nullptr); + Require(pthread_barrierattr_destroy_nid_postfix(&attr) == GUEST_EINVAL); + RunWaiters(withAttr); + Require(pthread_barrier_destroy_nid_postfix(&withAttr.barrier) == 0 && withAttr.barrier == nullptr); + + Context withEmptyAttr; + Require(pthread_barrier_init_nid_postfix(&withEmptyAttr.barrier, &missing, WAITERS) == 0); + RunWaiters(withEmptyAttr); + Require(pthread_barrier_destroy_nid_postfix(&withEmptyAttr.barrier) == 0); + + Context withoutAttr; + Require(pthread_barrier_init_nid_postfix(&withoutAttr.barrier, nullptr, WAITERS) == 0); + RunWaiters(withoutAttr); + Require(pthread_barrier_destroy_nid_postfix(&withoutAttr.barrier) == 0); + + GuestBarrier* single = nullptr; + Require(pthread_barrierattr_init_nid_postfix(&attr) == 0); + Require(pthread_barrier_init_nid_postfix(&single, &attr, 1) == 0); + Require(pthread_barrier_wait_nid_postfix(&single) == BARRIER_SERIAL_THREAD); + Require(pthread_barrier_wait_nid_postfix(&single) == BARRIER_SERIAL_THREAD); + Require(pthread_barrier_destroy_nid_postfix(&single) == 0); + Require(pthread_barrierattr_destroy_nid_postfix(&attr) == 0); +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 48ea116f5..f59ee301b 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -32,6 +32,7 @@ Throughout the project, every function at every stage either **does exactly what - [sceLibcInternalBacktraceForGame](../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature - [sceLibcInternalHeapErrorReportForGame](../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - unknown signature - [__progname](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - unknown data export +- [pthread_barrierattr_setpshared](../../core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp) (libkernel) - PTHREAD_PROCESS_SHARED throws: FreeBSD 9.0 libthr rejects it with EINVAL and 11.0 accepts it, and which one the console follows is unknown - [sceSslClose](../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature - [sceSslGetSerialNumber](../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - unknown signature - [X+4jdIS75P0](../../core/libs/prx/libSceAudioIn/Export.cpp) (libSceAudioIn) - unknown name, signature