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/LocalClient.cpp
|
||||||
src/IPC.cpp
|
src/IPC.cpp
|
||||||
src/SharedMemory.cpp
|
src/SharedMemory.cpp
|
||||||
|
src/Timer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(os ${SOURCES})
|
add_library(os ${SOURCES})
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
* @author apio (cloudapio.eu)
|
* @author apio (cloudapio.eu)
|
||||||
* @brief Base class for event-driven applications.
|
* @brief Base class for event-driven applications.
|
||||||
*
|
*
|
||||||
* @copyright Copyright (c) 2023, the Luna authors.
|
* @copyright Copyright (c) 2023-2024, the Luna authors.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <luna/HashMap.h>
|
#include <luna/HashMap.h>
|
||||||
#include <luna/LinkedList.h>
|
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
#include <os/Action.h>
|
#include <os/Action.h>
|
||||||
|
#include <os/Timer.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
|
||||||
namespace os
|
namespace os
|
||||||
@ -61,15 +61,6 @@ namespace os
|
|||||||
*/
|
*/
|
||||||
Result<void> register_signal_handler(int sig, void (*handler)(int));
|
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.
|
* @brief Run the event loop until it is asked to quit.
|
||||||
*
|
*
|
||||||
@ -98,12 +89,8 @@ namespace os
|
|||||||
|
|
||||||
int m_signal_pipe[2];
|
int m_signal_pipe[2];
|
||||||
|
|
||||||
struct Timer final : public LinkedListNode<Timer>
|
LinkedList<Timer> m_timer_list;
|
||||||
{
|
|
||||||
long target;
|
|
||||||
Action action;
|
|
||||||
};
|
|
||||||
|
|
||||||
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)
|
* @author apio (cloudapio.eu)
|
||||||
* @brief Base class for event-driven applications.
|
* @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 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
|
namespace os
|
||||||
{
|
{
|
||||||
EventLoop::EventLoop()
|
EventLoop::EventLoop()
|
||||||
@ -78,40 +63,6 @@ namespace os
|
|||||||
return {};
|
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()
|
int EventLoop::run()
|
||||||
{
|
{
|
||||||
while (!m_should_quit)
|
while (!m_should_quit)
|
||||||
@ -168,19 +119,7 @@ namespace os
|
|||||||
|
|
||||||
void EventLoop::handle_timer_signal(int)
|
void EventLoop::handle_timer_signal(int)
|
||||||
{
|
{
|
||||||
auto& queue = the().m_timer_queue;
|
auto& queue = the().m_timer_list;
|
||||||
if (queue.count())
|
queue.delayed_for_each([](Timer* t) { t->check_if_should_invoke_action(); });
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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…
x
Reference in New Issue
Block a user