Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void _initializeStatic(PthreadMutex* mutex, const char* funcName) {
PthreadMutex current = slot.load(std::memory_order_acquire);
if (!_isStaticInitializer(current)) return;
auto* created = new PthreadMutexPrivate();
created->_type = reinterpret_cast<std::uintptr_t>(current) == POSIX_ADAPTIVE_MUTEX_INITIALIZER ? MutexType::Adaptive : MutexType::ErrorCheck;
created->SetType(reinterpret_cast<std::uintptr_t>(current) == POSIX_ADAPTIVE_MUTEX_INITIALIZER ? MutexType::Adaptive : MutexType::ErrorCheck);
if (!slot.compare_exchange_strong(current, created, std::memory_order_acq_rel, std::memory_order_acquire))
delete created;
}
Expand Down Expand Up @@ -79,6 +79,31 @@ int APS5_VABI pthread_mutex_unlock_nid_postfix(PthreadMutex* mutex) {
return PosixThread::ToErrno(scePthreadMutexUnlock(mutex));
}

int APS5_VABI pthread_mutex_reltimedlock_np_nid_postfix(PthreadMutex* mutex, KernelUseconds usec) {
_initializeStatic(mutex, __func__);
return PosixThread::ToErrno(scePthreadMutexTimedlock(mutex, usec));
}

int APS5_VABI pthread_mutex_isowned_np_nid_postfix(PthreadMutex* mutex) {
return MutexOperations::IsOwned(mutex) ? 1 : 0;
}

int APS5_VABI pthread_mutex_getspinloops_np_nid_postfix(PthreadMutex* mutex, int* count) {
return PosixThread::ToErrno(scePthreadMutexGetspinloops(mutex, count));
}

int APS5_VABI pthread_mutex_setspinloops_np_nid_postfix(PthreadMutex* mutex, int count) {
return PosixThread::ToErrno(scePthreadMutexSetspinloops(mutex, count));
}

int APS5_VABI pthread_mutex_getyieldloops_np_nid_postfix(PthreadMutex* mutex, int* count) {
return PosixThread::ToErrno(scePthreadMutexGetyieldloops(mutex, count));
}

int APS5_VABI pthread_mutex_setyieldloops_np_nid_postfix(PthreadMutex* mutex, int count) {
return PosixThread::ToErrno(scePthreadMutexSetyieldloops(mutex, count));
}

int APS5_VABI pthread_mutexattr_destroy_nid_postfix(PthreadMutexattr* attr) {
return PosixThread::ToErrno(scePthreadMutexattrDestroy(attr));
}
Expand Down
5 changes: 5 additions & 0 deletions core/libs/prx/libkernel/Pthread/include/Mutex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class MutexOperations {
public:
static int Timedlock(PthreadMutex* mutex, const KernelTimespec* abstime);
static bool IsOwned(const PthreadMutex* mutex);
};

extern "C" {
Expand All @@ -20,6 +21,10 @@ int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexTimedlock(PthreadMutex* mutex, KernelUseconds usec);
int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexGetspinloops(PthreadMutex* mutex, int* count);
int APS5_VABI scePthreadMutexSetspinloops(PthreadMutex* mutex, int count);
int APS5_VABI scePthreadMutexGetyieldloops(PthreadMutex* mutex, int* count);
int APS5_VABI scePthreadMutexSetyieldloops(PthreadMutex* mutex, int count);

}

Expand Down
9 changes: 9 additions & 0 deletions core/libs/prx/libkernel/Pthread/include/Pthread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,23 @@ struct PthreadMutexattrPrivate {
int pshared = 0;
};

constexpr int ADAPTIVE_MUTEX_SPINS = 2000;

struct PthreadMutexPrivate {
std::recursive_timed_mutex _rmtx;
std::timed_mutex _mtx;
MutexType _type;
std::atomic<std::thread::id> _owner;
int _count;
int _spinloops = 0;
int _yieldloops = 0;

PthreadMutexPrivate() : _type(MutexType::ErrorCheck), _owner(std::thread::id{}), _count(0) {}

void SetType(MutexType type) {
_type = type;
_spinloops = type == MutexType::Adaptive ? ADAPTIVE_MUTEX_SPINS : 0;
}
};

