# HG changeset patch # User Timo Sirainen # Date 1134586312 -7200 # Node ID 194295062e5e72d304e50793756a80fdee494f02 # Parent 1649ca519b7d1189e5c7d355cc04d97a5b3388c2 Added kqueue support. Patch by Vaclav Haisman. diff -r 1649ca519b7d -r 194295062e5e AUTHORS --- a/AUTHORS Wed Dec 14 19:45:06 2005 +0200 +++ b/AUTHORS Wed Dec 14 20:51:52 2005 +0200 @@ -35,3 +35,5 @@ Simon Tatham (src/lib-storage/mail-thread.c merge sorting) Grepping 'Patch by' from ChangeLog shows up more people. + +Vaclav Haisman (src/lib/ioloop-kqueue.c) diff -r 1649ca519b7d -r 194295062e5e configure.in --- a/configure.in Wed Dec 14 19:45:06 2005 +0200 +++ b/configure.in Wed Dec 14 20:51:52 2005 +0200 @@ -327,6 +327,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 -r 1649ca519b7d -r 194295062e5e src/lib/Makefile.am --- a/src/lib/Makefile.am Wed Dec 14 19:45:06 2005 +0200 +++ b/src/lib/Makefile.am Wed Dec 14 20:51:52 2005 +0200 @@ -35,6 +35,7 @@ ioloop-poll.c \ ioloop-select.c \ ioloop-epoll.c \ + ioloop-kqueue.c \ lib.c \ lib-signals.c \ md4.c \ diff -r 1649ca519b7d -r 194295062e5e src/lib/ioloop-kqueue.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/ioloop-kqueue.c Wed Dec 14 20:51:52 2005 +0200 @@ -0,0 +1,187 @@ +/* + * FreeBSD kqueue() based ioloop handler. + * + * Copyright (c) 2005 Vaclav Haisman + * + * 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 +#include +#include + +#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; + enum io_condition mode; +}; + +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); + 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); +} + +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 kevent ev = { fd, 0, EV_DELETE, 0, 0, NULL }; + struct fdrecord *const fds = ctx->fds; + const int fd = io->fd; + const 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