fix(libkernel): return EDEADLK for a thread joining itself and EINVAL for a null thread

scePthreadJoin threw when a joinable thread joined itself and
scePthreadJoin and scePthreadDetach threw on a null thread, so
pthread_join and pthread_detach ended the process where FreeBSD libthr
returns EDEADLK and EINVAL.
This commit is contained in:
Hussein Kadhim 2026-10-11 23:29:08 +03:00 committed by MarcZX100
parent c059590d8e
commit 351ca3bb12
2 changed files with 18 additions and 4 deletions

View File

@ -618,10 +618,9 @@ int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, Pthread
}
int APS5_VABI scePthreadJoin(Pthread thread, void** retval) {
if (!thread) throw std::runtime_error("scePthreadJoin: null thread");
if (!thread) return SCE_KERNEL_ERROR_EINVAL;
if (thread->_detached) return SCE_KERNEL_ERROR_EINVAL;
if (thread == CurrentGuestState().current)
throw std::runtime_error("scePthreadJoin: cannot join current thread");
if (thread == CurrentGuestState().current) return SCE_KERNEL_ERROR_EDEADLK;
ThreadCancel::Check();
{
std::unique_lock lock(thread->_join_mtx);
@ -642,7 +641,7 @@ int APS5_VABI scePthreadJoin(Pthread thread, void** retval) {
}
int APS5_VABI scePthreadDetach(Pthread thread) {
if (!thread) throw std::runtime_error("scePthreadDetach: null thread");
if (!thread) return SCE_KERNEL_ERROR_EINVAL;
if (thread->_detached) return SCE_KERNEL_ERROR_EINVAL;
thread->_detached = true;
#ifdef _WIN32

View File

@ -7,6 +7,8 @@ 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 scePthreadDetach(Pthread thread);
int APS5_VABI pthread_join_nid_postfix(Pthread thread, void** value);
int APS5_VABI pthread_detach_nid_postfix(Pthread thread);
void APS5_VABI scePthreadExit(void* retval);
Pthread APS5_VABI scePthreadSelf();
int APS5_VABI scePthreadGetthreadid(void);
@ -46,6 +48,9 @@ static bool StackContains(Pthread thread, const void* address) {
static constexpr int SCE_OK = 0;
static constexpr int SCE_KERNEL_ERROR_EINVAL = 0x80020016;
static constexpr int SCE_KERNEL_ERROR_EPERM = 0x80020001;
static constexpr int SCE_KERNEL_ERROR_EDEADLK = 0x8002000b;
static constexpr int GUEST_EDEADLK = 11;
static constexpr int GUEST_EINVAL = 22;
static constexpr int MUTEX_TYPE_RECURSIVE = 2;
static constexpr int PTHREAD_CANCEL_ENABLE = 0;
static constexpr int PTHREAD_CANCEL_ASYNCHRONOUS = 2;
@ -69,6 +74,8 @@ struct WorkerContext {
PthreadMutex* mutex = nullptr;
int unlockResult = 0;
bool testcancelReturned = false;
int sceJoinSelf = 0;
int posixJoinSelf = 0;
};
static void* APS5_VABI Worker(void* arg) {
@ -80,6 +87,8 @@ static void* APS5_VABI Worker(void* arg) {
int local = 0;
context.workerStackReported = StackContains(context.selfFromWorker, &local);
context.unlockResult = scePthreadMutexUnlock(context.mutex);
context.sceJoinSelf = scePthreadJoin(context.selfFromWorker, nullptr);
context.posixJoinSelf = pthread_join_nid_postfix(context.selfFromWorker, nullptr);
int oldState = -1;
int oldType = -1;
context.testcancelReturned = scePthreadSetcancelstate(PTHREAD_CANCEL_ENABLE, &oldState) == SCE_OK &&
@ -103,6 +112,10 @@ int main() {
Require(StackContains(mainSelf, &local));
Require(scePthreadJoin(mainSelf, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadDetach(mainSelf) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadJoin(nullptr, nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadDetach(nullptr) == SCE_KERNEL_ERROR_EINVAL);
Require(pthread_join_nid_postfix(nullptr, nullptr) == GUEST_EINVAL);
Require(pthread_detach_nid_postfix(nullptr) == GUEST_EINVAL);
PthreadMutexattr attr = nullptr;
Require(scePthreadMutexattrInit(&attr) == SCE_OK);
@ -129,6 +142,8 @@ int main() {
Require(context.workerThreadId == static_cast<int>(context.workerTid));
Require(context.workerPosixThreadId == context.workerThreadId && context.workerPosixThreadId != static_cast<int>(mainTid));
Require(context.unlockResult == SCE_KERNEL_ERROR_EPERM);
Require(context.sceJoinSelf == SCE_KERNEL_ERROR_EDEADLK);
Require(context.posixJoinSelf == GUEST_EDEADLK);
Require(context.workerStackReported);
Require(context.testcancelReturned);