diff --git a/Knot.files b/Knot.files index 2d42bf6a0ca9014a402777da2bc50370adf3a31a..50c9fd64b7f16a71b19bfe943891e1fb37a045d1 100644 --- a/Knot.files +++ b/Knot.files @@ -107,6 +107,10 @@ src/common/general-tree.h src/common/general-tree.c src/common/WELL1024a.c src/common/WELL1024a.h +src/common/os_fdset.h +src/common/os_fdset.c +src/common/os_fdset_poll.c +src/common/os_fdset_epoll.c src/zcompile/parser-descriptor.h src/zcompile/parser-descriptor.c src/zcompile/parser-util.h diff --git a/src/Makefile.am b/src/Makefile.am index 82778ec524a1128498e7fb10fabb737149eefaf0..34065ef0f27a3963a79d230fbe91f03cd0260f6a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -175,8 +175,10 @@ libknots_la_SOURCES = \ common/ref.c \ common/errors.h \ common/errors.c \ - common/WELL1024a.h \ - common/WELL1024a.c + common/WELL1024a.h \ + common/WELL1024a.c \ + common/os_fdset.h \ + common/os_fdset.c libknotd_la_SOURCES = \ knot/stat/gatherer.c \ diff --git a/src/common/os_fdset.c b/src/common/os_fdset.c new file mode 100644 index 0000000000000000000000000000000000000000..1bbe1222fc062e55fd83b2c0779f483a58fef7cc --- /dev/null +++ b/src/common/os_fdset.c @@ -0,0 +1,7 @@ +#include "os_fdset.h" + +#ifdef HAVE_EPOLL +#include "os_fdset_epoll.c" +#else +#include "os_fdset_poll.c" +#endif diff --git a/src/common/os_fdset.h b/src/common/os_fdset.h new file mode 100644 index 0000000000000000000000000000000000000000..3dcbbbc76b2337681ed1b7fbe3851aa022064b88 --- /dev/null +++ b/src/common/os_fdset.h @@ -0,0 +1,139 @@ +/*! + * \file os_fdset.h + * + * \author Marek Vavrusa <marek.vavrusa@nic.cz> + * + * \brief Wrapper for native I/O multiplexing. + * + * Selects best implementation according to config. + * - select() + * - poll() \todo + * - epoll() + * - kqueue() + * + * \addtogroup common_lib + * @{ + */ + +#ifndef _KNOTD_OS_FDSET_H_ +#define _KNOTD_OS_FDSET_H_ + +#include <stddef.h> + +/*! \brief Opaque pointer to implementation-specific fdset data. */ +struct os_fdset_t; + +/*! \brief Single event descriptor. */ +typedef struct os_fd_t { + int fd; + int events; + size_t pos; +} os_fd_t; + +/*! \brief Unified event types. */ +enum os_ev_t { + OS_EV_READ = 1 << 0, + OS_EV_WRITE = 1 << 1, + OS_EV_ERROR = 1 << 2 +}; + +/*! + * \brief Create new fdset. + * + * FDSET implementation depends on system. + * + * \retval Pointer to initialized FDSET structure if successful. + * \retval NULL on error. + */ +struct os_fdset_t *os_fdset_new(); + +/*! + * \brief Destroy FDSET. + * + * \retval 0 if successful. + * \retval -1 on error. + */ +int os_fdset_destroy(struct os_fdset_t * fdset); + +/*! + * \brief Add file descriptor to watched set. + * + * \param fdset Target set. + * \param fd Added file descriptor. + * \param events Mask of watched events. + * + * \retval 0 if successful. + * \retval -1 on errors. + */ +int os_fdset_add(struct os_fdset_t *fdset, int fd, int events); + +/*! + * \brief Remove file descriptor from watched set. + * + * \param fdset Target set. + * \param fd File descriptor to be removed. + * + * \retval 0 if successful. + * \retval -1 on errors. + */ +int os_fdset_remove(struct os_fdset_t *fdset, int fd); + +/*! + * \brief Poll set for new events. + * + * \param fdset Target set. + * + * \retval Number of events if successful. + * \retval -1 on errors. + * + * \todo Timeout. + */ +int os_fdset_poll(struct os_fdset_t *fdset); + +/*! + * \brief Set event iterator to the beginning of last polled events. + * + * \param fdset Target set. + * \param it Event iterator. + * + * \retval 0 if successful. + * \retval -1 on errors. + */ +int os_fdset_begin(struct os_fdset_t *fdset, os_fd_t *it); + +/*! + * \brief Set event iterator to the end of last polled events. + * + * \param fdset Target set. + * \param it Event iterator. + * + * \retval 0 if successful. + * \retval -1 on errors. + */ +int os_fdset_end(struct os_fdset_t *fdset, os_fd_t *it); + +/*! + * \brief Set event iterator to the next event. + * + * Event iterator fd will be set to -1 if next event doesn't exist. + * + * \param fdset Target set. + * \param it Event iterator. + * + * \retval 0 if successful. + * \retval -1 on errors. + */ +int os_fdset_next(struct os_fdset_t *fdset, os_fd_t *it); + + +/*! + * \brief Returned name of underlying poll method. + * + * \retval Name if successful. + * \retval NULL if no method was loaded (shouldn't happen). + */ +const char* os_fdset_method(); + +#endif /* _KNOTD_OS_FDSET_H_ */ + +/*! @} */ diff --git a/src/common/os_fdset_epoll.c b/src/common/os_fdset_epoll.c new file mode 100644 index 0000000000000000000000000000000000000000..5e8b20b5e3cf3a235491ff89b27764987af650c1 --- /dev/null +++ b/src/common/os_fdset_epoll.c @@ -0,0 +1,3 @@ +#include "os_fdset.h" + + diff --git a/src/common/os_fdset_poll.c b/src/common/os_fdset_poll.c new file mode 100644 index 0000000000000000000000000000000000000000..7340ef8474eb2c862edb567406e0ab8f6402b7fd --- /dev/null +++ b/src/common/os_fdset_poll.c @@ -0,0 +1,191 @@ +#include <stdlib.h> +#include <string.h> +#include <sys/poll.h> +#include <stddef.h> + +#include "os_fdset.h" + +struct os_fdset_t { + struct pollfd *fds; + nfds_t nfds; + size_t reserved; + size_t polled; + size_t begin; +}; + +struct os_fdset_t *os_fdset_new() +{ + struct os_fdset_t *set = malloc(sizeof(struct os_fdset_t)); + if (!set) { + return 0; + } + + /* Blank memory. */ + memset(set, 0, sizeof(struct os_fdset_t)); + return set; +} + +int os_fdset_destroy(struct os_fdset_t * fdset) +{ + if(!fdset) { + return -1; + } + + /*! \todo No teardown required I guess. */ + + /* OK if NULL. */ + free(fdset->fds); + free(fdset); + return 0; +} + +int os_fdset_add(struct os_fdset_t *fdset, int fd, int events) +{ + if (!fdset || fd < 0 || events <= 0) { + return -1; + } + + /* Realloc needed. */ + if (fdset->nfds == fdset->reserved) { + const size_t chunk = 8; + const size_t nsize = sizeof(struct pollfd) * (fdset->reserved + chunk); + struct pollfd *fds_n = malloc(nsize); + if (!fds_n) { + return -1; + } + + /* Clear and copy old fdset data. */ + memset(fds_n, 0, nsize); + memcpy(fds_n, fdset->fds, fdset->nfds * sizeof(struct pollfd)); + free(fdset->fds); + fdset->fds = fds_n; + fdset->reserved += chunk; + } + + /* Append. */ + int nid = fdset->nfds++; + fdset->fds[nid].fd = fd; + fdset->fds[nid].events = POLLIN; /*! \todo Map events to POLL events. */ + return 0; +} + +int os_fdset_remove(struct os_fdset_t *fdset, int fd) +{ + if (!fdset || fd < 0) { + return -1; + } + + /* Find file descriptor. */ + unsigned found = 0; + size_t pos = 0; + for (size_t i = 0; i < fdset->nfds; ++i) { + if (fdset->fds[i].fd == fd) { + found = 1; + pos = i; + break; + } + } + + /* Check. */ + if (!found) { + return -1; + } + + /* Overwrite current item. */ + size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct pollfd); + memmove(fdset->fds + pos, fdset->fds + (pos + 1), remaining); + --fdset->nfds; + + /*! \todo Return memory if overallocated (nfds is far lower than reserved). */ + return 0; +} + +int os_fdset_poll(struct os_fdset_t *fdset) +{ + if (!fdset || fdset->nfds < 1 || !fdset->fds) { + return -1; + } + + /* Initialize pointers. */ + fdset->polled = 0; + fdset->begin = 0; + + /* Poll for events. */ + int ret = poll(fdset->fds, fdset->nfds, -1); + if (ret < 0) { + return -1; + } + + /* Set pointers for iterating. */ + fdset->polled = ret; + fdset->begin = 0; + return ret; +} + +int os_fdset_begin(struct os_fdset_t *fdset, os_fd_t *it) +{ + if (!fdset || !it) { + return -1; + } + + /* Find first. */ + it->pos = 0; + return os_fdset_next(fdset, it); +} + +int os_fdset_end(struct os_fdset_t *fdset, os_fd_t *it) +{ + if (!fdset || !it || fdset->nfds < 1) { + return -1; + } + + /* Check for polled events. */ + if (fdset->polled < 1) { + it->fd = -1; + it->pos = 0; + return -1; + } + + /* Trace last matching item from the end. */ + struct pollfd* pfd = fdset->fds + fdset->nfds - 1; + while (pfd != fdset->fds) { + if (pfd->events & pfd->revents) { + it->fd = pfd->fd; + it->pos = pfd - fdset->fds; + return 0; + } + } + + /* No end found, ends on the beginning. */ + it->fd = -1; + it->pos = 0; + return -1; +} + +int os_fdset_next(struct os_fdset_t *fdset, os_fd_t *it) +{ + if (!fdset || !it || fdset->nfds < 1) { + return -1; + } + + /* Find next with matching flags. */ + for (; it->pos < fdset->nfds; ++it->pos) { + struct pollfd* pfd = fdset->fds + it->pos; + if (pfd->events & pfd->revents) { + it->fd = pfd->fd; + it->events = pfd->revents; /*! \todo MAP events. */ + ++it->pos; /* Next will start after current. */ + return 0; + } + } + + /* No matching event found. */ + it->fd = -1; + it->pos = 0; + return -1; +} + +const char* os_fdset_method() +{ + return "poll"; +}