/** * @file EventLoop.cpp * @author apio (cloudapio.eu) * @brief Base class for event-driven applications. * * @copyright Copyright (c) 2023-2024, the Luna authors. * */ #include #include #include #include #include #include #include #include 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 EventLoop::register_fd_listener(int fd, Function listener) { TRY(m_fd_listeners.try_set(fd, move(listener))); TRY(m_pollfds.try_append({ .fd = fd, .events = POLLIN, .revents = 0 })); return {}; } Result EventLoop::register_signal_handler(int sig, Function 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(); }); } }