#pragma once #include #include #include #include #include #include "src/Exceptions/Exception.hpp" namespace Bloom { /** * The EventNotifier class provides a means to interrupt a thread that is blocked by an IO call. * * It's implementation is rather simple: It uses a Linux event file descriptor (sys/eventfd.h), which should be used * along side epoll() to interrupt a blocking call that is waiting on the epoll file descriptor. * * The EventListener can hold an instance to EventNotifier, where it will invoke EventNotifier::notify() everytime * a new event is registered on the listener. * * @TODO: This could do with some cleaning. It's a bit hacky. Also, maybe add the ability to register the event * file descriptor to an epoll instance within a public method (instead of relying on the caller to do this * themselves via EventNotifier::getFileDescriptor()). */ class EventNotifier { private: int fileDescriptor = -1; std::atomic initialised = false; void init() { this->fileDescriptor = ::eventfd(0, EFD_NONBLOCK); if (this->fileDescriptor < -1) { throw Exceptions::Exception("Failed to create new eventfd object - error number: " + std::to_string(errno)); } this->initialised = true; } void close() { ::close(this->fileDescriptor); this->initialised = false; } public: EventNotifier() { this->init(); }; ~EventNotifier() { if (this->initialised) { this->close(); } } int getFileDescriptor() const { return this->fileDescriptor; } bool isInitialised() { return this->initialised; } void notify() { if (::eventfd_write(this->fileDescriptor, 1) < 0) { throw Exceptions::Exception("Failed to increment eventfd counter - error number: " + std::to_string(errno)); } } void clear() { eventfd_t counter; if (::eventfd_read(this->fileDescriptor, &counter) < 0 && errno != EAGAIN) { throw Exceptions::Exception("Failed to clear EventNotifier object - eventfd_read failed - " "error number: " + std::to_string(errno)); } } }; }