libos: Move timer handling to a separate class and use POSIX timers

This commit is contained in:
apio 2024-01-05 22:16:50 +01:00
parent 3231a1296d
commit a9c339939a
Signed by: apio
GPG Key ID: B8A7D06E42258954
5 changed files with 133 additions and 81 deletions

View File

@ -19,6 +19,7 @@ set(SOURCES
src/LocalClient.cpp
src/IPC.cpp
src/SharedMemory.cpp
src/Timer.cpp
)
add_library(os ${SOURCES})

View File

@ -3,15 +3,15 @@
* @author apio (cloudapio.eu)
* @brief Base class for event-driven applications.
*
* @copyright Copyright (c) 2023, the Luna authors.
* @copyright Copyright (c) 2023-2024, the Luna authors.
*
*/
#pragma once
#include <luna/HashMap.h>
#include <luna/LinkedList.h>
#include <luna/Vector.h>
#include <os/Action.h>
#include <os/Timer.h>
#include <sys/poll.h>
namespace os
@ -61,15 +61,6 @@ namespace os
*/
Result<void> register_signal_handler(int sig, void (*handler)(int));
/**
* @brief Register a function to be called in a certain amount of time.
*
* @param milliseconds The number of milliseconds to wait before invoking the function.
* @param callback The function to call when the time is up.
* @return Result<void> Whether the operation succeeded.
*/
Result<void> register_timer(long milliseconds, Action&& callback);
/**
* @brief Run the event loop until it is asked to quit.
*
@ -98,12 +89,8 @@ namespace os
int m_signal_pipe[2];
struct Timer final : public LinkedListNode<Timer>
{
long target;
Action action;
};
LinkedList<Timer> m_timer_list;
LinkedList<Timer> m_timer_queue;
friend class Timer;
};
}

39
libos/include/os/Timer.h Normal file
View File

@ -0,0 +1,39 @@
/**
* @file Timer.h
* @author apio (cloudapio.eu)
* @brief Millisecond-precision timer.
*
* @copyright Copyright (c) 2024, the Luna authors.
*
*/
#pragma once
#include <luna/LinkedList.h>
#include <luna/OwnedPtr.h>
#include <os/Action.h>
#include <time.h>
namespace os
{
class EventLoop;
class Timer : LinkedListNode<Timer>
{
public:
static Result<OwnedPtr<Timer>> create_singleshot(unsigned int milliseconds, Action&& action);
static Result<OwnedPtr<Timer>> create_repeating(unsigned int milliseconds, Action&& action);
~Timer();
private:
timer_t m_timerid { -1 };
bool m_repeating;
struct timespec m_interval;
Action m_action;
void check_if_should_invoke_action();
Timer() = default;
friend class EventLoop;
};
}

View File

@ -3,7 +3,7 @@
* @author apio (cloudapio.eu)
* @brief Base class for event-driven applications.
*
* @copyright Copyright (c) 2023, the Luna authors.
* @copyright Copyright (c) 2023-2024, the Luna authors.
*
*/
@ -18,21 +18,6 @@
static os::EventLoop* s_the = nullptr;
static void alarm_milliseconds(long target)
{
struct itimerval itimer;
memset(&itimer.it_interval, 0, sizeof(itimer.it_interval));
itimer.it_value = { .tv_sec = target / 1000, .tv_usec = (target % 1000) * 1000 };
setitimer(ITIMER_REAL, &itimer, nullptr);
}
static long get_monotonic_time_in_milliseconds()
{
struct timespec time;
clock_gettime(CLOCK_MONOTONIC, &time);
return time.tv_sec * 1000 + time.tv_nsec / 1'000'000;
}
namespace os
{
EventLoop::EventLoop()
@ -78,40 +63,6 @@ namespace os
return {};
}
Result<void> EventLoop::register_timer(long milliseconds, Action&& callback)
{
long target = get_monotonic_time_in_milliseconds() + milliseconds;
auto* timer = TRY(make<Timer>());
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_milliseconds(milliseconds);
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)
@ -168,19 +119,7 @@ namespace os
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 - get_monotonic_time_in_milliseconds()));
}
timer->action();
delete timer;
}
auto& queue = the().m_timer_list;
queue.delayed_for_each([](Timer* t) { t->check_if_should_invoke_action(); });
}
}

86
libos/src/Timer.cpp Normal file
View File

@ -0,0 +1,86 @@
/**
* @file Timer.cpp
* @author apio (cloudapio.eu)
* @brief Millisecond-precision timer.
*
* @copyright Copyright (c) 2024, the Luna authors.
*
*/
#include <errno.h>
#include <os/EventLoop.h>
#include <os/Timer.h>
namespace os
{
Result<OwnedPtr<Timer>> Timer::create_singleshot(unsigned int milliseconds, Action&& action)
{
auto timer = TRY(adopt_owned_if_nonnull(new (std::nothrow) Timer()));
int status = timer_create(CLOCK_REALTIME, nullptr, &timer->m_timerid);
if (status < 0) return err(errno);
struct timespec time = { .tv_sec = milliseconds / 1000, .tv_nsec = (milliseconds % 1000) * 1'000'000 };
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = time };
timer_settime(timer->m_timerid, 0, &itimer, nullptr);
timer->m_repeating = false;
timer->m_action = move(action);
EventLoop::the().m_timer_list.append(timer.ptr());
return timer;
}
Result<OwnedPtr<Timer>> Timer::create_repeating(unsigned int milliseconds, Action&& action)
{
auto timer = TRY(adopt_owned_if_nonnull(new (std::nothrow) Timer()));
int status = timer_create(CLOCK_REALTIME, nullptr, &timer->m_timerid);
if (status < 0) return err(errno);
struct timespec time = { .tv_sec = milliseconds / 1000, .tv_nsec = (milliseconds % 1000) * 1'000'000 };
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = time };
timer_settime(timer->m_timerid, 0, &itimer, nullptr);
timer->m_repeating = true;
timer->m_interval = time;
timer->m_action = move(action);
EventLoop::the().m_timer_list.append(timer.ptr());
return timer;
}
void Timer::check_if_should_invoke_action()
{
struct itimerspec remaining;
if (timer_gettime(m_timerid, &remaining) < 0) return;
if (!remaining.it_value.tv_sec && !remaining.it_value.tv_nsec)
{
// Timer expired!
m_action();
if (!m_repeating)
{
timer_delete(m_timerid);
EventLoop::the().m_timer_list.remove(this);
m_timerid = -1;
}
else
{
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = m_interval };
timer_settime(m_timerid, 0, &itimer, nullptr);
}
}
}
Timer::~Timer()
{
if (m_timerid >= 0) timer_delete(m_timerid);
}
}