summaryrefslogtreecommitdiff
path: root/src/common/fdset_epoll.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/fdset_epoll.c')
-rw-r--r--src/common/fdset_epoll.c216
1 files changed, 216 insertions, 0 deletions
diff --git a/src/common/fdset_epoll.c b/src/common/fdset_epoll.c
new file mode 100644
index 0000000..cb2e3e1
--- /dev/null
+++ b/src/common/fdset_epoll.c
@@ -0,0 +1,216 @@
+/* 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_EPOLL_WAIT
+
+#include <sys/epoll.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "fdset_epoll.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 {
+ int epfd;
+ struct epoll_event *events;
+ size_t nfds;
+ size_t reserved;
+ size_t polled;
+};
+
+fdset_t *fdset_epoll_new()
+{
+ fdset_t *set = malloc(sizeof(fdset_t));
+ if (!set) {
+ return 0;
+ }
+
+ /* Blank memory. */
+ memset(set, 0, sizeof(fdset_t));
+
+ /* Create epoll fd. */
+ set->epfd = epoll_create(OS_FDS_CHUNKSIZE);
+
+ return set;
+}
+
+int fdset_epoll_destroy(fdset_t * fdset)
+{
+ if(!fdset) {
+ return -1;
+ }
+
+ /* Teardown epoll. */
+ close(fdset->epfd);
+
+ /* OK if NULL. */
+ free(fdset->events);
+ free(fdset);
+ return 0;
+}
+
+int fdset_epoll_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 = (fdset->reserved + chunk) *
+ sizeof(struct epoll_event);
+ struct epoll_event *events_n = malloc(nsize);
+ if (!events_n) {
+ return -1;
+ }
+
+ /* Clear and copy old fdset data. */
+ memset(events_n, 0, nsize);
+ memcpy(events_n, fdset->events,
+ fdset->nfds * sizeof(struct epoll_event));
+ free(fdset->events);
+ fdset->events = events_n;
+ fdset->reserved += chunk;
+ }
+
+ /* Add to epoll set. */
+ struct epoll_event ev;
+ memset(&ev, 0, sizeof(struct epoll_event));
+ ev.events = EPOLLIN; /*! \todo MAP events. */
+ ev.data.fd = fd;
+ if (epoll_ctl(fdset->epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ return -1;
+ }
+
+ ++fdset->nfds;
+ return 0;
+}
+
+int fdset_epoll_remove(fdset_t *fdset, int fd)
+{
+ if (!fdset || fd < 0) {
+ return -1;
+ }
+
+ /* Attempt to remove from set. */
+ struct epoll_event ev;
+ memset(&ev, 0, sizeof(struct epoll_event));
+ if (epoll_ctl(fdset->epfd, EPOLL_CTL_DEL, fd, &ev) < 0) {
+ return -1;
+ }
+
+ /* Overwrite current item. */
+ --fdset->nfds;
+
+ /*! \todo Return memory if overallocated (nfds is far lower than reserved). */
+ return 0;
+}
+
+int fdset_epoll_wait(fdset_t *fdset)
+{
+ if (!fdset || fdset->nfds < 1 || !fdset->events) {
+ return -1;
+ }
+
+ /* Poll new events. */
+ fdset->polled = 0;
+ int nfds = epoll_wait(fdset->epfd, fdset->events, fdset->nfds, -1);
+
+ /* Check. */
+ if (nfds < 0) {
+ return -1;
+ }
+
+ /* Events array is ordered from 0 to nfds. */
+ fdset->polled = nfds;
+ return nfds;
+}
+
+int fdset_epoll_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_epoll_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;
+ }
+
+ /* No end found, ends on the beginning. */
+ size_t nid = fdset->polled - 1;
+ it->fd = fdset->events[nid].data.fd;
+ it->pos = nid;
+ it->events = 0; /*! \todo Map events. */
+ return -1;
+}
+
+int fdset_epoll_next(fdset_t *fdset, fdset_it_t *it)
+{
+ if (!fdset || !it || fdset->nfds < 1) {
+ return -1;
+ }
+
+ /* Check boundaries. */
+ if (it->pos >= fdset->polled) {
+ return -1;
+ }
+
+ /* Select next. */
+ size_t nid = it->pos++;
+ it->fd = fdset->events[nid].data.fd;
+ it->events = 0; /*! \todo Map events. */
+ return 0;
+}
+
+const char* fdset_epoll_method()
+{
+ return "epoll";
+}
+
+/* Package APIs. */
+struct fdset_backend_t FDSET_EPOLL = {
+ .fdset_new = fdset_epoll_new,
+ .fdset_destroy = fdset_epoll_destroy,
+ .fdset_add = fdset_epoll_add,
+ .fdset_remove = fdset_epoll_remove,
+ .fdset_wait = fdset_epoll_wait,
+ .fdset_begin = fdset_epoll_begin,
+ .fdset_end = fdset_epoll_end,
+ .fdset_next = fdset_epoll_next,
+ .fdset_method = fdset_epoll_method
+};
+
+#endif