feat(libkernel): implement the pthread_barrierattr functions

pthread_barrier_init threw for any non-null attributes object, and no
function could create one. Add pthread_barrierattr_init, _destroy,
_getpshared and _setpshared after FreeBSD libthr, and let
pthread_barrier_init take the object.

PTHREAD_PROCESS_SHARED still throws in pthread_barrierattr_setpshared:
FreeBSD 9.0 rejects it and 11.0 accepts it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DotDebian 2026-10-02 17:08:07 +02:00
parent 313de1c5a3
commit b279848c12
4 changed files with 167 additions and 2 deletions

View File

@ -115,6 +115,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_once PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
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:$<TARGET_FILE_DIR:libkernel>")
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)

View File

@ -3,6 +3,8 @@
#include <cstdint>
#include <mutex>
#include <new>
#include <stdexcept>
#include <string>
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;

View File

@ -0,0 +1,109 @@
#include "SceTypes.hpp"
#include <array>
#include <atomic>
#include <cstdlib>
#include <stdexcept>
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<int> serial{0};
std::atomic<int> released{0};
};
static void* APS5_VABI Waiter(void* arg) {
auto& context = *static_cast<Context*>(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<Pthread, WAITERS> 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<int>(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);
}

View File

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