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 { namespace Libraries::Kernel {
constexpr int ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF; constexpr s32 ORBIS_KERNEL_SEM_VALUE_MAX = 0x7FFFFFFF;
struct PthreadSem { struct PthreadSem {
explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {} explicit PthreadSem(s32 value_) : semaphore{value_}, value{value_} {}
@@ -35,7 +35,7 @@ public:
is_fifo{is_fifo} {} is_fifo{is_fifo} {}
~OrbisSem() = default; ~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}; std::unique_lock lk{mutex};
if (token_count >= need_count) { if (token_count >= need_count) {
token_count -= need_count; token_count -= need_count;
@@ -84,13 +84,13 @@ public:
return true; return true;
} }
int Cancel(s32 set_count, s32* num_waiters) { s32 Cancel(s32 set_count, s32* num_waiters) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
if (num_waiters) { if (num_waiters) {
*num_waiters = wait_list.size(); *num_waiters = static_cast<s32>(wait_list.size());
} }
for (auto* waiter : wait_list) { for (auto* waiter : wait_list) {
waiter->was_cancled = true; waiter->was_canceled = true;
waiter->sem.release(); waiter->sem.release();
} }
wait_list.clear(); wait_list.clear();
@@ -115,7 +115,7 @@ public:
std::string thr_name; std::string thr_name;
bool was_signaled{}; bool was_signaled{};
bool was_deleted{}; bool was_deleted{};
bool was_cancled{}; bool was_canceled{};
explicit WaitingThread(s32 need_count, bool is_fifo) explicit WaitingThread(s32 need_count, bool is_fifo)
: sem{0}, priority{0}, need_count{need_count} { : sem{0}, priority{0}, need_count{need_count} {
@@ -127,40 +127,40 @@ public:
thr_name = g_curthread->name; thr_name = g_curthread->name;
} }
int GetResult(bool timed_out) { s32 GetResult() const {
if (timed_out) { if (was_signaled) {
return ORBIS_KERNEL_ERROR_ETIMEDOUT; return ORBIS_OK;
} }
if (was_deleted) { if (was_deleted) {
return ORBIS_KERNEL_ERROR_EACCES; return ORBIS_KERNEL_ERROR_EACCES;
} }
if (was_cancled) { if (was_canceled) {
return ORBIS_KERNEL_ERROR_ECANCELED; 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(); lk.unlock();
if (!timeout) { if (!timeout) {
// Wait indefinitely until we are woken up. // Wait indefinitely until we are woken up.
sem.acquire(); sem.acquire();
lk.lock(); 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 { } 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); 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)) { if (!orbis_sems.is_allocated(sem)) {
return ORBIS_KERNEL_ERROR_ESRCH; return ORBIS_KERNEL_ERROR_ESRCH;
} }
return orbis_sems[sem]->Cancel(setCount, pNumWaitThreads); 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)) { if (!orbis_sems.is_allocated(sem)) {
return ORBIS_KERNEL_ERROR_ESRCH; return ORBIS_KERNEL_ERROR_ESRCH;
} }
@@ -244,18 +244,18 @@ int PS4_SYSV_ABI sceKernelDeleteSema(OrbisKernelSema sem) {
return ORBIS_OK; 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) { if (value > ORBIS_KERNEL_SEM_VALUE_MAX) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
return -1; return -1;
} }
if (sem != nullptr) { if (sem != nullptr) {
*sem = new PthreadSem(value); *sem = new PthreadSem(static_cast<s32>(value));
} }
return 0; 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) { if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
return -1; return -1;
@@ -265,7 +265,7 @@ int PS4_SYSV_ABI posix_sem_destroy(PthreadSem** sem) {
return 0; 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) { if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
return -1; return -1;
@@ -275,7 +275,7 @@ int PS4_SYSV_ABI posix_sem_wait(PthreadSem** sem) {
return 0; 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) { if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
return -1; return -1;
@@ -288,7 +288,7 @@ int PS4_SYSV_ABI posix_sem_trywait(PthreadSem** sem) {
return 0; 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) { if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
return -1; return -1;
@@ -301,7 +301,7 @@ int PS4_SYSV_ABI posix_sem_timedwait(PthreadSem** sem, const OrbisKernelTimespec
return 0; 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) { if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
return -1; return -1;
@@ -315,7 +315,7 @@ int PS4_SYSV_ABI posix_sem_post(PthreadSem** sem) {
return 0; 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) { if (sem == nullptr || *sem == nullptr) {
*__Error() = POSIX_EINVAL; *__Error() = POSIX_EINVAL;
return -1; return -1;
@@ -326,7 +326,7 @@ int PS4_SYSV_ABI posix_sem_getvalue(PthreadSem** sem, int* sval) {
return 0; 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) { if (flag != 0) {
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
} }
@@ -388,7 +388,7 @@ s32 PS4_SYSV_ABI scePthreadSemPost(PthreadSem** sem) {
return ORBIS_OK; 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); s32 ret = posix_sem_getvalue(sem, sval);
if (ret != 0) { if (ret != 0) {
return ErrnoToSceKernelError(*__Error()); return ErrnoToSceKernelError(*__Error());