From ac318b56acfadacd35e68fb6a629829cc576f9e1 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Mon, 8 Sep 2025 19:47:12 -0700 Subject: [PATCH] equeue: Few fixes for sceKernelWaitEqueue (#3548) --- src/core/libraries/kernel/equeue.cpp | 52 +++++++++++++++------------- src/core/libraries/kernel/equeue.h | 2 +- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp index 06d1b57ac..2190f2533 100644 --- a/src/core/libraries/kernel/equeue.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -97,7 +97,14 @@ bool EqueueInternal::RemoveEvent(u64 id, s16 filter) { return has_found; } -int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { +int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, const SceKernelUseconds* timo) { + if (timo != nullptr && *timo == 0) { + // Effectively acts as a poll; only events that have already + // arrived at the time of this function call can be received + return GetTriggeredEvents(ev, num); + } + const auto micros = timo ? *timo : 0u; + if (HasSmallTimer()) { // If a small timer is set, just wait for it to expire. return WaitForSmallTimer(ev, num, micros); @@ -111,9 +118,11 @@ int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { }; if (micros == 0) { + // Wait indefinitely for events std::unique_lock lock{m_mutex}; m_cond.wait(lock, predicate); } else { + // Wait up until the timeout value std::unique_lock lock{m_mutex}; m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate); } @@ -127,12 +136,6 @@ int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { } } - if (ev->flags & SceKernelEvent::Flags::OneShot) { - for (auto ev_id = 0u; ev_id < count; ++ev_id) { - RemoveEvent(ev->ident, ev->filter); - } - } - return count; } @@ -159,18 +162,27 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) { int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { int count = 0; - for (auto& event : m_events) { - if (event.IsTriggered()) { - // Event should not trigger again - event.ResetTriggerState(); + for (auto it = m_events.begin(); it != m_events.end();) { + if (it->IsTriggered()) { + ev[count++] = it->event; - if (event.event.flags & SceKernelEvent::Flags::Clear) { - event.Clear(); + // Event should not trigger again + it->ResetTriggerState(); + + if (it->event.flags & SceKernelEvent::Flags::Clear) { + it->Clear(); } - ev[count++] = event.event; + if (it->event.flags & SceKernelEvent::Flags::OneShot) { + it = m_events.erase(it); + } else { + ++it; + } + if (count == num) { break; } + } else { + ++it; } } @@ -277,19 +289,11 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int } if (num < 1) { + *out = 0; return ORBIS_KERNEL_ERROR_EINVAL; } - if (timo == nullptr) { - // When the timeout is nullptr, we wait indefinitely - *out = eq->WaitForEvents(ev, num, 0); - } else if (*timo == 0) { - // Only events that have already arrived at the time of this function call can be received - *out = eq->GetTriggeredEvents(ev, num); - } else { - // Wait for up to the specified timeout value - *out = eq->WaitForEvents(ev, num, *timo); - } + *out = eq->WaitForEvents(ev, num, timo); if (*out == 0) { return ORBIS_KERNEL_ERROR_ETIMEDOUT; diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index fbc209265..e933f80d1 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -153,7 +153,7 @@ public: bool ScheduleEvent(u64 id, s16 filter, void (*callback)(SceKernelEqueue, const SceKernelEvent&)); bool RemoveEvent(u64 id, s16 filter); - int WaitForEvents(SceKernelEvent* ev, int num, u32 micros); + int WaitForEvents(SceKernelEvent* ev, int num, const SceKernelUseconds* timo); bool TriggerEvent(u64 ident, s16 filter, void* trigger_data); int GetTriggeredEvents(SceKernelEvent* ev, int num);