#include <unistd.h>
int main() {
- int kq = kqueue();
- close(kq);
+ int kqueuefd = kqueue();
+ close(kqueuefd);
return 0;
}
PktFilter6TestStub::openSocket(const Iface&,
const isc::asiolink::IOAddress& addr,
const uint16_t port, const bool) {
- int pipefd[2];
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
- int ret = pipe(pipefd);
- if (ret < 0) {
+ if (fd < 0) {
const char* errmsg = strerror(errno);
isc_throw(Unexpected,
- "PktFilter6TestStub: cannot open pipe: " << errmsg);
+ "PktFilter6TestStub: cannot open socket: " << errmsg);
}
try {
}
} catch (...) {
// Don't leak fd on simulated errors.
- close(pipefd[0]);
- close(pipefd[1]);
+ close(fd);
throw;
}
- close(pipefd[1]);
-
- return (SocketInfo(addr, port, pipefd[0]));
+ return (SocketInfo(addr, port, fd));
}
Pkt6Ptr
PktFilterTestStub::openSocket(Iface&,
const isc::asiolink::IOAddress& addr,
const uint16_t port, const bool, const bool) {
- int pipefd[2];
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
- int ret = pipe(pipefd);
- if (ret < 0) {
+ if (fd < 0) {
const char* errmsg = strerror(errno);
isc_throw(Unexpected,
- "PktFilterTestStub: cannot open pipe: " << errmsg);
+ "PktFilterTestStub: cannot open socket: " << errmsg);
}
try {
}
} catch (...) {
// Don't leak fd on simulated errors.
- close(pipefd[0]);
- close(pipefd[1]);
+ close(fd);
throw;
}
- close(pipefd[1]);
-
- return (SocketInfo(addr, port, pipefd[0]));
+ return (SocketInfo(addr, port, fd));
}
Pkt4Ptr
#include <cstring>
+#include <unistd.h>
+
using namespace std;
namespace isc {
if (epollfd_ == -1) {
isc_throw(Unexpected, "error opening epoll: " << strerror(errno));
}
- if (pipe(pipefd)) {
+ if (pipe(pipefd_)) {
close(epollfd_);
isc_throw(Unexpected, "error opening internal epoll pipe: " << strerror(errno));
}
EPollEventHandler::~EPollEventHandler() {
close(epollfd_);
- close(pipefd[1]);
- close(pipefd[0]);
+ close(pipefd_[1]);
+ close(pipefd_[0]);
}
void EPollEventHandler::add(int fd, bool read /* = true */, bool write /* = false */) {
}
struct epoll_event dummy;
memset(&dummy, 0, sizeof(dummy));
- dummy.data.fd = pipefd[0];
+ dummy.data.fd = pipefd_[0];
dummy.events |= EPOLLIN;
epoll_ctl(epollfd_, EPOLL_CTL_ADD, dummy.data.fd, &dummy);
used_data_.push_back(dummy);
/// @brief The pipe used to permit calling @ref waitEvent with no
/// registered file descriptors.
- int pipefd[2];
+ int pipefd_[2];
};
} // namespace isc::util;
if (string(env_type) == string("select")) {
type = FDEventHandler::TYPE_SELECT;
}
- if (string(env_type) == string("poll")) {
- type = FDEventHandler::TYPE_POLL;
- }
if (string(env_type) == string("epoll")) {
type = FDEventHandler::TYPE_EPOLL;
}
+ if (string(env_type) == string("kqueue")) {
+ type = FDEventHandler::TYPE_KQUEUE;
+ }
+ if (string(env_type) == string("poll")) {
+ type = FDEventHandler::TYPE_POLL;
+ }
}
switch(type) {
case FDEventHandler::TYPE_SELECT:
--- /dev/null
+// 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/kqueue_event_handler.h>
+
+#include <cstring>
+
+#include <unistd.h>
+
+using namespace std;
+
+namespace isc {
+namespace util {
+
+KQueueEventHandler::KQueueEventHandler() : FDEventHandler(TYPE_KQUEUE), kqueuefd_(-1) {
+ kqueuefd_ = kqueue();
+ if (kqueuefd_ == -1) {
+ isc_throw(Unexpected, "error opening kqueue: " << strerror(errno));
+ }
+ if (pipe(pipefd_)) {
+ close(kqueuefd_);
+ isc_throw(Unexpected, "error opening internal kqueue pipe: " << strerror(errno));
+ }
+ clear();
+}
+
+KQueueEventHandler::~KQueueEventHandler() {
+ close(kqueuefd_);
+ close(pipefd_[1]);
+ close(pipefd_[0]);
+}
+
+void KQueueEventHandler::add(int fd, bool read /* = true */, bool write /* = false */) {
+ if (fd < 0) {
+ isc_throw(BadValue, "invalid negative value for fd");
+ }
+ if (read) {
+ // Add this socket to read events
+ struct kevent data;
+ memset(&data, 0, sizeof(data));
+ EV_SET(&data, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
+ data_.push_back(data);
+ }
+ if (write) {
+ // Add this socket to write events
+ struct kevent data;
+ memset(&data, 0, sizeof(data));
+ EV_SET(&data, fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+ data_.push_back(data);
+ }
+}
+
+int KQueueEventHandler::waitEvent(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */,
+ bool use_timeout /* = true */) {
+ // Sanity check for microsecond timeout.
+ if (timeout_usec >= 1000000) {
+ isc_throw(BadValue, "fractional timeout must be shorter than"
+ " one million microseconds");
+ }
+ struct timespec select_timeout;
+ struct timespec* select_timeout_p = 0;
+ if (use_timeout) {
+ select_timeout.tv_sec = timeout_sec;
+ select_timeout.tv_nsec = timeout_usec * 1000;
+ select_timeout_p = &select_timeout;
+ }
+ map_.clear();
+ used_data_.clear();
+ errors_.clear();
+ errno = 0;
+ int saved_errno = errno;
+ for (auto data : data_) {
+ if (kevent(kqueuefd_, &data, 1, 0, 0, 0) == -1) {
+ saved_errno = errno;
+ errors_.insert(data.ident);
+ } else {
+ used_data_.push_back(data);
+ }
+ }
+ struct kevent dummy;
+ memset(&dummy, 0, sizeof(dummy));
+ EV_SET(&dummy, pipefd_[0], EVFILT_READ, EV_ADD, 0, 0, 0);
+ kevent(kqueuefd_, &dummy, 1, 0, 0, 0);
+ used_data_.push_back(dummy);
+ int result = 0;
+ if (errors_.empty()) {
+ result = kevent(kqueuefd_, 0, 0, used_data_.data(), used_data_.size(), select_timeout_p);
+ for (int i = 0; i < result; ++i) {
+ map_.emplace(used_data_[i].ident, &used_data_[i]);
+ }
+ }
+ for (auto data : data_) {
+ EV_SET(&data, data.ident, data.filter, EV_DELETE, 0, 0, 0);
+ kevent(kqueuefd_, &data, 1, 0, 0, 0);
+ }
+ EV_SET(&dummy, dummy.ident, dummy.filter, EV_DELETE, 0, 0, 0);
+ kevent(kqueuefd_, &dummy, 1, 0, 0, 0);
+ if (result != -1) {
+ errno = saved_errno;
+ }
+ if (errors_.size()) {
+ return (errors_.size());
+ }
+ return (result);
+}
+
+bool KQueueEventHandler::readReady(int fd) {
+ auto range = map_.equal_range(fd);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second->filter == EVFILT_READ) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+bool KQueueEventHandler::writeReady(int fd) {
+ auto range = map_.equal_range(fd);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second->filter == EVFILT_WRITE) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+bool KQueueEventHandler::hasError(int fd) {
+ if (errors_.count(fd)) {
+ return (true);
+ }
+ auto range = map_.equal_range(fd);
+ for (auto it = range.first; it != range.second; ++it) {
+ if ((it->second->flags & EV_EOF) || (it->second->filter == EV_ERROR)) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+void KQueueEventHandler::clear() {
+ data_.clear();
+ used_data_.clear();
+ errors_.clear();
+ map_.clear();
+ errno = 0;
+}
+
+} // end of namespace isc::util
+} // end of namespace isc
--- /dev/null
+// 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 KQUEUE_EVENT_HANDLER_H
+#define KQUEUE_EVENT_HANDLER_H
+
+#include <util/fd_event_handler.h>
+
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <sys/event.h>
+#include <sys/time.h>
+#include <sys/types.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 KQueueEventHandler : public FDEventHandler {
+public:
+ /// @brief Constructor.
+ KQueueEventHandler();
+
+ /// @brief Destructor.
+ virtual ~KQueueEventHandler();
+
+ /// @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.
+ /// @param use_timeout Flag which indicates if function should wait
+ /// with no timeout (wait forever).
+ /// @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,
+ bool use_timeout = true);
+
+ /// @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 Check if file descriptor has error.
+ ///
+ /// @param fd The file descriptor.
+ ///
+ /// @return True if file descriptor has error.
+ virtual bool hasError(int fd);
+
+ /// @brief Clear registered file descriptors.
+ void clear();
+
+private:
+ /// @brief The kqueue file descriptor.
+ int kqueuefd_;
+
+ /// @brief The kqueue file descriptors data.
+ std::vector<struct kevent> data_;
+
+ /// @brief The kqueue file descriptors data.
+ std::vector<struct kevent> used_data_;
+
+ /// @brief The set of file descriptors with errors.
+ std::unordered_set<int> errors_;
+
+ /// @brief The map with file descriptor to data reference.
+ std::unordered_multimap<int, struct kevent*> map_;
+
+ /// @brief The pipe used to permit calling @ref waitEvent with no
+ /// registered file descriptors.
+ int pipefd_[2];
+};
+
+} // namespace isc::util;
+} // namespace isc
+
+#endif // KQUEUE_EVENT_HANDLER_H
epoll_event_handler_cc = []
-epoll_event_handler_h = []
if HAVE_EPOLL
epoll_event_handler_cc = 'epoll_event_handler.cc'
- epoll_event_handler_h = 'epoll_event_handler.h'
endif
kqueue_event_handler_cc = []
-kqueue_event_handler_h = []
if HAVE_KQUEUE
kqueue_event_handler_cc = 'kqueue_event_handler.cc'
- kqueue_event_handler_h = 'kqueue_event_handler.h'
endif
kea_util_lib = shared_library(
'doubles.h',
'encode/encode.h',
'encode/utf8.h',
- epoll_event_handler_h,
+ 'epoll_event_handler.h',
'fd_event_handler.h',
'fd_event_handler_factory.h',
'filesystem.h',
'io/sockaddr_util.h',
'hash.h',
'io.h',
- kqueue_event_handler_h,
+ 'kqueue_event_handler.h',
'labeled_value.h',
'memory_segment.h',
'memory_segment_local.h',
errno = 0;
int fd;
- if (handler_->type() == FDEventHandler::TYPE_EPOLL) {
+ if (handler_->type() != FDEventHandler::TYPE_SELECT) {
// epoll does not allow add of /dev/zero to registered events.
fd = pipefd_[0];
EXPECT_EQ(1, write(pipefd_[1], &MARKER, sizeof(MARKER)));
EXPECT_EQ(EBADF, errno);
}
- if (handler_->type() == FDEventHandler::TYPE_EPOLL) {
+ if (handler_->type() != FDEventHandler::TYPE_SELECT) {
close(pipefd_[1]);
pipe(pipefd_);
}
--- /dev/null
+// 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 KQueueEventHandler
+#define FDEventHandlerTest KQueueEventHandlerTest
+
+#include <fd_event_handler_unittests.h>