mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-28 12:05:06 +00:00
On PS5 firmware 13.60 pthread_mutex_setyieldloops_np and scePthreadMutexSetyieldloops return EINVAL on a destroyed mutex, as the other spin and yield loop calls already did here. The default counts (2000 spins for an adaptive mutex, 0 otherwise, 0 yields) match. Co-Authored-By: Claude Opus 5.5 <[email protected]>
146 lines
7.2 KiB
C++
146 lines
7.2 KiB
C++
#include "prx/libkernel/KernelErrors.hpp"
|
|
#include "prx/libkernel/include/Api.hpp"
|
|
#include "prx/libkernel/Pthread/include/Mutex.hpp"
|
|
#include "SceTypes.hpp"
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <initializer_list>
|
|
#include <stdexcept>
|
|
|
|
static constexpr int SCE_OK = 0;
|
|
|
|
static constexpr int POSIX_EDEADLK = 11;
|
|
static constexpr int POSIX_EINVAL = 22;
|
|
static constexpr int POSIX_ETIMEDOUT = 60;
|
|
static const PthreadMutex AdaptiveInitializer = reinterpret_cast<PthreadMutex>(std::uintptr_t{1});
|
|
static const PthreadMutex DestroyedMutex = reinterpret_cast<PthreadMutex>(std::uintptr_t{2});
|
|
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
template <typename Call>
|
|
static bool Throws(Call call) {
|
|
try {
|
|
call();
|
|
} catch (const std::exception&) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct OtherThread {
|
|
PthreadMutex* mutex;
|
|
int isowned = -1;
|
|
int reltimedlock = -1;
|
|
};
|
|
|
|
static void* APS5_VABI CheckFromOtherThread(void* arg) {
|
|
auto& other = *static_cast<OtherThread*>(arg);
|
|
other.isowned = pthread_mutex_isowned_np_nid_postfix(other.mutex);
|
|
other.reltimedlock = pthread_mutex_reltimedlock_np_nid_postfix(other.mutex, 1000);
|
|
return nullptr;
|
|
}
|
|
|
|
static void CheckOwnershipAndRelativeLock() {
|
|
PthreadMutex mutex = nullptr;
|
|
Require(pthread_mutex_isowned_np_nid_postfix(&mutex) == 0 && mutex == nullptr);
|
|
PthreadMutex adaptive = AdaptiveInitializer;
|
|
Require(pthread_mutex_isowned_np_nid_postfix(&adaptive) == 0 && adaptive == AdaptiveInitializer);
|
|
Require(pthread_mutex_reltimedlock_np_nid_postfix(&mutex, 1000) == 0 && mutex != nullptr);
|
|
Require(pthread_mutex_isowned_np_nid_postfix(&mutex) == 1);
|
|
Require(pthread_mutex_reltimedlock_np_nid_postfix(&mutex, 1000) == POSIX_ETIMEDOUT);
|
|
OtherThread other{&mutex};
|
|
Pthread thread = nullptr;
|
|
Require(scePthreadCreate(&thread, nullptr, CheckFromOtherThread, &other, nullptr) == SCE_OK);
|
|
Require(scePthreadJoin(thread, nullptr) == SCE_OK);
|
|
Require(other.isowned == 0 && other.reltimedlock == POSIX_ETIMEDOUT);
|
|
Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0);
|
|
Require(pthread_mutex_isowned_np_nid_postfix(&mutex) == 0);
|
|
Require(pthread_mutex_reltimedlock_np_nid_postfix(&mutex, 0) == 0);
|
|
Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0);
|
|
Require(pthread_mutex_destroy_nid_postfix(&mutex) == 0);
|
|
Require(mutex == DestroyedMutex);
|
|
Require(pthread_mutex_isowned_np_nid_postfix(&mutex) == 0);
|
|
Require(pthread_mutex_reltimedlock_np_nid_postfix(&mutex, 1000) == POSIX_EINVAL);
|
|
Require(Throws([] { pthread_mutex_isowned_np_nid_postfix(nullptr); }));
|
|
}
|
|
|
|
static void CheckLoops(PthreadMutexattr* attr) {
|
|
PthreadMutex mutex = nullptr;
|
|
int count = -1;
|
|
Require(pthread_mutex_getspinloops_np_nid_postfix(&mutex, &count) == 0 && count == 0 && mutex != nullptr);
|
|
Require(pthread_mutex_getyieldloops_np_nid_postfix(&mutex, &count) == 0 && count == 0);
|
|
Require(pthread_mutex_destroy_nid_postfix(&mutex) == 0);
|
|
|
|
PthreadMutex adaptive = AdaptiveInitializer;
|
|
Require(scePthreadMutexGetspinloops(&adaptive, &count) == SCE_OK && count == 2000 && adaptive != AdaptiveInitializer);
|
|
Require(scePthreadMutexGetyieldloops(&adaptive, &count) == SCE_OK && count == 0);
|
|
Require(scePthreadMutexDestroy(&adaptive) == SCE_OK);
|
|
|
|
Require(scePthreadMutexattrSettype(attr, 4) == SCE_OK);
|
|
Require(scePthreadMutexInit(&mutex, attr, "adaptive") == SCE_OK);
|
|
Require(scePthreadMutexGetspinloops(&mutex, &count) == SCE_OK && count == 2000);
|
|
Require(scePthreadMutexSetspinloops(&mutex, 7) == SCE_OK);
|
|
Require(pthread_mutex_getspinloops_np_nid_postfix(&mutex, &count) == 0 && count == 7);
|
|
Require(pthread_mutex_setspinloops_np_nid_postfix(&mutex, -3) == 0);
|
|
Require(scePthreadMutexGetspinloops(&mutex, &count) == SCE_OK && count == -3);
|
|
Require(scePthreadMutexSetyieldloops(&mutex, 9) == SCE_OK);
|
|
Require(pthread_mutex_getyieldloops_np_nid_postfix(&mutex, &count) == 0 && count == 9);
|
|
Require(pthread_mutex_setyieldloops_np_nid_postfix(&mutex, 11) == 0);
|
|
Require(scePthreadMutexGetyieldloops(&mutex, &count) == SCE_OK && count == 11);
|
|
Require(pthread_mutex_lock_nid_postfix(&mutex) == 0);
|
|
Require(pthread_mutex_lock_nid_postfix(&mutex) == POSIX_EDEADLK);
|
|
Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0);
|
|
Require(Throws([&] { scePthreadMutexGetspinloops(&mutex, nullptr); }));
|
|
Require(Throws([&] { scePthreadMutexGetyieldloops(&mutex, nullptr); }));
|
|
Require(scePthreadMutexDestroy(&mutex) == SCE_OK);
|
|
|
|
count = -1;
|
|
Require(scePthreadMutexGetspinloops(&mutex, &count) == SCE_KERNEL_ERROR_EINVAL && count == -1);
|
|
Require(scePthreadMutexSetspinloops(&mutex, 1) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(scePthreadMutexGetyieldloops(&mutex, &count) == SCE_KERNEL_ERROR_EINVAL && count == -1);
|
|
Require(pthread_mutex_getspinloops_np_nid_postfix(&mutex, &count) == POSIX_EINVAL);
|
|
Require(pthread_mutex_setspinloops_np_nid_postfix(&mutex, 1) == POSIX_EINVAL);
|
|
Require(pthread_mutex_getyieldloops_np_nid_postfix(&mutex, &count) == POSIX_EINVAL && count == -1);
|
|
Require(scePthreadMutexSetyieldloops(&mutex, 1) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(pthread_mutex_setyieldloops_np_nid_postfix(&mutex, 1) == POSIX_EINVAL);
|
|
Require(mutex == DestroyedMutex);
|
|
|
|
for (const int type : {1, 2, 3}) {
|
|
Require(scePthreadMutexattrSettype(attr, type) == SCE_OK);
|
|
Require(scePthreadMutexInit(&mutex, attr, nullptr) == SCE_OK);
|
|
Require(scePthreadMutexGetspinloops(&mutex, &count) == SCE_OK && count == 0);
|
|
Require(scePthreadMutexDestroy(&mutex) == SCE_OK);
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
PthreadMutexattr attr = nullptr;
|
|
Require(scePthreadMutexattrInit(&attr) == SCE_OK);
|
|
for (int type : {-1, 0, 5}) Require(scePthreadMutexattrSettype(&attr, type) == SCE_KERNEL_ERROR_EINVAL);
|
|
for (int type : {1, 2, 3, 4}) Require(scePthreadMutexattrSettype(&attr, type) == SCE_OK);
|
|
for (int protocol : {-1, 3}) Require(scePthreadMutexattrSetprotocol(&attr, protocol) == SCE_KERNEL_ERROR_EINVAL);
|
|
for (int protocol : {0, 1, 2}) Require(scePthreadMutexattrSetprotocol(&attr, protocol) == SCE_OK);
|
|
|
|
PthreadMutexattr unset = nullptr;
|
|
Require(scePthreadMutexattrSettype(&unset, 1) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(scePthreadMutexattrSetprotocol(&unset, 0) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(scePthreadMutexattrDestroy(&unset) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(scePthreadMutexattrSettype(nullptr, 1) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(scePthreadMutexattrDestroy(nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
|
|
|
PthreadMutex mutex = nullptr;
|
|
Require(scePthreadMutexInit(&mutex, &unset, "unset") == SCE_OK);
|
|
Require(scePthreadMutexLock(&mutex) == SCE_OK);
|
|
Require(scePthreadMutexDestroy(&mutex) == SCE_KERNEL_ERROR_EBUSY);
|
|
Require(scePthreadMutexUnlock(&mutex) == SCE_OK);
|
|
Require(scePthreadMutexDestroy(&mutex) == SCE_OK);
|
|
Require(mutex == reinterpret_cast<PthreadMutex>(std::uintptr_t{2}));
|
|
Require(scePthreadMutexDestroy(&mutex) == SCE_KERNEL_ERROR_EINVAL);
|
|
|
|
Require(scePthreadMutexInit(&mutex, &attr, "attr") == SCE_OK);
|
|
Require(scePthreadMutexDestroy(&mutex) == SCE_OK);
|
|
CheckLoops(&attr);
|
|
CheckOwnershipAndRelativeLock();
|
|
Require(scePthreadMutexattrDestroy(&attr) == SCE_OK);
|
|
}
|