summaryrefslogtreecommitdiff
path: root/src/common/fdset_kqueue.c
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-11-02 22:44:12 +0100
committerOndřej Surý <ondrej@sury.org>2011-11-02 22:44:12 +0100
commitc8d5977bb546dae9ed59d81556639c49badd8121 (patch)
tree4c86750db26c1c3502b60f2cd78ca9611cfa01d6 /src/common/fdset_kqueue.c
downloadknot-upstream/0.8.0_pre1.tar.gz
Imported Upstream version 0.8.0~pre1upstream/0.8.0_pre1
Diffstat (limited to 'src/common/fdset_kqueue.c')
-rw-r--r--src/common/fdset_kqueue.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/src/common/fdset_kqueue.c b/src/common/fdset_kqueue.c
new file mode 100644
index 0000000..c7199ae
--- /dev/null
+++ b/src/common/fdset_kqueue.c
@@ -0,0 +1,251 @@
+/* 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_KQUEUE
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+#include "fdset_kqueue.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 kq;
+ struct kevent *events;
+ struct kevent *revents;
+ size_t nfds;
+ size_t reserved;
+ size_t polled;
+};
+
+fdset_t *fdset_kqueue_new()
+{
+ fdset_t *set = malloc(sizeof(fdset_t));
+ if (!set) {
+ return 0;
+ }
+
+ /* Blank memory. */
+ memset(set, 0, sizeof(fdset_t));
+
+ /* Create kqueue fd. */
+ set->kq = kqueue();
+ if (set->kq < 0) {
+ free(set);
+ set = 0;
+ }
+
+ return set;
+}
+
+int fdset_kqueue_destroy(fdset_t * fdset)
+{
+ if(!fdset) {
+ return -1;
+ }
+
+ /* Teardown kqueue. */
+ close(fdset->kq);
+
+ /* OK if NULL. */
+ free(fdset->revents);
+ free(fdset->events);
+ free(fdset);
+ return 0;
+}
+
+int fdset_kqueue_realloc(void **old, size_t oldsize, size_t nsize)
+{
+ void *nmem = malloc(nsize);
+ if (!nmem) {
+ return -1;
+ }
+
+ /* Clear and copy old fdset data. */
+ memset(nmem, 0, nsize);
+ if (oldsize > 0) {
+ memcpy(nmem, *old, oldsize);
+ free(*old);
+ }
+
+ *old = nmem;
+ return 0;
+}
+
+int fdset_kqueue_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 kevent);
+ const size_t oldsize = fdset->nfds * sizeof(struct kevent);
+
+ if (fdset_kqueue_realloc(&fdset->events, oldsize, nsize) < 0) {
+ return -1;
+ }
+
+ if (fdset_kqueue_realloc(&fdset->revents, oldsize, nsize) < 0) {
+ return -1;
+ }
+
+ }
+
+ /* Add to kqueue set. */
+ int evfilt = EVFILT_READ; /*! \todo Map events. */
+ EV_SET(&fdset->events[fdset->nfds], fd, evfilt,
+ EV_ADD|EV_ENABLE, 0, 0, 0);
+
+ ++fdset->nfds;
+ return 0;
+}
+
+int fdset_kqueue_remove(fdset_t *fdset, int fd)
+{
+ if (!fdset || fd < 0) {
+ return -1;
+ }
+
+ /* Find in set. */
+ int pos = -1;
+ for (int i = 0; i < fdset->nfds; ++i) {
+ if (fdset->events[i].ident == fd) {
+ pos = i;
+ break;
+ }
+ }
+
+ if (pos < 0) {
+ return -1;
+ }
+
+ /* Remove filters. */
+ EV_SET(&fdset->events[pos], fd, EVFILT_READ,
+ EV_DISABLE|EV_DELETE, 0, 0, 0);
+
+ /* Attempt to remove from set. */
+ size_t remaining = ((fdset->nfds - pos) - 1) * sizeof(struct kevent);
+ memmove(fdset->events + pos, fdset->events + (pos + 1), remaining);
+
+ /* Overwrite current item. */
+ --fdset->nfds;
+
+ /*! \todo Return memory if overallocated (nfds is far lower than reserved). */
+ return 0;
+}
+
+int fdset_kqueue_wait(fdset_t *fdset)
+{
+ if (!fdset || fdset->nfds < 1 || !fdset->events) {
+ return -1;
+ }
+
+ /* Poll new events. */
+ fdset->polled = 0;
+ int nfds = kevent(fdset->kq, fdset->events, fdset->nfds,
+ fdset->revents, fdset->nfds, 0);
+
+ /* Check. */
+ if (nfds < 0) {
+ return -1;
+ }
+
+ /* Events array is ordered from 0 to nfds. */
+ fdset->polled = nfds;
+ return nfds;
+}
+
+int fdset_kqueue_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_kqueue_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->revents[nid].ident;
+ it->pos = nid;
+ it->events = 0; /*! \todo Map events. */
+ return -1;
+}
+
+int fdset_kqueue_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->revents[nid].ident;
+ it->events = 0; /*! \todo Map events. */
+ return 0;
+}
+
+const char* fdset_kqueue_method()
+{
+ return "kqueue";
+}
+
+/* Package APIs. */
+struct fdset_backend_t FDSET_KQUEUE = {
+ .fdset_new = fdset_kqueue_new,
+ .fdset_destroy = fdset_kqueue_destroy,
+ .fdset_add = fdset_kqueue_add,
+ .fdset_remove = fdset_kqueue_remove,
+ .fdset_wait = fdset_kqueue_wait,
+ .fdset_begin = fdset_kqueue_begin,
+ .fdset_end = fdset_kqueue_end,
+ .fdset_next = fdset_kqueue_next,
+ .fdset_method = fdset_kqueue_method
+};
+
+#endif