From 92c22d60ef9f53373c8fbdccb7ab597446733e1b Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 3 Oct 2026 16:07:22 +0400 Subject: [PATCH] Support periodic kernel timers through shared event queue deadlines --- src/kernel/eventQueue.cpp | 38 ++++++++++++------ src/kernel/eventQueue.h | 3 ++ src/libs/libKernel.cpp | 1 + tests/EventQueueLifetimeTests.cpp | 65 +++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 12 deletions(-) diff --git a/src/kernel/eventQueue.cpp b/src/kernel/eventQueue.cpp index 14561b045..010a1f072 100644 --- a/src/kernel/eventQueue.cpp +++ b/src/kernel/eventQueue.cpp @@ -138,7 +138,13 @@ int KernelEqueuePrivate::GetTriggeredEvents(KernelEvent* ev, int num) { void KernelEqueuePrivate::TriggerExpiredTimers(uint64_t now_ns) { 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(count); + event.deadline_ns += count * event.interval_ns; + } event.triggered = true; } } @@ -209,7 +215,9 @@ int KernelEqueuePrivate::AddEvent(const KernelEqueueEvent& event) { return e.event.ident == ident && e.event.filter == filter; }); if (it != m_events.end()) { + TriggerExpiredTimers(MonotonicTimeNs()); it->deadline_ns = event.deadline_ns; + it->interval_ns = event.interval_ns; it->event.udata = event.event.udata; for (auto& pending: it->pending_events) { pending.udata = event.event.udata; @@ -511,6 +519,22 @@ int KYTY_SYSV_ABI KernelDeleteUserEvent(KernelEqueue eq, int id) { return KernelDeleteEvent(eq, static_cast(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(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(usec) * 1000, true, udata); +} + int KYTY_SYSV_ABI KernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTimespec* ts, void* udata) { if (ts == nullptr) { @@ -524,17 +548,7 @@ int KYTY_SYSV_ABI KernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTim const auto delay_ns = static_cast(ts->tv_sec) * 1000000000ull + static_cast(ts->tv_nsec); - const auto now_ns = MonotonicTimeNs(); - - KernelEqueueEvent event {}; - event.deadline_ns = delay_ns <= UINT64_MAX - now_ns ? now_ns + delay_ns : UINT64_MAX; - event.event.ident = static_cast(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); + return AddTimerEvent(eq, id, delay_ns, false, udata); } int KYTY_SYSV_ABI KernelDeleteHRTimerEvent(KernelEqueue eq, int id) { diff --git a/src/kernel/eventQueue.h b/src/kernel/eventQueue.h index 765d72fd4..ca724cd71 100644 --- a/src/kernel/eventQueue.h +++ b/src/kernel/eventQueue.h @@ -51,6 +51,7 @@ struct KernelFilter { struct KernelEqueueEvent { bool triggered = false; uint64_t deadline_ns = 0; + uint64_t interval_ns = 0; KernelEvent event; KernelFilter filter; std::deque 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 KernelTriggerUserEventForAll(int id, void* udata); 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, void* udata); int KYTY_SYSV_ABI KernelDeleteHRTimerEvent(KernelEqueue eq, int id); diff --git a/src/libs/libKernel.cpp b/src/libs/libKernel.cpp index bfa5a0f70..3a41bdc43 100644 --- a/src/libs/libKernel.cpp +++ b/src/libs/libKernel.cpp @@ -3148,6 +3148,7 @@ LIB_DEFINE(InitLibKernel_1_Equeue) { LIB_FUNC("WDszmSbWuDk", EventQueue::KernelAddUserEventEdge); LIB_FUNC("F6e0kwo4cnk", EventQueue::KernelTriggerUserEvent); LIB_FUNC("LJDwdSNTnDg", EventQueue::KernelDeleteUserEvent); + LIB_FUNC("57ZK+ODEXWY", EventQueue::KernelAddTimerEvent); LIB_FUNC("R74tt43xP6k", EventQueue::KernelAddHRTimerEvent); LIB_FUNC("J+LF6LwObXU", EventQueue::KernelDeleteHRTimerEvent); LIB_FUNC("bBfz7kMF2Ho", EventQueue::KernelAddAmprEvent); diff --git a/tests/EventQueueLifetimeTests.cpp b/tests/EventQueueLifetimeTests.cpp index cae2b641c..430333da2 100644 --- a/tests/EventQueueLifetimeTests.cpp +++ b/tests/EventQueueLifetimeTests.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,69 @@ void CheckConcurrentResult(int result, const char* 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(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(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) { auto* count = static_cast(event->filter.data); count->fetch_add(1, std::memory_order_relaxed); @@ -431,6 +495,7 @@ void TestConcurrentDelete() { } // namespace int main() { + TestPeriodicTimerEvents(); TestDuplicateAddPreservesEventState(); TestCallbackStateOutlivesPort(); TestCallbackOwnsPayload();