[Dovecot] Patch: ioloop using kqueue/kevent for FreeBSD
Vaclav Haisman
V.Haisman at sh.cvut.cz
Wed Dec 14 18:42:45 EET 2005
Only after posting the first patch I realised it is not good enough wrt/
IO_ERROR handling. The attached patch should be better.
Vaclav Haisman
-------------- next part --------------
diff -rN -u old-dovecot-cvs/configure.in new-dovecot-cvs/configure.in
--- old-dovecot-cvs/configure.in 2005-12-14 17:33:23.680977636 +0100
+++ new-dovecot-cvs/configure.in 2005-12-14 17:33:29.313807864 +0100
@@ -1,6 +1,10 @@
AC_INIT(dovecot, 1.0.alpha5, [dovecot at dovecot.org])
AC_CONFIG_SRCDIR([src])
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+AC_CANONICAL_TARGET
+
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE
@@ -327,6 +331,15 @@
])
fi
+if test "$ioloop" = "kqueue"; then
+ AC_CHECK_FUNC(kqueue, [
+ AC_DEFINE(IOLOOP_KQUEUE,, [Implement I/O loop with FreeBSD kqueue()])
+ have_ioloop=yes
+ ], [
+ ioloop=""
+ ])
+fi
+
if test "$ioloop" = "" || test "$ioloop" = "poll"; then
AC_CHECK_FUNC(poll, [
AC_DEFINE(IOLOOP_POLL,, Implement I/O loop with poll())
diff -rN -u old-dovecot-cvs/src/lib/Makefile.am new-dovecot-cvs/src/lib/Makefile.am
--- old-dovecot-cvs/src/lib/Makefile.am 2005-12-14 17:33:23.678298456 +0100
+++ new-dovecot-cvs/src/lib/Makefile.am 2005-12-14 17:33:24.907951306 +0100
@@ -35,6 +35,7 @@
ioloop-poll.c \
ioloop-select.c \
ioloop-epoll.c \
+ ioloop-kqueue.c \
lib.c \
lib-signals.c \
md4.c \
diff -rN -u old-dovecot-cvs/src/lib/ioloop-kqueue.c new-dovecot-cvs/src/lib/ioloop-kqueue.c
--- old-dovecot-cvs/src/lib/ioloop-kqueue.c 1970-01-01 01:00:00.000000000 +0100
+++ new-dovecot-cvs/src/lib/ioloop-kqueue.c 2005-12-14 17:33:26.384132530 +0100
@@ -0,0 +1,210 @@
+/*
+ * FreeBSD kqueue() based ioloop handler.
+ *
+ * Copyright (c) 2005 Vaclav Haisman <v.haisman at sh.cvut.cz>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/* @UNSAFE: whole file */
+
+#include "lib.h"
+#include "ioloop-internal.h"
+
+#ifdef IOLOOP_KQUEUE
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+#ifndef INITIAL_BUF_SIZE
+# define INITIAL_BUF_SIZE 128
+#endif
+
+
+struct ioloop_handler_context {
+ int kq;
+ size_t evbuf_size;
+ struct kevent *evbuf;
+
+ size_t fds_size;
+ struct fdrecord *fds;
+};
+
+struct fdrecord {
+ struct io *errio;
+ /* IO_READ | IO_WRITE | IO_ERROR */
+ unsigned char mode : 3;
+};
+
+
+void io_loop_handler_init(struct ioloop *ioloop)
+{
+ struct ioloop_handler_context *ctx;
+
+ ioloop->handler_context = ctx =
+ p_new(ioloop->pool, struct ioloop_handler_context, 1);
+ ctx->evbuf_size = INITIAL_BUF_SIZE;
+ ctx->evbuf = p_new(ioloop->pool, struct kevent, ctx->evbuf_size);
+ memset(ctx->evbuf, 0, sizeof(struct kevent) * ctx->evbuf_size);
+ ctx->kq = kqueue ();
+ if (ctx->kq < 0)
+ i_fatal("kqueue(): %m");
+
+ ctx->fds_size = INITIAL_BUF_SIZE;
+ ctx->fds = p_new(ioloop->pool, struct fdrecord, ctx->fds_size);
+ memset(ctx->fds, 0, sizeof(struct fdrecord) * ctx->fds_size);
+}
+
+
+void io_loop_handler_deinit(struct ioloop *ioloop)
+{
+ p_free(ioloop->pool, ioloop->handler_context->evbuf);
+ p_free(ioloop->pool, ioloop->handler_context->fds);
+ p_free(ioloop->pool, ioloop->handler_context);
+}
+
+
+void io_loop_handle_add(struct ioloop *ioloop, struct io *io)
+{
+ struct ioloop_handler_context *ctx = ioloop->handler_context;
+ const int fd = io->fd;
+ struct kevent ev = {fd, 0, EV_ADD | EV_CLEAR | EV_EOF, 0, 0, NULL};
+ enum io_condition condition = io->condition;
+
+ /* grow ctx->fds array if necessary */
+ if ((size_t)fd >= ctx->fds_size)
+ {
+ size_t old_size = ctx->fds_size;
+
+ ctx->fds_size = nearest_power((unsigned int)fd+1);
+ i_assert(ctx->fds_size < (size_t)-1 / sizeof(int));
+
+ ctx->fds = p_realloc(ioloop->pool, ctx->fds,
+ sizeof(struct fdrecord) * old_size,
+ sizeof(struct fdrecord) * ctx->fds_size);
+ memset(ctx->fds + old_size, 0,
+ sizeof(struct fdrecord) * (ctx->fds_size - old_size));
+ }
+
+ if (condition & (IO_READ | IO_WRITE))
+ ev.udata = io;
+ if (condition & IO_ERROR)
+ ctx->fds[fd].errio = io;
+
+ if (condition & (IO_READ | IO_ERROR))
+ {
+ ctx->fds[fd].mode |= condition;
+ ev.filter = EVFILT_READ;
+ kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
+ }
+ if (condition & (IO_WRITE | IO_ERROR))
+ {
+ ctx->fds[fd].mode |= condition;
+ ev.filter = EVFILT_WRITE;
+ kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
+ }
+}
+
+
+void io_loop_handle_remove(struct ioloop *ioloop, struct io *io)
+{
+ struct ioloop_handler_context *ctx = ioloop->handler_context;
+ struct fdrecord * const fds = ctx->fds;
+ const int fd = io->fd;
+ struct kevent ev = {fd, 0, EV_DELETE, 0, 0, NULL};
+ enum io_condition condition = io->condition;
+
+
+ i_assert((size_t)fd < ctx->fds_size);
+ i_assert(fds[fd].mode != 0);
+
+ if (condition & IO_ERROR)
+ fds[fd].errio = NULL;
+ if (condition & (IO_READ | IO_ERROR))
+ {
+ ev.filter = EVFILT_READ;
+ fds[fd].mode &= ~condition;
+ if ((fds[fd].mode & (IO_READ | IO_ERROR)) == 0)
+ kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
+ }
+ if (condition & (IO_WRITE | IO_ERROR))
+ {
+ ev.filter = EVFILT_WRITE;
+ fds[fd].mode &= ~condition;
+ if ((fds[fd].mode & (IO_WRITE | IO_ERROR)) == 0)
+ kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
+ }
+}
+
+
+void io_loop_handler_run(struct ioloop *ioloop)
+{
+ struct ioloop_handler_context *ctx = ioloop->handler_context;
+ struct timeval tv;
+ struct timespec ts;
+ unsigned int t_id;
+ int msecs, ret, i;
+
+ /* get the time left for next timeout task */
+ msecs = io_loop_get_wait_time(ioloop->timeouts, &tv, NULL);
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+
+ /* wait for events */
+ ret = kevent (ctx->kq, NULL, 0, ctx->evbuf, ctx->evbuf_size, &ts);
+ if (ret < 0 && errno != EINTR)
+ i_fatal("kevent(): %m");
+
+ /* execute timeout handlers */
+ io_loop_handle_timeouts(ioloop);
+
+ if (ret <= 0 || !ioloop->running) {
+ /* no I/O events */
+ return;
+ }
+
+ i_assert((size_t)ret <= ctx->evbuf_size);
+
+ /* loop through all received events */
+ for (i = 0; i < ret; ++i)
+ {
+ struct io *io = ctx->evbuf[i].udata;
+
+ i_assert(ctx->evbuf[i].ident < ctx->fds_size);
+ if (ctx->fds[ctx->evbuf[i].ident].mode & IO_ERROR)
+ {
+ struct io *errio = ctx->fds[ctx->evbuf[i].ident].errio;
+
+ t_id = t_push();
+ errio->callback(errio->context);
+ if (t_pop() != t_id)
+ i_panic("Leaked a t_pop() call"
+ " in I/O handler %p",
+ (void *)errio->callback);
+ }
+
+ if (ctx->fds[ctx->evbuf[i].ident].mode & (IO_WRITE | IO_READ))
+ {
+ t_id = t_push();
+ io->callback(io->context);
+ if (t_pop() != t_id)
+ i_panic("Leaked a t_pop() call"
+ " in I/O handler %p",
+ (void *)io->callback);
+ }
+ }
+}
+
+
+#endif // IOLOOP_KQUEUE
+
+/*
+Local Variables:
+eval: (c-set-style "linux")
+whitespace-auto-cleanup: t
+End:
+*/
More information about the dovecot
mailing list