Vaclav Haisman
2005-Dec-14 12:50 UTC
[Dovecot] Patch: ioloop using kqueue/kevent for FreeBSD
Hi,
I would like to submit the attached patch. It implements IO loop using
FreeBSD's kqueue/kevent syscalls. It is based on snapshot of CVS HEAD as of
2005-12-12.
I could only give it limited testing on FreeBSD 5.4 but it works fine so far.
Vaclav Haisman
-------------- next part --------------
diff -rN -u old-dovecot-cvs/autogen.sh new-dovecot-cvs/autogen.sh
--- old-dovecot-cvs/autogen.sh 2005-12-14 11:35:03.537711451 +0100
+++ new-dovecot-cvs/autogen.sh 2005-12-14 11:35:06.149980951 +0100
@@ -1,5 +1,5 @@
-aclocal
-libtoolize --force
-automake --add-missing
-autoheader
-autoconf
+aclocal15
+libtoolize13 --force
+automake15 --add-missing
+autoheader259
+autoconf259
diff -rN -u old-dovecot-cvs/configure.in new-dovecot-cvs/configure.in
--- old-dovecot-cvs/configure.in 2005-12-14 11:35:03.545823016 +0100
+++ new-dovecot-cvs/configure.in 2005-12-14 11:35:06.150746230 +0100
@@ -1,6 +1,10 @@
AC_INIT(dovecot, 1.0.alpha5, [dovecot@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 11:35:03.542457074 +0100
+++ new-dovecot-cvs/src/lib/Makefile.am 2005-12-14 11:35:03.660215582 +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 11:35:03.751180389 +0100
@@ -0,0 +1,183 @@
+/*
+ * FreeBSD kqueue() based ioloop handler.
+ *
+ * Copyright (c) 2005 Vaclav Haisman <v.haisman@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 {
+ /* 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;
+ struct kevent ev = {io->fd, 0, EV_ADD | EV_CLEAR | EV_EOF, 0, 0,
io};
+ enum io_condition condition = io->condition;
+
+ /* grow ctx->fds array if necessary */
+ if ((size_t)io->fd >= ctx->fds_size) {
+ size_t old_size = ctx->fds_size;
+
+ ctx->fds_size = nearest_power((unsigned int)io->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_ERROR))
+ {
+ ctx->fds[io->fd].mode |= condition;
+ ev.filter = EVFILT_READ;
+ kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
+ }
+ if (condition & (IO_WRITE | IO_ERROR))
+ {
+ ctx->fds[io->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_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;
+
+ 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:
+*/
Vaclav Haisman
2005-Dec-14 18:39 UTC
[Dovecot] Patch: ioloop using kqueue/kevent for FreeBSD
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@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@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:
+*/
Can you please change the instances of FreeBSD in the autoconf script and the code to just BSD. On Wed, Dec 14, 2005 at 11:54:10AM +0100, Vaclav Haisman wrote:> Hi, > I would like to submit the attached patch. It implements IO loop using > FreeBSD's kqueue/kevent syscalls. It is based on snapshot of CVS HEAD as of > 2005-12-12. > > I could only give it limited testing on FreeBSD 5.4 but it works fine so > far. > > > Vaclav HaismanContent-Description: ioloop using FreeBSD kqueue/kevent> diff -rN -u old-dovecot-cvs/autogen.sh new-dovecot-cvs/autogen.sh > --- old-dovecot-cvs/autogen.sh 2005-12-14 11:35:03.537711451 +0100 > +++ new-dovecot-cvs/autogen.sh 2005-12-14 11:35:06.149980951 +0100 > @@ -1,5 +1,5 @@ > -aclocal > -libtoolize --force > -automake --add-missing > -autoheader > -autoconf > +aclocal15 > +libtoolize13 --force > +automake15 --add-missing > +autoheader259 > +autoconf259 > diff -rN -u old-dovecot-cvs/configure.in new-dovecot-cvs/configure.in > --- old-dovecot-cvs/configure.in 2005-12-14 11:35:03.545823016 +0100 > +++ new-dovecot-cvs/configure.in 2005-12-14 11:35:06.150746230 +0100 > @@ -1,6 +1,10 @@ > AC_INIT(dovecot, 1.0.alpha5, [dovecot@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 11:35:03.542457074 +0100 > +++ new-dovecot-cvs/src/lib/Makefile.am 2005-12-14 11:35:03.660215582 +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 11:35:03.751180389 +0100 > @@ -0,0 +1,183 @@ > +/* > + * FreeBSD kqueue() based ioloop handler. > + * > + * Copyright (c) 2005 Vaclav Haisman <v.haisman@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 { > + /* 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; > + struct kevent ev = {io->fd, 0, EV_ADD | EV_CLEAR | EV_EOF, 0, 0, io}; > + enum io_condition condition = io->condition; > + > + /* grow ctx->fds array if necessary */ > + if ((size_t)io->fd >= ctx->fds_size) { > + size_t old_size = ctx->fds_size; > + > + ctx->fds_size = nearest_power((unsigned int)io->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_ERROR)) > + { > + ctx->fds[io->fd].mode |= condition; > + ev.filter = EVFILT_READ; > + kevent(ctx->kq, &ev, 1, NULL, 0, NULL); > + } > + if (condition & (IO_WRITE | IO_ERROR)) > + { > + ctx->fds[io->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_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; > + > + 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: > +*/ >