diff options
Diffstat (limited to 'src/common/fdset_poll.c')
-rw-r--r-- | src/common/fdset_poll.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/common/fdset_poll.c b/src/common/fdset_poll.c new file mode 100644 index 0000000..8682eaf --- /dev/null +++ b/src/common/fdset_poll.c @@ -0,0 +1,230 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + 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 <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#ifdef HAVE_POLL + +#include <stdlib.h> +#include <string.h> +#include <sys/poll.h> +#include <stddef.h> + +#include "common/fdset_poll.h" + +#define OS_FDS_CHUNKSIZE 8 /*!< Number of pollfd structs in a chunk. */ +#define OS_FDS_KEEPCHUNKS 32 /*!< Will attempt to free memory when reached. */ + +struct fdset_t { + struct pollfd *fds; + nfds_t nfds; + size_t reserved; + size_t polled; + size_t begin; +}; + +fdset_t *fdset_poll_new() +{ + fdset_t *set = malloc(sizeof(fdset_t)); + if (!set) { + return 0; + } + + /* Blank memory. */ + memset(set, 0, sizeof(fdset_t)); + return set; +} + +int fdset_poll_destroy(fdset_t * fdset) +{ + if(!fdset) { + return -1; + } + + /*! \todo No teardown required I guess. */ + + /* OK if NULL. */ + free(fdset->fds); + free(fdset); + return 0; +} + +int fdset_poll_add(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 = OS_FDS_CHUNKSIZE; + 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 fdset_poll_remove(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). */ + /*! \todo Maybe >64 free chunks is excess? */ + return 0; +} + +int fdset_poll_wait(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 fdset_poll_begin(fdset_t *fdset, fdset_it_t *it) +{ + if (!fdset || !it) { + return -1; + } + + /* Find first. */ + it->pos = 0; + return fdset_next(fdset, it); +} + +int fdset_poll_end(fdset_t *fdset, fdset_it_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 fdset_poll_next(fdset_t *fdset, fdset_it_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* fdset_poll_method() +{ + return "poll"; +} + +/* Package APIs. */ +struct fdset_backend_t FDSET_POLL = { + .fdset_new = fdset_poll_new, + .fdset_destroy = fdset_poll_destroy, + .fdset_add = fdset_poll_add, + .fdset_remove = fdset_poll_remove, + .fdset_wait = fdset_poll_wait, + .fdset_begin = fdset_poll_begin, + .fdset_end = fdset_poll_end, + .fdset_next = fdset_poll_next, + .fdset_method = fdset_poll_method +}; + +#endif |