// Copyright 2026 Amber Hu #include "EventManager.h" #include #include EventManager::EventManager() { // Create a set to populate with file descriptors we want to wait on // The number passed in does not matter, see man epoll_create epoll_fd_ = epoll_create(42); } void EventManager::RegisterCallback(int fd, CallbackFn callback) { struct CallbackData cb_data; cb_data.callback = callback; cb_data.fd = fd; this->active_list_.push_front(cb_data); struct epoll_event event_data; event_data.data.ptr = &(this->active_list_.front()); event_data.events = EPOLLIN; epoll_ctl(this->epoll_fd_, EPOLL_CTL_ADD, fd, &event_data); } void EventManager::RemoveCallback(int fd) { epoll_ctl(this->epoll_fd_, EPOLL_CTL_DEL, fd, NULL); // This uses an anonymous function, similar to lambda x: x == y in Python this->active_list_.remove_if( [fd](const CallbackData& curr){ return curr.fd == fd;}); } void EventManager::WaitAndDispatch() { struct epoll_event res; epoll_wait(this->epoll_fd_, &res, 1, -1); // Timeout is -1 (no timeout) // Retrieve the data we tucked away when we registered the callback struct CallbackData* cb_data = reinterpret_cast(res.data.ptr); // That CallbackData told us which fn to invoke and which fd to pass in cb_data->callback(cb_data->fd); } EventManager::~EventManager() { // Close all open file descriptors before shutting down for (auto curr : this->active_list_) { std::cout << "Closing fd " << curr.fd << std::endl; close(curr.fd); } close(this->epoll_fd_); }