struct PthreadRwlockattrPrivate {
Expand Down
47 changes: 45 additions & 2 deletions core/libs/prx/libkernel/Pthread/src/Mutex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ PthreadMutex resolveMutex(PthreadMutex* mutex, bool initialize) {
throw std::runtime_error("Mutex is not initialized");
auto* created = new PthreadMutexPrivate();
if (current == adaptiveInitializer())
created->_type = MutexType::Adaptive;
created->SetType(MutexType::Adaptive);
slot.store(created, std::memory_order_release);
return created;
}
Expand Down Expand Up @@ -80,6 +80,15 @@ int acquireMutex(PthreadMutex mutex, TAcquire acquire, int unavailable, bool try

}

bool MutexOperations::IsOwned(const PthreadMutex* mutex) {
if (!mutex)
throw std::invalid_argument("Mutex pointer is null");
const auto current = std::atomic_ref<PthreadMutex>(*const_cast<PthreadMutex*>(mutex)).load(std::memory_order_acquire);
if (!isInitializedMutex(current))
return false;
return current->_owner.load(std::memory_order_acquire) == std::this_thread::get_id();
}

int MutexOperations::Timedlock(PthreadMutex* mutex, const KernelTimespec* abstime) {
if (!abstime || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
throw std::invalid_argument("Invalid absolute mutex timeout");
Expand Down Expand Up @@ -152,7 +161,7 @@ int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* a
throw std::invalid_argument("Mutex pointer is null");
auto replacement = std::make_unique<PthreadMutexPrivate>();
if (attr && *attr)
replacement->_type = (*attr)->type;
replacement->SetType((*attr)->type);
std::lock_guard lock(initializationMutex);
std::atomic_ref<PthreadMutex>(*mutex).store(replacement.release(), std::memory_order_release);
return 0;
Expand Down Expand Up @@ -224,4 +233,38 @@ int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex) {
return acquireMutex(resolveMutex(mutex, true), [](auto& native) { return native.try_lock(); }, sceBusy, true);
}

int APS5_VABI scePthreadMutexGetspinloops(PthreadMutex* mutex, int* count) {
auto* current = resolveMutex(mutex, true);
if (!current)
return SCE_KERNEL_ERROR_EINVAL;
if (!count)
throw std::invalid_argument("Spin loop count pointer is null");
*count = current->_spinloops;
return 0;
}

int APS5_VABI scePthreadMutexSetspinloops(PthreadMutex* mutex, int count) {
auto* current = resolveMutex(mutex, true);
if (!current)
return SCE_KERNEL_ERROR_EINVAL;
current->_spinloops = count;
return 0;
}

int APS5_VABI scePthreadMutexGetyieldloops(PthreadMutex* mutex, int* count) {
auto* current = resolveMutex(mutex, true);
if (!current)
return SCE_KERNEL_ERROR_EINVAL;
if (!count)
throw std::invalid_argument("Yield loop count pointer is null");
*count = current->_yieldloops;
return 0;
}

int APS5_VABI scePthreadMutexSetyieldloops(PthreadMutex* mutex, int count) {
if (auto* current = resolveMutex(mutex, true))
current->_yieldloops = count;
return 0;
}

}
120 changes: 120 additions & 0 deletions core/libs/tests/GuestPthreadMutexErrors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <cstdint>
#include <cstdlib>
#include <initializer_list>
#include <stdexcept>

extern "C" {
int APS5_VABI scePthreadMutexattrInit(PthreadMutexattr* attr);
Expand All @@ -12,14 +13,131 @@ int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* a
int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexGetspinloops(PthreadMutex* mutex, int* count);
int APS5_VABI scePthreadMutexSetspinloops(PthreadMutex* mutex, int count);
int APS5_VABI scePthreadMutexGetyieldloops(PthreadMutex* mutex, int* count);
int APS5_VABI scePthreadMutexSetyieldloops(PthreadMutex* mutex, int count);
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_mutex_lock_nid_postfix(PthreadMutex* mutex);
int APS5_VABI pthread_mutex_unlock_nid_postfix(PthreadMutex* mutex);
int APS5_VABI pthread_mutex_destroy_nid_postfix(PthreadMutex* mutex);
int APS5_VABI pthread_mutex_reltimedlock_np_nid_postfix(PthreadMutex* mutex, KernelUseconds usec);
int APS5_VABI pthread_mutex_isowned_np_nid_postfix(PthreadMutex* mutex);
int APS5_VABI pthread_mutex_getspinloops_np_nid_postfix(PthreadMutex* mutex, int* count);
int APS5_VABI pthread_mutex_setspinloops_np_nid_postfix(PthreadMutex* mutex, int count);
int APS5_VABI pthread_mutex_getyieldloops_np_nid_postfix(PthreadMutex* mutex, int* count);
int APS5_VABI pthread_mutex_setyieldloops_np_nid_postfix(PthreadMutex* mutex, int count);
}

static constexpr int SCE_OK = 0;
static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast<int>(0x80020016u);
static constexpr int SCE_KERNEL_ERROR_EBUSY = static_cast<int>(0x80020010u);

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_EDEADLK);
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);
Expand All @@ -46,5 +164,7 @@ int main() {

Require(scePthreadMutexInit(&mutex, &attr, "attr") == SCE_OK);
Require(scePthreadMutexDestroy(&mutex) == SCE_OK);
CheckLoops(&attr);
CheckOwnershipAndRelativeLock();
Require(scePthreadMutexattrDestroy(&attr) == SCE_OK);
}
Loading
Loading