]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#4141] implemented poll event handler
authorRazvan Becheriu <razvan@isc.org>
Thu, 30 Oct 2025 21:18:00 +0000 (23:18 +0200)
committerRazvan Becheriu <razvan@isc.org>
Fri, 21 Nov 2025 13:02:43 +0000 (13:02 +0000)
src/lib/util/fd_event_handler.h
src/lib/util/fd_event_handler_factory.cc
src/lib/util/meson.build
src/lib/util/poll_event_handler.cc [new file with mode: 0644]
src/lib/util/poll_event_handler.h [new file with mode: 0644]
src/lib/util/tests/fd_event_handler_factory_unittests.cc
src/lib/util/tests/fd_event_handler_unittests.h
src/lib/util/tests/meson.build
src/lib/util/tests/poll_event_handler_unittests.cc [new file with mode: 0644]
src/lib/util/tests/watch_socket_unittests.cc

index 73d2425dd22a2c7c8bf026c7458d74d59d6cf0cf..0d2d186ae718f2c652e6afb11a003e4a4a6b23e6 100644 (file)
@@ -22,6 +22,7 @@ public:
         TYPE_SELECT = 1,
         TYPE_EPOLL = 2, // Linux OS (Linux like OS) only
         TYPE_KQUEUE = 3, // BSD OS (BSD like OS) only
+        TYPE_POLL = 4,
     };
 
     /// @brief Constructor.
index 91a5b99818eafffa1b355bd766dde13a35413573..5d68a81964184b933d9f514db55dfb89433fb5e0 100644 (file)
@@ -7,6 +7,7 @@
 #include <config.h>
 
 #include <util/fd_event_handler_factory.h>
