mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
Add pthread_spin_init (ZMn3clnAGBA), pthread_spin_destroy (IJIggoPZExk), pthread_spin_lock (pw+70ClLYlY), pthread_spin_trylock (rCTGkBIHfPY) and pthread_spin_unlock (LEfMMCT+SlM). The NIDs were computed with tools/nid_names.py and match aerolib.csv. libkernel has no scePthreadSpin* twin in aerolib, so the calls are implemented directly, like the barrier. Titles: Porpoise PPSA99764 and RetroArch PPSA99169 import pthread_spin_lock. Predicted from relink registry order, not observed: after pthread_cond_reltimedwait_np, Porpoise stops with "FAIL: unresolved ELF import pw+70ClLYlY from libkernel.prx" (registry index 387; RetroArch index 493). No call site is resolved in the registry. Behaviour follows FreeBSD 11.4 libthr thr_pspinlock.c and thr_umtx.h (8c639945a44588f6cfdddfb057bb5aad88735da0). The guest pthread_spinlock_t holds a pointer to an allocated lock. EINVAL for a null or destroyed lock and for a pshared other than PTHREAD_PROCESS_PRIVATE or PTHREAD_PROCESS_SHARED, EBUSY from trylock while any thread holds the lock, EPERM from unlock by a thread that does not hold it. libthr spins forever when the holder locks again; that call throws here. A shared lock is allocated like a private one, since guest code runs in one process. Recorded in TechnicalDebt. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 13ebf7f926fa2ee1c825cb1fcf18451a8781c045)
123 lines
4.8 KiB
C++
123 lines
4.8 KiB
C++
#include "SceTypes.hpp"
|
|
#include <cstdlib>
|
|
#include <stdexcept>
|
|
|
|
struct PthreadSpinlockPrivate;
|
|
using PthreadSpinlock = PthreadSpinlockPrivate*;
|
|
|
|
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_spin_init_nid_postfix(PthreadSpinlock* lock, int pshared);
|
|
int APS5_VABI pthread_spin_destroy_nid_postfix(PthreadSpinlock* lock);
|
|
int APS5_VABI pthread_spin_lock_nid_postfix(PthreadSpinlock* lock);
|
|
int APS5_VABI pthread_spin_trylock_nid_postfix(PthreadSpinlock* lock);
|
|
int APS5_VABI pthread_spin_unlock_nid_postfix(PthreadSpinlock* lock);
|
|
}
|
|
|
|
static constexpr int GUEST_EPERM = 1;
|
|
static constexpr int GUEST_EBUSY = 16;
|
|
static constexpr int GUEST_EINVAL = 22;
|
|
static constexpr int PTHREAD_PROCESS_PRIVATE = 0;
|
|
static constexpr int PTHREAD_PROCESS_SHARED = 1;
|
|
static constexpr int Increments = 200000;
|
|
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
struct Shared {
|
|
PthreadSpinlock lock = nullptr;
|
|
long counter = 0;
|
|
int trylock = -1;
|
|
int unlock = -1;
|
|
};
|
|
|
|
static void* APS5_VABI Contender(void* arg) {
|
|
auto& shared = *static_cast<Shared*>(arg);
|
|
shared.trylock = pthread_spin_trylock_nid_postfix(&shared.lock);
|
|
shared.unlock = pthread_spin_unlock_nid_postfix(&shared.lock);
|
|
return nullptr;
|
|
}
|
|
|
|
static void* APS5_VABI Incrementer(void* arg) {
|
|
auto& shared = *static_cast<Shared*>(arg);
|
|
for (int i = 0; i < Increments; ++i) {
|
|
Require(pthread_spin_lock_nid_postfix(&shared.lock) == 0);
|
|
++shared.counter;
|
|
Require(pthread_spin_unlock_nid_postfix(&shared.lock) == 0);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static void ArgumentErrors() {
|
|
Require(pthread_spin_init_nid_postfix(nullptr, PTHREAD_PROCESS_PRIVATE) == GUEST_EINVAL);
|
|
PthreadSpinlock lock = nullptr;
|
|
Require(pthread_spin_init_nid_postfix(&lock, 2) == GUEST_EINVAL);
|
|
Require(pthread_spin_init_nid_postfix(&lock, -1) == GUEST_EINVAL);
|
|
Require(lock == nullptr);
|
|
Require(pthread_spin_lock_nid_postfix(nullptr) == GUEST_EINVAL);
|
|
Require(pthread_spin_trylock_nid_postfix(nullptr) == GUEST_EINVAL);
|
|
Require(pthread_spin_unlock_nid_postfix(nullptr) == GUEST_EINVAL);
|
|
Require(pthread_spin_destroy_nid_postfix(nullptr) == GUEST_EINVAL);
|
|
Require(pthread_spin_lock_nid_postfix(&lock) == GUEST_EINVAL);
|
|
Require(pthread_spin_trylock_nid_postfix(&lock) == GUEST_EINVAL);
|
|
Require(pthread_spin_unlock_nid_postfix(&lock) == GUEST_EINVAL);
|
|
Require(pthread_spin_destroy_nid_postfix(&lock) == GUEST_EINVAL);
|
|
}
|
|
|
|
static void SingleThread(int pshared) {
|
|
PthreadSpinlock lock = nullptr;
|
|
Require(pthread_spin_init_nid_postfix(&lock, pshared) == 0);
|
|
Require(lock != nullptr);
|
|
Require(pthread_spin_unlock_nid_postfix(&lock) == GUEST_EPERM);
|
|
Require(pthread_spin_lock_nid_postfix(&lock) == 0);
|
|
Require(pthread_spin_trylock_nid_postfix(&lock) == GUEST_EBUSY);
|
|
bool threw = false;
|
|
try {
|
|
pthread_spin_lock_nid_postfix(&lock);
|
|
} catch (const std::runtime_error&) {
|
|
threw = true;
|
|
}
|
|
Require(threw);
|
|
Require(pthread_spin_unlock_nid_postfix(&lock) == 0);
|
|
Require(pthread_spin_unlock_nid_postfix(&lock) == GUEST_EPERM);
|
|
Require(pthread_spin_trylock_nid_postfix(&lock) == 0);
|
|
Require(pthread_spin_unlock_nid_postfix(&lock) == 0);
|
|
Require(pthread_spin_destroy_nid_postfix(&lock) == 0);
|
|
Require(lock == nullptr);
|
|
Require(pthread_spin_destroy_nid_postfix(&lock) == GUEST_EINVAL);
|
|
}
|
|
|
|
static void HeldByAnotherThread() {
|
|
Shared shared;
|
|
Require(pthread_spin_init_nid_postfix(&shared.lock, PTHREAD_PROCESS_PRIVATE) == 0);
|
|
Require(pthread_spin_lock_nid_postfix(&shared.lock) == 0);
|
|
Pthread thread = nullptr;
|
|
Require(scePthreadCreate(&thread, nullptr, Contender, &shared, nullptr) == 0);
|
|
Require(scePthreadJoin(thread, nullptr) == 0);
|
|
Require(shared.trylock == GUEST_EBUSY);
|
|
Require(shared.unlock == GUEST_EPERM);
|
|
Require(pthread_spin_unlock_nid_postfix(&shared.lock) == 0);
|
|
Require(pthread_spin_destroy_nid_postfix(&shared.lock) == 0);
|
|
}
|
|
|
|
static void MutualExclusion() {
|
|
Shared shared;
|
|
Require(pthread_spin_init_nid_postfix(&shared.lock, PTHREAD_PROCESS_PRIVATE) == 0);
|
|
Pthread first = nullptr;
|
|
Pthread second = nullptr;
|
|
Require(scePthreadCreate(&first, nullptr, Incrementer, &shared, nullptr) == 0);
|
|
Require(scePthreadCreate(&second, nullptr, Incrementer, &shared, nullptr) == 0);
|
|
Require(scePthreadJoin(first, nullptr) == 0);
|
|
Require(scePthreadJoin(second, nullptr) == 0);
|
|
Require(shared.counter == 2L * Increments);
|
|
Require(pthread_spin_destroy_nid_postfix(&shared.lock) == 0);
|
|
}
|
|
|
|
int main() {
|
|
ArgumentErrors();
|
|
SingleThread(PTHREAD_PROCESS_PRIVATE);
|
|
SingleThread(PTHREAD_PROCESS_SHARED);
|
|
HeldByAnotherThread();
|
|
MutualExclusion();
|
|
}
|