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:
parent
313de1c5a3
commit
b279848c12
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue