2023-10-06 20:06:34 +00:00
|
|
|
/**
|
|
|
|
* @file EventLoop.cpp
|
|
|
|
* @author apio (cloudapio.eu)
|
|
|
|
* @brief Base class for event-driven applications.
|
|
|
|
*
|
|
|
|
* @copyright Copyright (c) 2023, the Luna authors.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <os/EventLoop.h>
|
|
|
|
#include <os/File.h>
|
2023-10-07 12:26:35 +00:00
|
|
|
#include <signal.h>
|
2023-10-06 20:06:34 +00:00
|
|
|
#include <string.h>
|
2023-11-16 20:58:45 +00:00
|
|
|
#include <sys/time.h>
|
2023-10-09 20:00:15 +00:00
|
|
|
#include <time.h>
|
2023-10-07 12:26:35 +00:00
|
|
|
#include <unistd.h>
|
2023-10-06 20:06:34 +00:00
|
|
|
|
|
|
|
static os::EventLoop* s_the = nullptr;
|
|
|
|
|
2023-11-16 20:58:45 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-10-06 20:06:34 +00:00
|
|
|
namespace os
|
|
|
|
{
|
|
|
|
EventLoop::EventLoop()
|
|
|
|
{
|
|
|
|
s_the = this;
|
2023-10-07 12:26:35 +00:00
|
|
|
|
|
|
|
// Set up synchronous signal handling.
|
|
|
|
check(pipe(m_signal_pipe) == 0);
|
|
|
|
m_pollfds.try_append({ .fd = m_signal_pipe[0], .events = POLLIN, .revents = 0 }).release_value();
|
|
|
|
|
|
|
|
register_signal_handler(SIGTERM, EventLoop::handle_quit_signal).release_value();
|
|
|
|
register_signal_handler(SIGINT, EventLoop::handle_quit_signal).release_value();
|
2023-10-09 20:00:15 +00:00
|
|
|
register_signal_handler(SIGALRM, EventLoop::handle_timer_signal).release_value();
|
2023-10-06 20:06:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EventLoop::~EventLoop()
|
|
|
|
{
|
|
|
|
s_the = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
EventLoop& EventLoop::the()
|
|
|
|
{
|
|
|
|
check(s_the);
|
|
|
|
return *s_the;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result<void> EventLoop::register_fd_listener(int fd, void (*listener)(int, int))
|
|
|
|
{
|
|
|
|
TRY(m_fd_listeners.try_set(fd, listener));
|
|
|
|
TRY(m_pollfds.try_append({ .fd = fd, .events = POLLIN, .revents = 0 }));
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-10-07 12:26:35 +00:00
|
|
|
Result<void> EventLoop::register_signal_handler(int sig, void (*handler)(int))
|
|
|
|
{
|
|
|
|
struct sigaction sa;
|
|
|
|
sa.sa_handler = EventLoop::handle_signal;
|
|
|
|
sa.sa_flags = 0;
|
|
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
check(sigaction(sig, &sa, nullptr) == 0);
|
|
|
|
|
|
|
|
TRY(m_signal_handlers.try_set(sig, handler));
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-11-16 20:58:45 +00:00
|
|
|
Result<void> EventLoop::register_timer(long milliseconds, Action&& callback)
|
2023-10-09 20:00:15 +00:00
|
|
|
{
|
2023-11-16 20:58:45 +00:00
|
|
|
long target = get_monotonic_time_in_milliseconds() + milliseconds;
|
2023-10-09 20:00:15 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2023-11-16 20:58:45 +00:00
|
|
|
alarm_milliseconds(milliseconds);
|
2023-10-09 20:00:15 +00:00
|
|
|
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 {};
|
|
|
|
}
|
|
|
|
|
2023-10-06 20:06:34 +00:00
|
|
|
int EventLoop::run()
|
|
|
|
{
|
|
|
|
while (!m_should_quit)
|
|
|
|
{
|
|
|
|
for (auto& pfd : m_pollfds) { pfd.revents = 0; }
|
|
|
|
|
|
|
|
int rc = poll(m_pollfds.data(), m_pollfds.size(), 1000);
|
|
|
|
if (!rc) continue;
|
|
|
|
if (rc < 0 && errno != EINTR)
|
|
|
|
{
|
|
|
|
os::println("poll: error: %s", strerror(errno));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-10-07 12:26:35 +00:00
|
|
|
if (m_pollfds[0].revents & POLLIN)
|
|
|
|
{
|
|
|
|
int sig;
|
|
|
|
read(m_signal_pipe[0], &sig, sizeof(int));
|
|
|
|
|
|
|
|
auto handler = m_signal_handlers.try_get(sig);
|
|
|
|
if (handler.has_value()) { handler.value()(sig); }
|
|
|
|
}
|
|
|
|
|
2023-10-06 20:06:34 +00:00
|
|
|
for (usize i = 0; i < m_fd_listeners.size(); i++)
|
|
|
|
{
|
2023-10-07 12:26:35 +00:00
|
|
|
auto& pfd = m_pollfds[i + 1];
|
|
|
|
if (pfd.revents)
|
|
|
|
{
|
|
|
|
auto handler = m_fd_listeners.try_get(pfd.fd);
|
|
|
|
if (handler.has_value()) handler.value()(pfd.fd, pfd.revents);
|
|
|
|
}
|
2023-10-06 20:06:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_quit_status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EventLoop::quit(int status)
|
|
|
|
{
|
|
|
|
m_quit_status = status;
|
|
|
|
m_should_quit = true;
|
|
|
|
}
|
2023-10-07 12:26:35 +00:00
|
|
|
|
|
|
|
void EventLoop::handle_signal(int sig)
|
|
|
|
{
|
|
|
|
write(the().m_signal_pipe[1], &sig, sizeof(int));
|
|
|
|
}
|
|
|
|
|
|
|
|
void EventLoop::handle_quit_signal(int sig)
|
|
|
|
{
|
|
|
|
os::println("EventLoop: quit requested by signal %d", sig);
|
|
|
|
the().quit(sig);
|
|
|
|
}
|
2023-10-09 20:00:15 +00:00
|
|
|
|
|
|
|
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();
|
2023-11-16 20:58:45 +00:00
|
|
|
if (first.has_value())
|
|
|
|
{
|
|
|
|
alarm((unsigned int)(first.value()->target - get_monotonic_time_in_milliseconds()));
|
|
|
|
}
|
2023-10-09 20:00:15 +00:00
|
|
|
|
|
|
|
timer->action();
|
|
|
|
delete timer;
|
|
|
|
}
|
|
|
|
}
|
2023-10-06 20:06:34 +00:00
|
|
|
}
|