/** * @file EventLoop.cpp * @author apio (cloudapio.eu) * @brief Base class for event-driven applications. * * @copyright Copyright (c) 2023, the Luna authors. * */ #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(); } EventLoop::~EventLoop() { s_the = nullptr; } EventLoop& EventLoop::the() { check(s_the); return *s_the; } Result 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 {}; } Result 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 {}; } 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()(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()(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); } }