semaphore: Fix determining wait status when canceled/deleted (#3490)

This commit is contained in:
squidbus
2025-08-31 14:39:57 -07:00
committed by GitHub
parent ba65408608
commit af9947a862

View File

@@ -19,7 +19,7 @@
namespace Libraries::Kernel {
constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF;
constexpr s32 ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF;
struct PthreadSem {
explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {}
@@ -35,7 +35,7 @@ public:
is_fifo{is_fifo} {}
~OrbisSem() = default;
int Wait(bool can_block, s32 need_count, u32* timeout) {
s32 Wait(bool can_block, s32 need_count, u32* timeout) {
std::unique_lock lk{mutex};
if (token_count >= need_count) {
token_count -= need_count;
@@ -84,13 +84,13 @@ public:
return true;
}
int Cancel(s32 set_count, s32* num_waiters) {
s32 Cancel(s32 set_count, s32* num_waiters) {
std::scoped_lock lk{mutex};
if (num_waiters) {
*num_waiters = wait_list.size();
*num_waiters = static_cast<s32>(wait_list.size());
}
for (auto* waiter : wait_list) {
waiter->was_cancled = true;
waiter->was_canceled = true;
waiter->sem.release();
}
wait_list.clear();
@@ -115,7 +115,7 @@ public:
std::string thr_name;
bool was_signaled{};
bool was_deleted{};
bool was_cancled{};
bool was_canceled{};
explicit WaitingThread(s32 need_count, bool is_fifo)
: sem{0}, priority{0}, need_count{need_count} {
@@ -127,40 +127,40 @@ public:
thr_name = g_curthread->name;
}
int GetResult(bool timed_out) {
if (timed_out) {
return ORBIS_KERNEL_ERROR_ETIMEDOUT;
s32 GetResult() const {
if (was_signaled) {
return ORBIS_OK;
}
if (was_deleted) {
return ORBIS_KERNEL_ERROR_EACCES;
}
if (was_cancled) {
if (was_canceled) {
return ORBIS_KERNEL_ERROR_ECANCELED;
}
return ORBIS_OK;
return ORBIS_KERNEL_ERROR_ETIMEDOUT;
}
int Wait(std::unique_lock<std::mutex>& lk, u32* timeout) {
s32 Wait(std::unique_lock<std::mutex>& lk, u32* timeout) {
lk.unlock();
if (!timeout) {
// Wait indefinitely until we are woken up.
sem.acquire();
lk.lock();
return GetResult(false);
}
// Wait until timeout runs out, recording how much remaining time there was.
const auto start = std::chrono::high_resolution_clock::now();
sem.try_acquire_for(std::chrono::microseconds(*timeout));
const auto end = std::chrono::high_resolution_clock::now();
const auto time =
std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
lk.lock();
if (was_signaled) {
*timeout -= time;
} else {
*timeout = 0;
// Wait until timeout runs out, recording how much remaining time there was.
const auto start = std::chrono::high_resolution_clock::now();
sem.try_acquire_for(std::chrono::microseconds(*timeout));
const auto end = std::chrono::high_resolution_clock::now();
const auto time =
std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
lk.lock();
if (was_signaled) {
*timeout -= time;
} else {
*timeout = 0;
}
}
return GetResult(!was_signaled);
return GetResult();
}
};
@@ -228,14 +228,14 @@ s32 PS4_SYSV_ABI sceKernelPollSema(OrbisKernelSema sem, s32 needCount) {
return orbis_sems[sem]->Wait(false, needCount, nullptr);
}
int PS4_SYSV_ABI sceKernelCancelSema(OrbisKernelSema sem, s32 setCount, s32* pNumWaitThreads) {
s32 PS4_SYSV_ABI sceKernelCancelSema(OrbisKernelSema sem, s32 setCount, s32* pNumWaitThreads) {
if (!orbis_sems.is_allocated(sem)) {
return ORBIS_KERNEL_ERROR_ESRCH;
}
return orbis_sems[sem]->Cancel(setCount, pNumWaitThreads);
}
int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) {
s32 PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) {
if (!orbis_sems.is_allocated(sem)) {
return ORBIS_KERNEL_ERROR_ESRCH;
}
@@ -244,18 +244,18 @@ int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) {
return ORBIS_OK;
}
int PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, int pshared, u32 value) {
s32 PS4_SYSV_ABI posix_sem_init(PthreadSem** sem, s32 pshared, u32 value) {
if (value > ORBIS_KERNEL_SEM_VALUE_MAX) {
*__Error() = POSIX_EINVAL;
return -1;
}
if (sem != nullptr) {
*sem = new PthreadSem(value);
*sem = new PthreadSem(static_cast<s32>(value));
}
return 0;
}
int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) {
s32 PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
return -1;
@@ -265,7 +265,7 @@ int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) {
return 0;
}
int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) {
s32 PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
return -1;
@@ -275,7 +275,7 @@ int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) {
return 0;
}
int PS4_SYSV_ABI posix_sem_trywait(PthreadSem** sem) {
s32 PS4_SYSV_ABI posix_sem_trywait(PthreadSem** sem) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
return -1;
@@ -288,7 +288,7 @@ int PS4_SYSV_ABI posix_sem_trywait(PthreadSem** sem) {
return 0;
}
int PS4_SYSV_ABI posix_sem_timedwait(PthreadSem** sem, const OrbisKernelTimespec* t) {
s32 PS4_SYSV_ABI posix_sem_timedwait(PthreadSem** sem, const OrbisKernelTimespec* t) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
return -1;
@@ -301,7 +301,7 @@ int PS4_SYSV_ABI posix_sem_timedwait(PthreadSem** sem, const OrbisKernelTimespec
return 0;
}
int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) {
s32 PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
return -1;
@@ -315,7 +315,7 @@ int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) {
return 0;
}
int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) {
s32 PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, s32* sval) {
if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL;
return -1;
@@ -326,7 +326,7 @@ int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) {
return 0;
}
s32 PS4_SYSV_ABI scePthreadSemInit(PthreadSem** sem, int flag, u32 value, const char* name) {
s32 PS4_SYSV_ABI scePthreadSemInit(PthreadSem** sem, s32 flag, u32 value, const char* name) {
if (flag != 0) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
@@ -388,7 +388,7 @@ s32 PS4_SYSV_ABI scePthreadSemPost(PthreadSem** sem) {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePthreadSemGetvalue(PthreadSem** sem, int* sval) {
s32 PS4_SYSV_ABI scePthreadSemGetvalue(PthreadSem** sem, s32* sval) {
s32 ret = posix_sem_getvalue(sem, sval);
if (ret != 0) {
return ErrnoToSceKernelError(*__Error());