feat(libkernel): sceKernelAddTimerEvent and sceKernelDeleteTimerEvent
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
def8119927
commit
d081e6749d
|
|
@ -46,7 +46,14 @@ void KernelEqueuePrivate::Close() {
|
|||
|
||||
void KernelEqueuePrivate::TriggerExpiredTimers(uint64_t nowNs) {
|
||||
for (auto& ev : m_events) {
|
||||
if (!ev.triggered && ev.deadlineNs != 0 && ev.deadlineNs <= nowNs) {
|
||||
if (ev.deadlineNs != 0 && ev.deadlineNs <= nowNs) {
|
||||
if (ev.event.filter == EVFILT_TIMER) {
|
||||
const uint64_t count = ev.intervalNs == 0
|
||||
? (ev.triggered ? 0 : 1)
|
||||
: 1 + (nowNs - ev.deadlineNs) / ev.intervalNs;
|
||||
ev.event.data += static_cast<intptr_t>(count);
|
||||
ev.deadlineNs += count * ev.intervalNs;
|
||||
}
|
||||
ev.triggered = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -176,7 +183,11 @@ int KernelEqueuePrivate::AddEvent(const KernelEqueueEvent& event) {
|
|||
}
|
||||
);
|
||||
if (it != m_events.end()) {
|
||||
if (event.event.filter == EVFILT_TIMER) {
|
||||
TriggerExpiredTimers(MonotonicNs());
|
||||
}
|
||||
it->deadlineNs = event.deadlineNs;
|
||||
it->intervalNs = event.intervalNs;
|
||||
it->event.udata = event.event.udata;
|
||||
for (auto& pending : it->pendingEvents) {
|
||||
pending.udata = event.event.udata;
|
||||
|
|
@ -403,6 +414,22 @@ int APS5_VABI sceKernelDeleteHRTimerEvent(KernelEqueue eq, int id) {
|
|||
return EqueueDeleteEvent_nid_postfix(eq, static_cast<uintptr_t>(id), EVFILT_HRTIMER);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelAddTimerEvent(KernelEqueue eq, int id, KernelUseconds usec, void* udata) {
|
||||
const uint64_t intervalNs = static_cast<uint64_t>(usec) * 1000ULL;
|
||||
KernelEqueueEvent event{};
|
||||
event.deadlineNs = KernelEqueuePrivate::MonotonicNs() + intervalNs;
|
||||
event.intervalNs = intervalNs;
|
||||
event.event.ident = static_cast<uintptr_t>(id);
|
||||
event.event.filter = EVFILT_TIMER;
|
||||
event.event.flags = EV_ADD | EV_CLEAR;
|
||||
event.event.udata = udata;
|
||||
return EqueueAddEvent_nid_postfix(eq, event);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelDeleteTimerEvent(KernelEqueue eq, int id) {
|
||||
return EqueueDeleteEvent_nid_postfix(eq, static_cast<uintptr_t>(id), EVFILT_TIMER);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelAddAmprEvent(KernelEqueue eq, int id, void* udata) {
|
||||
if (eq == 0) {
|
||||
return EQUEUE_OK;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "prx/libkernel/KernelErrors.hpp"
|
||||
#include "prx/libkernel/Time/include/TimedWait.hpp"
|
||||
|
||||
static constexpr int16_t EVFILT_TIMER = -7;
|
||||
static constexpr int16_t EVFILT_USER = -11;
|
||||
static constexpr int16_t EVFILT_VIDEO_OUT = -13;
|
||||
static constexpr int16_t EVFILT_HRTIMER = -15;
|
||||
|
|
@ -42,6 +43,7 @@ struct KernelFilter {
|
|||
struct KernelEqueueEvent {
|
||||
bool triggered = false;
|
||||
uint64_t deadlineNs = 0;
|
||||
uint64_t intervalNs = 0;
|
||||
KernelEvent event;
|
||||
KernelFilter filter;
|
||||
std::deque<KernelEvent> pendingEvents;
|
||||
|
|
@ -64,9 +66,9 @@ public:
|
|||
int GetTriggeredEvents(KernelEvent* ev, int num);
|
||||
int WaitForEvents(KernelEvent* ev, int num, uint32_t micros);
|
||||
void Close();
|
||||
static uint64_t MonotonicNs();
|
||||
|
||||
private:
|
||||
static uint64_t MonotonicNs();
|
||||
void TriggerExpiredTimers(uint64_t nowNs);
|
||||
bool NextTimerWaitMicros(uint64_t nowNs, uint32_t* out) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI sceKernelCreateEqueue(KernelEqueue* eq, const char* name);
|
||||
|
|
@ -11,6 +13,8 @@ int APS5_VABI sceKernelAddUserEvent(KernelEqueue eq, int id);
|
|||
int APS5_VABI sceKernelTriggerUserEvent(KernelEqueue eq, int id, void* udata);
|
||||
int APS5_VABI sceKernelDeleteUserEvent(KernelEqueue eq, int id);
|
||||
int APS5_VABI sceKernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTimespec* ts, void* udata);
|
||||
int APS5_VABI sceKernelAddTimerEvent(KernelEqueue eq, int id, KernelUseconds usec, void* udata);
|
||||
int APS5_VABI sceKernelDeleteTimerEvent(KernelEqueue eq, int id);
|
||||
intptr_t APS5_VABI sceKernelGetEventData(const KernelEvent* ev);
|
||||
intptr_t APS5_VABI sceKernelGetEventFflags(const KernelEvent* ev);
|
||||
int APS5_VABI sceKernelGetEventFilter(const KernelEvent* ev);
|
||||
|
|
@ -19,6 +23,10 @@ void* APS5_VABI sceKernelGetEventUserData(const KernelEvent* ev);
|
|||
}
|
||||
|
||||
static constexpr int SCE_OK = 0;
|
||||
static constexpr int SCE_KERNEL_ERROR_ENOENT = static_cast<int>(0x80020002);
|
||||
static constexpr int SCE_KERNEL_ERROR_EBADF = static_cast<int>(0x80020009);
|
||||
static constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = static_cast<int>(0x8002003c);
|
||||
static constexpr int EVFILT_TIMER = -7;
|
||||
static constexpr int EVFILT_USER = -11;
|
||||
static constexpr int EVFILT_HRTIMER = -15;
|
||||
|
||||
|
|
@ -34,7 +42,82 @@ static bool RejectsNull(TResult (APS5_VABI *accessor)(const KernelEvent*)) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static void SleepAtLeast(std::chrono::steady_clock::duration duration) {
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
while (std::chrono::steady_clock::now() - start < duration) {
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
}
|
||||
|
||||
static void VerifyPeriodicTimer() {
|
||||
using namespace std::chrono_literals;
|
||||
int first = 0;
|
||||
int second = 0;
|
||||
Require(sceKernelAddTimerEvent(0, 10, 1000, &first) == SCE_KERNEL_ERROR_EBADF);
|
||||
Require(sceKernelDeleteTimerEvent(0, 10) == SCE_KERNEL_ERROR_EBADF);
|
||||
|
||||
KernelEqueue eq = 0;
|
||||
Require(sceKernelCreateEqueue(&eq, "timer") == SCE_OK);
|
||||
Require(sceKernelDeleteTimerEvent(eq, 10) == SCE_KERNEL_ERROR_ENOENT);
|
||||
|
||||
KernelEvent events[2]{};
|
||||
int count = 0;
|
||||
const KernelUseconds poll = 0;
|
||||
const KernelUseconds wait = 1000000;
|
||||
Require(sceKernelAddTimerEvent(eq, 10, 1000, &first) == SCE_OK);
|
||||
SleepAtLeast(5ms);
|
||||
Require(sceKernelWaitEqueue(eq, events, 2, &count, &poll) == SCE_OK);
|
||||
Require(count == 1);
|
||||
Require(sceKernelGetEventId(&events[0]) == 10);
|
||||
Require(sceKernelGetEventFilter(&events[0]) == EVFILT_TIMER);
|
||||
Require(sceKernelGetEventData(&events[0]) >= 5);
|
||||
Require(sceKernelGetEventUserData(&events[0]) == &first);
|
||||
Require(sceKernelWaitEqueue(eq, events, 2, &count, &wait) == SCE_OK);
|
||||
Require(count == 1 && sceKernelGetEventData(&events[0]) >= 1);
|
||||
|
||||
SleepAtLeast(5ms);
|
||||
Require(sceKernelAddTimerEvent(eq, 10, 3000000000u, &second) == SCE_OK);
|
||||
Require(sceKernelWaitEqueue(eq, events, 2, &count, &poll) == SCE_OK);
|
||||
Require(count == 1);
|
||||
Require(sceKernelGetEventData(&events[0]) >= 5);
|
||||
Require(sceKernelGetEventUserData(&events[0]) == &second);
|
||||
Require(sceKernelWaitEqueue(eq, events, 2, &count, &poll) == SCE_KERNEL_ERROR_ETIMEDOUT);
|
||||
Require(sceKernelDeleteTimerEvent(eq, 10) == SCE_OK);
|
||||
Require(sceKernelDeleteTimerEvent(eq, 10) == SCE_KERNEL_ERROR_ENOENT);
|
||||
|
||||
Require(sceKernelAddTimerEvent(eq, 13, 3000000000u, &first) == SCE_OK);
|
||||
const auto readded = std::chrono::steady_clock::now();
|
||||
Require(sceKernelAddTimerEvent(eq, 13, 200000, &first) == SCE_OK);
|
||||
SleepAtLeast(250ms);
|
||||
Require(sceKernelWaitEqueue(eq, events, 2, &count, &poll) == SCE_OK);
|
||||
Require(count == 1 && sceKernelGetEventData(&events[0]) >= 1);
|
||||
const intptr_t expirations = sceKernelGetEventData(&events[0]);
|
||||
const int again = sceKernelWaitEqueue(eq, events, 2, &count, &poll);
|
||||
if (std::chrono::steady_clock::now() - readded < 400ms) {
|
||||
Require(expirations == 1 && again == SCE_KERNEL_ERROR_ETIMEDOUT);
|
||||
}
|
||||
Require(sceKernelWaitEqueue(eq, events, 2, &count, &wait) == SCE_OK);
|
||||
Require(count == 1 && sceKernelGetEventId(&events[0]) == 13);
|
||||
Require(sceKernelDeleteTimerEvent(eq, 13) == SCE_OK);
|
||||
|
||||
Require(sceKernelAddTimerEvent(eq, 11, 0, &first) == SCE_OK);
|
||||
Require(sceKernelAddTimerEvent(eq, 11, 0, &first) == SCE_OK);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Require(sceKernelWaitEqueue(eq, events, 2, &count, &poll) == SCE_OK);
|
||||
Require(count == 1 && sceKernelGetEventData(&events[0]) == 1);
|
||||
}
|
||||
Require(sceKernelDeleteTimerEvent(eq, 11) == SCE_OK);
|
||||
Require(sceKernelWaitEqueue(eq, events, 2, &count, &poll) == SCE_KERNEL_ERROR_ETIMEDOUT);
|
||||
|
||||
const KernelTimespec delay{0, 1000000};
|
||||
Require(sceKernelAddHRTimerEvent(eq, 12, &delay, &first) == SCE_OK);
|
||||
Require(sceKernelDeleteTimerEvent(eq, 12) == SCE_KERNEL_ERROR_ENOENT);
|
||||
Require(sceKernelDeleteEqueue(eq) == SCE_OK);
|
||||
}
|
||||
|
||||
int main() {
|
||||
VerifyPeriodicTimer();
|
||||
|
||||
KernelEqueue eq = 0;
|
||||
Require(sceKernelCreateEqueue(&eq, "events") == SCE_OK);
|
||||
const KernelUseconds timeout = 1000000;
|
||||
|
|
|
|||
Loading…
Reference in New Issue