libos: Move timer handling to a separate class and use POSIX timers
This commit is contained in:
parent
3231a1296d
commit
a9c339939a
@ -19,6 +19,7 @@ set(SOURCES
|
||||
src/LocalClient.cpp
|
||||
src/IPC.cpp
|
||||
src/SharedMemory.cpp
|
||||
src/Timer.cpp
|
||||
)
|
||||
|
||||
add_library(os ${SOURCES})
|
||||
|
@ -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
39
libos/include/os/Timer.h
Normal 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;
|
||||
};
|
||||
}
|
@ -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
86
libos/src/Timer.cpp
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user