Files
AnyPS5/core/libs/tests/GuestPthreadMutexErrors.cpp
T
MaxandClaude Opus 5.5 55ff5484d6 fix(libkernel): time out timed locks of a lock the caller already holds
scePthreadMutexTimedlock on a mutex the caller holds, and
scePthreadRwlockTimedrdlock and scePthreadRwlockTimedwrlock on a rwlock
it holds for writing, returned EDEADLK at once. The console returns
ETIMEDOUT, as the prosper console oracle measured. They now sleep until
the timeout and return ETIMEDOUT, as FreeBSD libthr does. The POSIX
timed locks call these and change with them.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 02:15:31 +02:00

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_OK);
Require(pthread_mutex_setyieldloops_np_nid_postfix(&mutex, 1) == 0);
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);
}