+#include <util/poll_event_handler.h>
 #include <util/select_event_handler.h>
 
 namespace isc {
@@ -14,7 +15,7 @@ namespace util {
 
 FDEventHandlerPtr FDEventHandlerFactory::factoryFDEventHandler() {
     // todo: use configuration to initialize the FDEventHandler.
-    return (FDEventHandlerPtr(new SelectEventHandler()));
+    return (FDEventHandlerPtr(new PollEventHandler()));
 }
 
 } // end of namespace isc::util
index 0a90402e0894a1ce36dad178a06c0deb9f43fc99..8131a7f13fd1d14c522e0e98c3598b1e4f05c1b4 100644 (file)
@@ -13,6 +13,7 @@ kea_util_lib = shared_library(
     'memory_segment_local.cc',
     'multi_threading_mgr.cc',
     'pid_file.cc',
+    'poll_event_handler.cc',
     'ready_check.cc',
     'reconnect_ctl.cc',
     'select_event_handler.cc',
@@ -61,6 +62,7 @@ kea_util_headers = [
     'optional.h',
     'pid_file.h',
     'pointer_util.h',
+    'poll_event_handler.h',
     'range_utilities.h',
     'readwrite_mutex.h',
     'ready_check.h',
diff --git a/src/lib/util/poll_event_handler.cc b/src/lib/util/poll_event_handler.cc
new file mode 100644 (file)
index 0000000..9aa2ee6
--- /dev/null
@@ -0,0 +1,71 @@
+// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <util/poll_event_handler.h>
+
+namespace isc {
+namespace util {
+
+PollEventHandler::PollEventHandler() : FDEventHandler(TYPE_POLL) {
+    clear();
+}
+
+void PollEventHandler::add(int fd, bool read /* = true */, bool write /* = false */) {
+    if (fd < 0) {
+        isc_throw(BadValue, "invalid negative value for fd");
+    }
+    struct pollfd data;
+    memset(&data, 0, sizeof(data));
+    data.fd = fd;
+    if (read) {
+        // Add this socket to read events
+        data.events |= POLLIN;
+    }
+    if (write) {
+        // Add this socket to write events
+        data.events |= POLLOUT;
+    }
+    data_.push_back(data);
+}
+
+int PollEventHandler::waitEvent(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
+    // Sanity check for microsecond timeout.
+    if (timeout_usec >= 1000000) {
+        isc_throw(BadValue, "fractional timeout must be shorter than"
+                  " one million microseconds");
+    }
+    int timeout = timeout_sec * 1000 + timeout_usec / 1000;
+    map_.clear();
+    for (size_t i = 0; i < data_.size(); ++i) {
+        map_[data_[i].fd] = &data_[i];
+    }
+    return (poll(data_.data(), data_.size(), timeout));
+}
+
+bool PollEventHandler::readReady(int fd) {
+    if (map_.find(fd) == map_.end()) {
+        return (false);
+    }
+    return (map_[fd]->revents & POLLIN);
+}
+
+bool PollEventHandler::writeReady(int fd) {
+    if (map_.find(fd) == map_.end()) {
+        return (false);
+    }
+    return (map_[fd]->revents & POLLOUT);
+}
+
+void PollEventHandler::clear() {
+    data_.clear();
+    map_.clear();
+}
+
+} // end of namespace isc::util
+} // end of namespace isc
diff --git a/src/lib/util/poll_event_handler.h b/src/lib/util/poll_event_handler.h
new file mode 100644 (file)
index 0000000..c9408f6
--- /dev/null
@@ -0,0 +1,72 @@
+// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef POLL_EVENT_HANDLER_H
+#define POLL_EVENT_HANDLER_H
+
+#include <util/fd_event_handler.h>
+
+#include <sys/poll.h>
+
+namespace isc {
+namespace util {
+
+/// @brief File descriptor event handler class handles events for registered
+/// file descriptors. This class uses the OS select syscall for event handling.
+class PollEventHandler : public FDEventHandler {
+public:
+    /// @brief Constructor.
+    PollEventHandler();
+
+    /// @brief Destructor.
+    virtual ~PollEventHandler() = default;
+
+    /// @brief Add file descriptor to watch for events.
+    ///
+    /// @param fd The file descriptor.
+    /// @param read The flag indicating if the file descriptor should be
+    /// registered for read ready events.
+    /// @param write The flag indicating if the file descriptor should be
+    /// registered for write ready events.
+    void add(int fd, bool read = true, bool write = false);
+
+    /// @brief Wait for events on registered file descriptors.
+    ///
+    /// @param timeout_sec The wait timeout in seconds.
+    /// @param timeout_usec The wait timeout in micro seconds
+    /// @return -1 on error, 0 if no data is available (timeout expired),
+    /// 1 if data is ready.
+    int waitEvent(uint32_t timeout_sec, uint32_t timeout_usec = 0);
+
+    /// @brief Check if file descriptor is ready for read operation.
+    ///
+    /// @param fd The file descriptor.
+    ///
+    /// @return True if file descriptor is ready for reading.
+    bool readReady(int fd);
+
+    /// @brief Check if file descriptor is ready for write operation.
+    ///
+    /// @param fd The file descriptor.
+    ///
+    /// @return True if file descriptor is ready for writing.
+    bool writeReady(int fd);
+
+    /// @brief Clear registered file descriptors.
+    void clear();
+
+private:
+    /// @brief The poll file descriptors data.
+    std::vector<struct pollfd> data_;
+
+    /// @brief The map with file descriptor to data reference.
+    std::unordered_map<int, struct pollfd*> map_;
+};
+
+}  // namespace isc::util;
+}  // namespace isc
+
+#endif  // POLL_EVENT_HANDLER_H
index 40405f8c44da1ee4bf3bb6bb3f474a2f187f73e2..0c01b38fe7338db387c5bcb18e9e8be1f2c30e83 100644 (file)
@@ -21,8 +21,9 @@ namespace {
 // and process passed parameters.
 TEST(FDEventHandlerFactory, factory) {
     FDEventHandlerPtr handler = FDEventHandlerFactory::factoryFDEventHandler();
-    EXPECT_EQ(handler->type(), FDEventHandler::TYPE_SELECT);
-    EXPECT_THROW(handler->add(FD_SETSIZE), BadValue);
+    if (handler->type() == FDEventHandler::TYPE_SELECT) {
+        EXPECT_THROW(handler->add(FD_SETSIZE), BadValue);
+    }
 }
 
 } // end of anonymous namespace
index 364f9d505eaf1f175f1f8b9d12257e2aecb93467..5b468f77832a2d5b682cde976a9191759b5de580 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <exceptions/exceptions.h>
 #include <util/fd_event_handler.h>
+#include <util/poll_event_handler.h>
 #include <util/select_event_handler.h>
 
 #include <gtest/gtest.h>
index 9dd8f69097095e5a2b4db799fc61b67e5aa646c4..964dafc945eff16bdd1554a03d5d86f05efd7e4d 100644 (file)
@@ -23,6 +23,7 @@ kea_util_tests = executable(
     'multi_threading_mgr_unittest.cc',
     'optional_unittest.cc',
     'pid_file_unittest.cc',
+    'poll_event_handler_unittests.cc',
     'range_utilities_unittest.cc',
     'readwrite_mutex_unittest.cc',
     'run_unittests.cc',
diff --git a/src/lib/util/tests/poll_event_handler_unittests.cc b/src/lib/util/tests/poll_event_handler_unittests.cc
new file mode 100644 (file)
index 0000000..0d3ddff
--- /dev/null
@@ -0,0 +1,12 @@
+// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#define FDEventHandlerType PollEventHandler
+#define FDEventHandlerTest PollEventHandlerTest
+
+#include <fd_event_handler_unittests.h>
index b19096c56894a4b6a3e9644e6ffa6917bd9eaa00..9dff44b2347ec10c3e4f21cb8d4c19325aa6da8f 100644 (file)
@@ -5,6 +5,7 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 #include <config.h>
 
+#include <util/fd_event_handler_factory.h>
 #include <util/ready_check.h>
 #include <util/watch_socket.h>
 
@@ -131,7 +132,11 @@ TEST(WatchSocketTest, closedWhileReady) {
     ASSERT_NO_THROW(watch->clearReady());
 
     // Verify the select_fd fails as socket is invalid/closed.
-    EXPECT_EQ(-1, selectCheck(select_fd));
+    if (FDEventHandlerFactory::factoryFDEventHandler()->type() == FDEventHandler::TYPE_SELECT) {
+        EXPECT_EQ(-1, selectCheck(select_fd));
+    } else {
+        EXPECT_EQ(1, selectCheck(select_fd));
+    }
 
     // Verify that subsequent attempts to mark it will fail.
     ASSERT_THROW(watch->markReady(), WatchSocketError);
@@ -202,7 +207,11 @@ TEST(WatchSocketTest, badReadOnClear) {
 
     // Verify the select_fd does not evaluate to ready.
     EXPECT_FALSE(watch->isReady());
-    EXPECT_NE(1, selectCheck(select_fd));
+    if (FDEventHandlerFactory::factoryFDEventHandler()->type() == FDEventHandler::TYPE_SELECT) {
+        EXPECT_NE(1, selectCheck(select_fd));
+    } else {
+        EXPECT_EQ(1, selectCheck(select_fd));
+    }
 
     // Verify that getSelectFd() returns INVALID.
     ASSERT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());