From 2b55f8f5eafdb17fa606c8f572de48e35b74e425 Mon Sep 17 00:00:00 2001 From: Nav Date: Mon, 28 Mar 2022 01:02:52 +0100 Subject: [PATCH] New RAII wrapper for Linux epoll instances --- CMakeLists.txt | 1 + src/Helpers/EpollInstance.cpp | 77 +++++++++++++++++++++++++++++++++++ src/Helpers/EpollInstance.hpp | 70 +++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 src/Helpers/EpollInstance.cpp create mode 100644 src/Helpers/EpollInstance.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cb43fb70..9e1ffd87 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,7 @@ add_executable(Bloom # Helpers & other src/Logger/Logger.cpp src/Helpers/Paths.cpp + src/Helpers/EpollInstance.cpp src/VersionNumber.cpp src/Generated/resources.cpp diff --git a/src/Helpers/EpollInstance.cpp b/src/Helpers/EpollInstance.cpp new file mode 100644 index 00000000..d2adea14 --- /dev/null +++ b/src/Helpers/EpollInstance.cpp @@ -0,0 +1,77 @@ +#include "EpollInstance.hpp" + +#include +#include +#include +#include + +#include "src/Exceptions/Exception.hpp" + +namespace Bloom +{ + using Exceptions::Exception; + + EpollInstance::EpollInstance() { + this->fileDescriptor = ::epoll_create(1); + + if (this->fileDescriptor < 0) { + throw Exception( + "Failed to create epoll instance - error number " + std::to_string(errno) + + " returned." + ); + } + } + + void EpollInstance::addEntry(int fileDescriptor, std::uint16_t eventMask) { + struct epoll_event event = { + .events = eventMask, + .data = { + .fd = fileDescriptor + } + }; + + if (::epoll_ctl(this->fileDescriptor.value(), EPOLL_CTL_ADD, fileDescriptor, &event) != 0) { + throw Exception( + "Failed to add entry to epoll instance - error number " + std::to_string(errno) + " returned." + ); + } + } + + void EpollInstance::removeEntry(int fileDescriptor) { + if (::epoll_ctl(this->fileDescriptor.value(), EPOLL_CTL_DEL, fileDescriptor, NULL) != 0) { + throw Exception( + "Failed to remove entry from epoll instance - error number " + std::to_string(errno) + + " returned." + ); + } + } + + std::optional EpollInstance::waitForEvent(std::optional timeout) { + std::array events = {}; + + const auto eventCount = ::epoll_wait( + this->fileDescriptor.value(), + events.data(), + 1, + timeout.has_value() ? static_cast(timeout->count()) : -1 + ); + + if (eventCount < 1) { + return std::nullopt; + } + + return static_cast(events.at(0).data.fd); + } + + EpollInstance::EpollInstance(EpollInstance&& other) noexcept + : fileDescriptor(other.fileDescriptor) + { + other.fileDescriptor = std::nullopt; + } + + EpollInstance::~EpollInstance() noexcept { + if (this->fileDescriptor.value_or(-1) >= 0) { + ::close(this->fileDescriptor.value()); + } + } +} diff --git a/src/Helpers/EpollInstance.hpp b/src/Helpers/EpollInstance.hpp new file mode 100644 index 00000000..278a1678 --- /dev/null +++ b/src/Helpers/EpollInstance.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace Bloom +{ + enum class EpollEvent: std::uint16_t + { + READ_READY = EPOLL_EVENTS::EPOLLIN, + WRITE_READY = EPOLL_EVENTS::EPOLLOUT, + }; + + /** + * The EpollInstance class is an RAII wrapper for a single Linux epoll instance. + * + * See https://man7.org/linux/man-pages/man7/epoll.7.html for more on the Linux epoll API. + */ + class EpollInstance + { + public: + EpollInstance(); + + /** + * Adds an entry to the epoll instance. + * + * @param fileDescriptor + * @param eventMask + */ + void addEntry(int fileDescriptor, std::uint16_t eventMask); + + /** + * Removes an entry from the epoll instance. + * + * @param fileDescriptor + */ + void removeEntry(int fileDescriptor); + + /** + * Waits on the epoll instance until an event occurs for any of the registered files. + * + * @param timeout + * Millisecond timeout. If not provided, no timeout will be applied and this function will block until an + * event occurs. + * + * @return + * The file descriptor of the file for which the event occurred, or std::nullopt if a timeout was reached. + */ + std::optional waitForEvent(std::optional timeout = std::nullopt); + + /* + * EpollInstance objects should not be copied. + */ + EpollInstance(EpollInstance& other) = delete; + EpollInstance& operator = (EpollInstance& other) = delete; + + /* + * TODO: Implement this. For now, use the move constructor. + */ + EpollInstance& operator = (EpollInstance&& other) = delete; + + EpollInstance(EpollInstance&& other) noexcept; + ~EpollInstance() noexcept; + + private: + std::optional fileDescriptor; + }; +}