Files
AnyPS5/core/libs/tests/GuestCondRecursiveMutex.cpp
T
clem-gbandClaude Opus 5.5 7406c975d2 fix(libkernel): release every level of a recursive mutex during a cond wait
A condition wait on a recursive mutex locked more than once unlocked the
native mutex only once, so the thread that should signal blocked forever
on it. Unlock the remaining levels before the wait and lock them again
after it, as FreeBSD libthr does in _mutex_cv_unlock and _mutex_cv_lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
2026-10-08 10:53:58 +02:00

102 lines
4.3 KiB
C++

#include "SceTypes.hpp"
#include <cstdlib>
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 scePthreadMutexattrInit(PthreadMutexattr* attr);
int APS5_VABI scePthreadMutexattrDestroy(PthreadMutexattr* attr);
int APS5_VABI scePthreadMutexattrSettype(PthreadMutexattr* attr, int type);
int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char* name);
int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex);
int APS5_VABI scePthreadCondInit(PthreadCond* cond, const PthreadCondattr* attr, const char* name);
int APS5_VABI scePthreadCondDestroy(PthreadCond* cond);
int APS5_VABI scePthreadCondSignal(PthreadCond* cond);
int APS5_VABI scePthreadCondWait(PthreadCond* cond, PthreadMutex* mutex);
int APS5_VABI scePthreadCondTimedwait(PthreadCond* cond, PthreadMutex* mutex, unsigned int usec);
int APS5_VABI pthread_cond_wait_nid_postfix(PthreadCond* cond, PthreadMutex* mutex);
int APS5_VABI pthread_cond_timedwait_nid_postfix(PthreadCond* cond, PthreadMutex* mutex, const KernelTimespec* abstime);
int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp);
}
static constexpr int SCE_OK = 0;
static constexpr int SCE_KERNEL_ERROR_EPERM = static_cast<int>(0x80020001);
static constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = static_cast<int>(0x8002003C);
static constexpr int POSIX_ETIMEDOUT = 60;
static constexpr int MUTEX_TYPE_RECURSIVE = 2;
static void Require(bool value) { if (!value) std::abort(); }
struct Context {
PthreadMutex mutex = nullptr;
PthreadCond cond = nullptr;
bool signalled = false;
};
static void* APS5_VABI Signaller(void* arg) {
auto& context = *static_cast<Context*>(arg);
Require(scePthreadMutexLock(&context.mutex) == SCE_OK);
context.signalled = true;
Require(scePthreadCondSignal(&context.cond) == SCE_OK);
Require(scePthreadMutexUnlock(&context.mutex) == SCE_OK);
return nullptr;
}
static int SceWait(Context& context) {
return scePthreadCondWait(&context.cond, &context.mutex);
}
static int SceTimedwait(Context& context) {
const int result = scePthreadCondTimedwait(&context.cond, &context.mutex, 1000);
return result == SCE_KERNEL_ERROR_ETIMEDOUT ? SCE_OK : result;
}
static int PosixWait(Context& context) {
return pthread_cond_wait_nid_postfix(&context.cond, &context.mutex);
}
static int PosixTimedwait(Context& context) {
KernelTimespec deadline{};
Require(clock_gettime_nid_postfix(0, &deadline) == 0);
deadline.tv_nsec += 1000000;
if (deadline.tv_nsec >= 1000000000) {
deadline.tv_nsec -= 1000000000;
++deadline.tv_sec;
}
const int result = pthread_cond_timedwait_nid_postfix(&context.cond, &context.mutex, &deadline);
return result == POSIX_ETIMEDOUT ? SCE_OK : result;
}
static void WaitWithRecursiveMutex(int (*wait)(Context&)) {
Context context;
PthreadMutexattr attr = nullptr;
Require(scePthreadMutexattrInit(&attr) == SCE_OK);
Require(scePthreadMutexattrSettype(&attr, MUTEX_TYPE_RECURSIVE) == SCE_OK);
Require(scePthreadMutexInit(&context.mutex, &attr, nullptr) == SCE_OK);
Require(scePthreadMutexattrDestroy(&attr) == SCE_OK);
Require(scePthreadCondInit(&context.cond, nullptr, nullptr) == SCE_OK);
Require(scePthreadMutexLock(&context.mutex) == SCE_OK);
Require(scePthreadMutexLock(&context.mutex) == SCE_OK);
Pthread thread = nullptr;
Require(scePthreadCreate(&thread, nullptr, Signaller, &context, nullptr) == SCE_OK);
while (!context.signalled)
Require(wait(context) == SCE_OK);
Require(scePthreadMutexUnlock(&context.mutex) == SCE_OK);
Require(scePthreadMutexUnlock(&context.mutex) == SCE_OK);
Require(scePthreadMutexUnlock(&context.mutex) == SCE_KERNEL_ERROR_EPERM);
Require(scePthreadJoin(thread, nullptr) == SCE_OK);
Require(scePthreadCondDestroy(&context.cond) == SCE_OK);
Require(scePthreadMutexDestroy(&context.mutex) == SCE_OK);
}
int main() {
WaitWithRecursiveMutex(SceWait);
WaitWithRecursiveMutex(SceTimedwait);
WaitWithRecursiveMutex(PosixWait);
WaitWithRecursiveMutex(PosixTimedwait);
}