From 0824ba7e23a92180cb203445bdff0fc63b776b9b Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 9 Oct 2023 22:00:15 +0200 Subject: [PATCH] libos: Add timers to event loops Only second precision for now, as alarm() is used to control the timers. Hopefully setitimer() or timer_create() can be added to the kernel soon to benefit from more precision. --- libos/include/os/EventLoop.h | 24 +++++++++++++++++ libos/src/EventLoop.cpp | 51 ++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/libos/include/os/EventLoop.h b/libos/include/os/EventLoop.h index e48b9ff8..ea56c2b4 100644 --- a/libos/include/os/EventLoop.h +++ b/libos/include/os/EventLoop.h @@ -9,7 +9,9 @@ #pragma once #include +#include #include +#include #include namespace os @@ -52,9 +54,22 @@ namespace os * * Unlike standard POSIX signal handling, this handler will be run synchronously as part of the event loop, * eliminating many problems with asynchronous signal handling. + * + * @param sig The signal to handle. + * @param handler The function to call when this signal is caught. + * @return Result Whether the operation succeeded. */ Result register_signal_handler(int sig, void (*handler)(int)); + /** + * @brief Register a function to be called in a certain amount of time. + * + * @param seconds The number of seconds to wait before invoking the function. + * @param callback The function to call when the time is up. + * @return Result Whether the operation succeeded. + */ + Result register_timer(unsigned int seconds, Action&& callback); + /** * @brief Run the event loop until it is asked to quit. * @@ -76,10 +91,19 @@ namespace os static void handle_signal(int sig); static void handle_quit_signal(int); + static void handle_timer_signal(int); bool m_should_quit { false }; int m_quit_status { 0 }; int m_signal_pipe[2]; + + struct Timer final : public LinkedListNode + { + long target; + Action action; + }; + + LinkedList m_timer_queue; }; } diff --git a/libos/src/EventLoop.cpp b/libos/src/EventLoop.cpp index 43655f8c..5fe31d5d 100644 --- a/libos/src/EventLoop.cpp +++ b/libos/src/EventLoop.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include static os::EventLoop* s_the = nullptr; @@ -28,6 +29,7 @@ namespace os register_signal_handler(SIGTERM, EventLoop::handle_quit_signal).release_value(); register_signal_handler(SIGINT, EventLoop::handle_quit_signal).release_value(); + register_signal_handler(SIGALRM, EventLoop::handle_timer_signal).release_value(); } EventLoop::~EventLoop() @@ -60,6 +62,40 @@ namespace os return {}; } + Result EventLoop::register_timer(unsigned int seconds, Action&& callback) + { + long target = time(NULL) + seconds; + + auto* timer = TRY(make()); + timer->target = target; + timer->action = move(callback); + + Timer* next = nullptr; + + for (auto* t : m_timer_queue) + { + if (target < t->target) + { + next = t; + break; + } + } + + if (m_timer_queue.first().value_or(nullptr) == next) + { + alarm(seconds); + m_timer_queue.prepend(timer); + } + else if (next == nullptr) { m_timer_queue.append(timer); } + else + { + auto previous = m_timer_queue.previous(next).value(); + m_timer_queue.add_after(previous, timer); + } + + return {}; + } + int EventLoop::run() { while (!m_should_quit) @@ -113,4 +149,19 @@ namespace os os::println("EventLoop: quit requested by signal %d", sig); the().quit(sig); } + + void EventLoop::handle_timer_signal(int) + { + auto& queue = the().m_timer_queue; + if (queue.count()) + { + auto timer = queue.remove(queue.expect_first()); + + auto first = queue.first(); + if (first.has_value()) { alarm((unsigned int)(first.value()->target - time(NULL))); } + + timer->action(); + delete timer; + } + } }