mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 05:16:56 +01:00
Support periodic kernel timers through shared event queue deadlines
This commit is contained in:
+26
-12
@@ -138,7 +138,13 @@ int KernelEqueuePrivate::GetTriggeredEvents(KernelEvent* ev, int num) {
|
|||||||
|
|
||||||
void KernelEqueuePrivate::TriggerExpiredTimers(uint64_t now_ns) {
|
void KernelEqueuePrivate::TriggerExpiredTimers(uint64_t now_ns) {
|
||||||
for (auto& event: m_events) {
|
for (auto& event: m_events) {
|
||||||
if (!event.triggered && event.deadline_ns != 0 && event.deadline_ns <= now_ns) {
|
if (event.deadline_ns != 0 && event.deadline_ns <= now_ns) {
|
||||||
|
if (event.event.filter == KERNEL_EVFILT_TIMER) {
|
||||||
|
const auto count = event.interval_ns == 0 ? !event.triggered :
|
||||||
|
1 + (now_ns - event.deadline_ns) / event.interval_ns;
|
||||||
|
event.event.data += static_cast<intptr_t>(count);
|
||||||
|
event.deadline_ns += count * event.interval_ns;
|
||||||
|
}
|
||||||
event.triggered = true;
|
event.triggered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,7 +215,9 @@ int KernelEqueuePrivate::AddEvent(const KernelEqueueEvent& event) {
|
|||||||
return e.event.ident == ident && e.event.filter == filter;
|
return e.event.ident == ident && e.event.filter == filter;
|
||||||
});
|
});
|
||||||
if (it != m_events.end()) {
|
if (it != m_events.end()) {
|
||||||
|
TriggerExpiredTimers(MonotonicTimeNs());
|
||||||
it->deadline_ns = event.deadline_ns;
|
it->deadline_ns = event.deadline_ns;
|
||||||
|
it->interval_ns = event.interval_ns;
|
||||||
it->event.udata = event.event.udata;
|
it->event.udata = event.event.udata;
|
||||||
for (auto& pending: it->pending_events) {
|
for (auto& pending: it->pending_events) {
|
||||||
pending.udata = event.event.udata;
|
pending.udata = event.event.udata;
|
||||||
@@ -511,6 +519,22 @@ int KYTY_SYSV_ABI KernelDeleteUserEvent(KernelEqueue eq, int id) {
|
|||||||
return KernelDeleteEvent(eq, static_cast<uintptr_t>(id), KERNEL_EVFILT_USER);
|
return KernelDeleteEvent(eq, static_cast<uintptr_t>(id), KERNEL_EVFILT_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int AddTimerEvent(KernelEqueue eq, int id, uint64_t delay_ns, bool periodic, void* udata) {
|
||||||
|
const auto now_ns = MonotonicTimeNs();
|
||||||
|
KernelEqueueEvent event {};
|
||||||
|
event.deadline_ns = delay_ns <= UINT64_MAX - now_ns ? now_ns + delay_ns : UINT64_MAX;
|
||||||
|
event.interval_ns = periodic ? delay_ns : 0;
|
||||||
|
event.event.ident = static_cast<uintptr_t>(id);
|
||||||
|
event.event.filter = periodic ? KERNEL_EVFILT_TIMER : KERNEL_EVFILT_HRTIMER;
|
||||||
|
event.event.flags = EV_ADD | (periodic ? EV_CLEAR : EV_ONESHOT);
|
||||||
|
event.event.udata = udata;
|
||||||
|
return KernelAddEvent(eq, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
int KYTY_SYSV_ABI KernelAddTimerEvent(KernelEqueue eq, int id, KernelUseconds usec, void* udata) {
|
||||||
|
return AddTimerEvent(eq, id, static_cast<uint64_t>(usec) * 1000, true, udata);
|
||||||
|
}
|
||||||
|
|
||||||
int KYTY_SYSV_ABI KernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTimespec* ts,
|
int KYTY_SYSV_ABI KernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTimespec* ts,
|
||||||
void* udata) {
|
void* udata) {
|
||||||
if (ts == nullptr) {
|
if (ts == nullptr) {
|
||||||
@@ -524,17 +548,7 @@ int KYTY_SYSV_ABI KernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTim
|
|||||||
|
|
||||||
const auto delay_ns =
|
const auto delay_ns =
|
||||||
static_cast<uint64_t>(ts->tv_sec) * 1000000000ull + static_cast<uint64_t>(ts->tv_nsec);
|
static_cast<uint64_t>(ts->tv_sec) * 1000000000ull + static_cast<uint64_t>(ts->tv_nsec);
|
||||||
const auto now_ns = MonotonicTimeNs();
|
return AddTimerEvent(eq, id, delay_ns, false, udata);
|
||||||
|
|
||||||
KernelEqueueEvent event {};
|
|
||||||
event.deadline_ns = delay_ns <= UINT64_MAX - now_ns ? now_ns + delay_ns : UINT64_MAX;
|
|
||||||
event.event.ident = static_cast<uintptr_t>(id);
|
|
||||||
event.event.filter = KERNEL_EVFILT_HRTIMER;
|
|
||||||
event.event.flags = EV_ADD | EV_ONESHOT;
|
|
||||||
event.event.fflags = 0;
|
|
||||||
event.event.data = 0;
|
|
||||||
event.event.udata = udata;
|
|
||||||
return KernelAddEvent(eq, event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int KYTY_SYSV_ABI KernelDeleteHRTimerEvent(KernelEqueue eq, int id) {
|
int KYTY_SYSV_ABI KernelDeleteHRTimerEvent(KernelEqueue eq, int id) {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ struct KernelFilter {
|
|||||||
struct KernelEqueueEvent {
|
struct KernelEqueueEvent {
|
||||||
bool triggered = false;
|
bool triggered = false;
|
||||||
uint64_t deadline_ns = 0;
|
uint64_t deadline_ns = 0;
|
||||||
|
uint64_t interval_ns = 0;
|
||||||
KernelEvent event;
|
KernelEvent event;
|
||||||
KernelFilter filter;
|
KernelFilter filter;
|
||||||
std::deque<KernelEvent> pending_events;
|
std::deque<KernelEvent> pending_events;
|
||||||
@@ -72,6 +73,8 @@ int KYTY_SYSV_ABI KernelAddUserEventEdge(KernelEqueue eq, int id);
|
|||||||
int KYTY_SYSV_ABI KernelTriggerUserEvent(KernelEqueue eq, int id, void* udata);
|
int KYTY_SYSV_ABI KernelTriggerUserEvent(KernelEqueue eq, int id, void* udata);
|
||||||
int KYTY_SYSV_ABI KernelTriggerUserEventForAll(int id, void* udata);
|
int KYTY_SYSV_ABI KernelTriggerUserEventForAll(int id, void* udata);
|
||||||
int KYTY_SYSV_ABI KernelDeleteUserEvent(KernelEqueue eq, int id);
|
int KYTY_SYSV_ABI KernelDeleteUserEvent(KernelEqueue eq, int id);
|
||||||
|
int KYTY_SYSV_ABI KernelAddTimerEvent(KernelEqueue eq, int id, KernelUseconds usec,
|
||||||
|
void* udata);
|
||||||
int KYTY_SYSV_ABI KernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTimespec* ts,
|
int KYTY_SYSV_ABI KernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTimespec* ts,
|
||||||
void* udata);
|
void* udata);
|
||||||
int KYTY_SYSV_ABI KernelDeleteHRTimerEvent(KernelEqueue eq, int id);
|
int KYTY_SYSV_ABI KernelDeleteHRTimerEvent(KernelEqueue eq, int id);
|
||||||
|
|||||||
@@ -3148,6 +3148,7 @@ LIB_DEFINE(InitLibKernel_1_Equeue) {
|
|||||||
LIB_FUNC("WDszmSbWuDk", EventQueue::KernelAddUserEventEdge);
|
LIB_FUNC("WDszmSbWuDk", EventQueue::KernelAddUserEventEdge);
|
||||||
LIB_FUNC("F6e0kwo4cnk", EventQueue::KernelTriggerUserEvent);
|
LIB_FUNC("F6e0kwo4cnk", EventQueue::KernelTriggerUserEvent);
|
||||||
LIB_FUNC("LJDwdSNTnDg", EventQueue::KernelDeleteUserEvent);
|
LIB_FUNC("LJDwdSNTnDg", EventQueue::KernelDeleteUserEvent);
|
||||||
|
LIB_FUNC("57ZK+ODEXWY", EventQueue::KernelAddTimerEvent);
|
||||||
LIB_FUNC("R74tt43xP6k", EventQueue::KernelAddHRTimerEvent);
|
LIB_FUNC("R74tt43xP6k", EventQueue::KernelAddHRTimerEvent);
|
||||||
LIB_FUNC("J+LF6LwObXU", EventQueue::KernelDeleteHRTimerEvent);
|
LIB_FUNC("J+LF6LwObXU", EventQueue::KernelDeleteHRTimerEvent);
|
||||||
LIB_FUNC("bBfz7kMF2Ho", EventQueue::KernelAddAmprEvent);
|
LIB_FUNC("bBfz7kMF2Ho", EventQueue::KernelAddAmprEvent);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -27,6 +28,69 @@ void CheckConcurrentResult(int result, const char* text) {
|
|||||||
Check(result == OK || result == KERNEL_ERROR_EBADF || result == KERNEL_ERROR_ENOENT, text);
|
Check(result == OK || result == KERNEL_ERROR_EBADF || result == KERNEL_ERROR_ENOENT, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestPeriodicTimerEvents() {
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
using Libs::LibKernel::KERNEL_ERROR_ETIMEDOUT;
|
||||||
|
Check(EventQueue::KernelAddTimerEvent(EventQueue::KERNEL_EQUEUE_INVALID, 10, 1000, nullptr) ==
|
||||||
|
KERNEL_ERROR_EBADF,
|
||||||
|
"timer rejects invalid queue");
|
||||||
|
EventQueue::KernelEqueue queue = EventQueue::KERNEL_EQUEUE_INVALID;
|
||||||
|
Check(EventQueue::KernelCreateEqueue(&queue, "periodic-timer") == OK, "create timer queue");
|
||||||
|
auto* user_data = reinterpret_cast<void*>(0x1234);
|
||||||
|
Check(EventQueue::KernelAddTimerEvent(queue, 10, 1000, user_data) == OK, "add periodic timer");
|
||||||
|
std::this_thread::sleep_for(5ms);
|
||||||
|
EventQueue::KernelEvent events[2] {};
|
||||||
|
int out = 0;
|
||||||
|
Libs::LibKernel::KernelUseconds timeout = 0;
|
||||||
|
Check(EventQueue::KernelWaitEqueue(queue, events, 2, &out, &timeout) == OK && out == 1,
|
||||||
|
"periodic occurrences are aggregated into one event");
|
||||||
|
Check(events[0].ident == 10 && events[0].filter == EventQueue::KERNEL_EVFILT_TIMER &&
|
||||||
|
events[0].data >= 5 && events[0].udata == user_data,
|
||||||
|
"timer reports identity, userdata and elapsed occurrence count");
|
||||||
|
timeout = 100000;
|
||||||
|
Check(EventQueue::KernelWaitEqueue(queue, events, 2, &out, &timeout) == OK && out == 1 &&
|
||||||
|
events[0].data >= 1,
|
||||||
|
"periodic timer remains registered after delivery");
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(5ms);
|
||||||
|
user_data = reinterpret_cast<void*>(0x5678);
|
||||||
|
Check(EventQueue::KernelAddTimerEvent(queue, 10, 3000000000u, user_data) == OK,
|
||||||
|
"update periodic timer interval");
|
||||||
|
timeout = 0;
|
||||||
|
Check(EventQueue::KernelWaitEqueue(queue, events, 2, &out, &timeout) == OK && out == 1 &&
|
||||||
|
events[0].data >= 5 && events[0].udata == user_data,
|
||||||
|
"timer update preserves accumulated occurrences and updates userdata");
|
||||||
|
Check(EventQueue::KernelWaitEqueue(queue, events, 2, &out, &timeout) == KERNEL_ERROR_ETIMEDOUT,
|
||||||
|
"timer update replaces the repeating interval");
|
||||||
|
Check(EventQueue::KernelDeleteEvent(queue, 10, EventQueue::KERNEL_EVFILT_TIMER) == OK,
|
||||||
|
"delete periodic timer");
|
||||||
|
|
||||||
|
Check(EventQueue::KernelAddTimerEvent(queue, 10, 0, user_data) == OK,
|
||||||
|
"add immediate periodic timer");
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
Check(EventQueue::KernelAddTimerEvent(queue, 10, 0, user_data) == OK,
|
||||||
|
"update pending immediate timer");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
Check(EventQueue::KernelWaitEqueue(queue, events, 2, &out, &timeout) == OK && out == 1 &&
|
||||||
|
events[0].data == 1,
|
||||||
|
"zero interval remains immediately ready after delivery");
|
||||||
|
}
|
||||||
|
Check(EventQueue::KernelDeleteEvent(queue, 10, EventQueue::KERNEL_EVFILT_TIMER) == OK,
|
||||||
|
"delete immediate timer");
|
||||||
|
|
||||||
|
const Libs::LibKernel::KernelTimespec delay {0, 1000000};
|
||||||
|
Check(EventQueue::KernelAddHRTimerEvent(queue, 10, &delay, user_data) == OK,
|
||||||
|
"add high resolution timer");
|
||||||
|
timeout = 100000;
|
||||||
|
Check(EventQueue::KernelWaitEqueue(queue, events, 2, &out, &timeout) == OK && out == 1 &&
|
||||||
|
events[0].filter == EventQueue::KERNEL_EVFILT_HRTIMER && events[0].udata == user_data,
|
||||||
|
"shared timer scheduling delivers high resolution event");
|
||||||
|
Check(EventQueue::KernelDeleteHRTimerEvent(queue, 10) == KERNEL_ERROR_ENOENT,
|
||||||
|
"high resolution timer is removed after one delivery");
|
||||||
|
Check(EventQueue::KernelDeleteEqueue(queue) == OK, "delete timer queue");
|
||||||
|
}
|
||||||
|
|
||||||
void CountDeletedEvent(EventQueue::KernelEqueue, EventQueue::KernelEqueueEvent* event) {
|
void CountDeletedEvent(EventQueue::KernelEqueue, EventQueue::KernelEqueueEvent* event) {
|
||||||
auto* count = static_cast<std::atomic_uint32_t*>(event->filter.data);
|
auto* count = static_cast<std::atomic_uint32_t*>(event->filter.data);
|
||||||
count->fetch_add(1, std::memory_order_relaxed);
|
count->fetch_add(1, std::memory_order_relaxed);
|
||||||
@@ -431,6 +495,7 @@ void TestConcurrentDelete() {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
TestPeriodicTimerEvents();
|
||||||
TestDuplicateAddPreservesEventState();
|
TestDuplicateAddPreservesEventState();
|
||||||
TestCallbackStateOutlivesPort();
|
TestCallbackStateOutlivesPort();
|
||||||
TestCallbackOwnsPayload();
|
TestCallbackOwnsPayload();
|
||||||
|
|||||||
Reference in New Issue
Block a user