Luna/libos/src/EventLoop.cpp

126 lines
3.3 KiB
C++

/**
* @file EventLoop.cpp
* @author apio (cloudapio.eu)
* @brief Base class for event-driven applications.
*
* @copyright Copyright (c) 2023-2024, the Luna authors.
*
*/
#include <errno.h>
#include <os/EventLoop.h>
#include <os/File.h>
#include <signal.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
static os::EventLoop* s_the = nullptr;
namespace os
{
EventLoop::EventLoop()
{
s_the = this;
// 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();
register_signal_handler(SIGALRM, EventLoop::handle_timer_signal).release_value();
}
EventLoop::~EventLoop()
{
s_the = nullptr;
}
EventLoop& EventLoop::the()
{
check(s_the);
return *s_the;
}
Result<void> EventLoop::register_fd_listener(int fd, Function<int, int> listener)
{
TRY(m_fd_listeners.try_set(fd, move(listener)));
TRY(m_pollfds.try_append({ .fd = fd, .events = POLLIN, .revents = 0 }));
return {};
}
Result<void> EventLoop::register_signal_handler(int sig, Function<int> handler)
{
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, move(handler)));
return {};
}
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;
}
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_ptr())(sig); }
}
for (usize i = 0; i < m_fd_listeners.size(); i++)
{
auto& pfd = m_pollfds[i + 1];
if (pfd.revents)
{
auto handler = m_fd_listeners.try_get(pfd.fd);
if (handler.has_value()) (*handler.value_ptr())(pfd.fd, pfd.revents);
}
}
}
return m_quit_status;
}
void EventLoop::quit(int status)
{
m_quit_status = status;
m_should_quit = true;
}
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);
}
void EventLoop::handle_timer_signal(int)
{
auto& queue = the().m_timer_list;
queue.delayed_for_each([](Timer* t) { t->check_if_should_invoke_action(); });
}
}