From f449f278dd3c70e479a035f50a9bb817a9b433ba Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 17:24:08 +0200 Subject: Adding upstream version 3.2.6. Signed-off-by: Daniel Baumann --- src/knot/common/fdset.h | 382 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 src/knot/common/fdset.h (limited to 'src/knot/common/fdset.h') diff --git a/src/knot/common/fdset.h b/src/knot/common/fdset.h new file mode 100644 index 0000000..95a5c61 --- /dev/null +++ b/src/knot/common/fdset.h @@ -0,0 +1,382 @@ +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +/*! + * \brief I/O multiplexing with context and timeouts for each fd. + */ + +#pragma once + +#include +#include +#include +#include + +#ifdef HAVE_EPOLL +#include +#elif HAVE_KQUEUE +#include +#else +#include +#endif + +#include "libknot/errcode.h" + +#define FDSET_RESIZE_STEP 256 +#ifdef HAVE_EPOLL +#define FDSET_REMOVE_FLAG ~0U +#endif + +/*! \brief Set of file descriptors with associated context and timeouts. */ +typedef struct { + unsigned n; /*!< Active fds. */ + unsigned size; /*!< Array size (allocated). */ + void **ctx; /*!< Context for each fd. */ + time_t *timeout; /*!< Timeout for each fd (seconds precision). */ +#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE) +#ifdef HAVE_EPOLL + struct epoll_event *ev; /*!< Epoll event storage for each fd. */ + struct epoll_event *recv_ev; /*!< Array for polled events. */ +#elif HAVE_KQUEUE + struct kevent *ev; /*!< Kqueue event storage for each fd. */ + struct kevent *recv_ev; /*!< Array for polled events. */ +#endif + unsigned recv_size; /*!< Size of array for polled events. */ + int pfd; /*!< File descriptor of kernel polling structure (epoll or kqueue). */ +#else + struct pollfd *pfd; /*!< Poll state for each fd. */ +#endif +} fdset_t; + +/*! \brief State of iterator over received events */ +typedef struct { + fdset_t *set; /*!< Source fdset_t. */ + unsigned idx; /*!< Event index offset. */ + int unprocessed; /*!< Unprocessed events left. */ +#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE) +#ifdef HAVE_EPOLL + struct epoll_event *ptr; /*!< Pointer on processed event. */ +#elif HAVE_KQUEUE + struct kevent *ptr; /*!< Pointer on processed event. */ +#endif + unsigned dirty; /*!< Number of fd to be removed on commit. */ +#endif +} fdset_it_t; + +typedef enum { +#ifdef HAVE_EPOLL + FDSET_POLLIN = EPOLLIN, + FDSET_POLLOUT = EPOLLOUT, +#elif HAVE_KQUEUE + FDSET_POLLIN = EVFILT_READ, + FDSET_POLLOUT = EVFILT_WRITE, +#else + FDSET_POLLIN = POLLIN, + FDSET_POLLOUT = POLLOUT, +#endif +} fdset_event_t; + +/*! \brief Mark-and-sweep state. */ +typedef enum { + FDSET_KEEP, + FDSET_SWEEP +} fdset_sweep_state_t; + +/*! \brief Sweep callback (set, index, data) */ +typedef fdset_sweep_state_t (*fdset_sweep_cb_t)(fdset_t *, int, void *); + +/*! + * \brief Initialize fdset to given size. + * + * \param set Target set. + * \param size Initial set size. + * + * \return Error code, KNOT_EOK if success. + */ +int fdset_init(fdset_t *set, const unsigned size); + +/*! + * \brief Clear whole context of the fdset. + * + * \param set Target set. + */ +void fdset_clear(fdset_t *set); + +/*! + * \brief Add file descriptor to watched set. + * + * \param set Target set. + * \param fd Added file descriptor. + * \param events Mask of watched events. + * \param ctx Context (optional). + * + * \retval ret >= 0 is index of the added fd. + * \retval ret < 0 on error. + */ +int fdset_add(fdset_t *set, const int fd, const fdset_event_t events, void *ctx); + +/*! + * \brief Remove and close file descriptor from watched set. + * + * \param set Target set. + * \param idx Index of the removed fd. + * + * \return Error code, KNOT_EOK if success. + */ +int fdset_remove(fdset_t *set, const unsigned idx); + +/*! + * \brief Wait for receive events. + * + * Skip events based on offset and set iterator on first event. + * + * \param set Target set. + * \param it Event iterator storage. + * \param offset Index of first event. + * \param timeout_ms Timeout of operation in milliseconds (use -1 for unlimited). + * + * \retval ret >= 0 represents number of events received. + * \retval ret < 0 on error. + */ +int fdset_poll(fdset_t *set, fdset_it_t *it, const unsigned offset, const int timeout_ms); + +/*! + * \brief Set file descriptor watchdog interval. + * + * Set time (interval from now) after which the associated file descriptor + * should be sweeped (see fdset_sweep). Good example is setting a grace period + * of N seconds between socket activity. If socket is not active within + * , it is sweeped and closed. + * + * \param set Target set. + * \param idx Index of the file descriptor. + * \param interval Allowed interval without activity (seconds). + * -1 disables watchdog timer. + * + * \return Error code, KNOT_EOK if success. + */ +int fdset_set_watchdog(fdset_t *set, const unsigned idx, const int interval); + +/*! + * \brief Sweep file descriptors with exceeding inactivity period. + * + * \param set Target set. + * \param cb Callback for sweeped descriptors. + * \param data Pointer to extra data. + */ +void fdset_sweep(fdset_t *set, const fdset_sweep_cb_t cb, void *data); + +/*! + * \brief Returns file descriptor based on index. + * + * \param set Target set. + * \param idx Index of the file descriptor. + * + * \retval ret >= 0 for file descriptor. + * \retval ret < 0 on errors. + */ +inline static int fdset_get_fd(const fdset_t *set, const unsigned idx) +{ + assert(set && idx < set->n); + +#ifdef HAVE_EPOLL + return set->ev[idx].data.fd; +#elif HAVE_KQUEUE + return set->ev[idx].ident; +#else + return set->pfd[idx].fd; +#endif +} + +/*! + * \brief Returns number of file descriptors stored in set. + * + * \param set Target set. + * + * \retval Number of descriptors stored + */ +inline static unsigned fdset_get_length(const fdset_t *set) +{ + assert(set); + + return set->n; +} + +/*! + * \brief Get index of event in set referenced by iterator. + * + * \param it Target iterator. + * + * \retval Index of event. + */ +inline static unsigned fdset_it_get_idx(const fdset_it_t *it) +{ + assert(it); + +#ifdef HAVE_EPOLL + return it->ptr->data.u64; +#elif HAVE_KQUEUE + return (unsigned)(intptr_t)it->ptr->udata; +#else + return it->idx; +#endif +} + +/*! + * \brief Get file descriptor of event referenced by iterator. + * + * \param it Target iterator. + * + * \retval ret >= 0 for file descriptor. + * \retval ret < 0 on errors. + */ +inline static int fdset_it_get_fd(const fdset_it_t *it) +{ + assert(it); + +#ifdef HAVE_EPOLL + return it->set->ev[fdset_it_get_idx(it)].data.fd; +#elif HAVE_KQUEUE + return it->ptr->ident; +#else + return it->set->pfd[it->idx].fd; +#endif +} + +/*! + * \brief Move iterator on next received event. + * + * \param it Target iterator. + */ +inline static void fdset_it_next(fdset_it_t *it) +{ + assert(it); + +#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE) + do { + it->ptr++; + it->unprocessed--; + } while (it->unprocessed > 0 && fdset_it_get_idx(it) < it->idx); +#else + if (--it->unprocessed > 0) { + while (it->set->pfd[++it->idx].revents == 0); /* nop */ + } +#endif +} + +/*! + * \brief Remove file descriptor referenced by iterator from watched set. + * + * \param it Target iterator. + * + * \return Error code, KNOT_EOK if success. + */ +inline static void fdset_it_remove(fdset_it_t *it) +{ + assert(it); + +#ifdef HAVE_EPOLL + const int idx = fdset_it_get_idx(it); + it->set->ev[idx].events = FDSET_REMOVE_FLAG; + it->dirty++; +#elif HAVE_KQUEUE + const int idx = fdset_it_get_idx(it); + /* Bitwise negated filter marks event for delete. */ + /* Filters become: */ + /* [FreeBSD] */ + /* EVFILT_READ (-1) -> 0 */ + /* EVFILT_WRITE (-2) -> 1 */ + /* [NetBSD] */ + /* EVFILT_READ (0) -> -1 */ + /* EVFILT_WRITE (1) -> -2 */ + /* If not marked for delete then mark for delete. */ +#if defined(__NetBSD__) + if ((signed short)it->set->ev[idx].filter >= 0) +#else + if (it->set->ev[idx].filter < 0) +#endif + { + it->set->ev[idx].filter = ~it->set->ev[idx].filter; + } + it->dirty++; +#else + (void)fdset_remove(it->set, fdset_it_get_idx(it)); + /* Iterator should return on last valid already processed element. */ + /* On `next` call (in for-loop) will point on first unprocessed. */ + it->idx--; +#endif +} + +/*! + * \brief Commit changes made in fdset using iterator. + * + * \param it Target iterator. + */ +void fdset_it_commit(fdset_it_t *it); + +/*! + * \brief Decide if there is more received events. + * + * \param it Target iterator. + * + * \retval Logical flag representing 'done' state. + */ +inline static bool fdset_it_is_done(const fdset_it_t *it) +{ + assert(it); + + return it->unprocessed <= 0; +} + +/*! + * \brief Decide if event referenced by iterator is POLLIN event. + * + * \param it Target iterator. + * + * \retval Logical flag represents 'POLLIN' event received. + */ +inline static bool fdset_it_is_pollin(const fdset_it_t *it) +{ + assert(it); + +#ifdef HAVE_EPOLL + return it->ptr->events & EPOLLIN; +#elif HAVE_KQUEUE + return it->ptr->filter == EVFILT_READ; +#else + return it->set->pfd[it->idx].revents & POLLIN; +#endif +} + +/*! + * \brief Decide if event referenced by iterator is error event. + * + * \param it Target iterator. + * + * \retval Logical flag represents error event received. + */ +inline static bool fdset_it_is_error(const fdset_it_t *it) +{ + assert(it); + +#ifdef HAVE_EPOLL + return it->ptr->events & (EPOLLERR | EPOLLHUP); +#elif HAVE_KQUEUE + return it->ptr->flags & EV_ERROR; +#else + return it->set->pfd[it->idx].revents & (POLLERR | POLLHUP | POLLNVAL); +#endif +} -- cgit v1.2.3