Mercurial > dovecot > original-hg > dovecot-1.2
changeset 3958:3e9b43d0cd80 HEAD
kqueue updates. Patch by Vaclav Haisman
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 29 Jan 2006 12:32:37 +0200 |
parents | 8f0ff62befd3 |
children | ef5595d6ddec |
files | src/lib/ioloop-kqueue.c src/lib/ioloop-notify-kqueue.c src/lib/ioloop.c |
diffstat | 3 files changed, 170 insertions(+), 127 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib/ioloop-kqueue.c Sun Jan 29 11:54:32 2006 +0200 +++ b/src/lib/ioloop-kqueue.c Sun Jan 29 12:32:37 2006 +0200 @@ -12,11 +12,13 @@ /* @UNSAFE: whole file */ #include "lib.h" -#include "ioloop-internal.h" #ifdef IOLOOP_KQUEUE +#include "fd-close-on-exec.h" +#include "ioloop-internal.h" #include <unistd.h> +#include <fcntl.h> #include <sys/types.h> #include <sys/event.h> #include <sys/time.h> @@ -28,166 +30,182 @@ #define MASK (IO_READ | IO_WRITE | IO_ERROR) struct ioloop_handler_context { - int kq; - size_t evbuf_size; - struct kevent *evbuf; + int kq; + size_t evbuf_size; + struct kevent *evbuf; - size_t fds_size; - struct fdrecord *fds; + size_t fds_size; + struct fdrecord *fds; }; struct fdrecord { - struct io *errio; - enum io_condition mode; + struct io *errio; + enum io_condition mode; }; void io_loop_handler_init(struct ioloop *ioloop) { - struct ioloop_handler_context *ctx; + struct ioloop_handler_context *ctx; - ioloop->handler_context = ctx = - p_new(ioloop->pool, struct ioloop_handler_context, 1); + 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->evbuf = p_new(ioloop->pool, struct kevent, ctx->evbuf_size); + ctx->kq = kqueue(); + if (ctx->kq < 0) + i_fatal("kqueue() in io_loop_handler_init() failed: %m"); + fd_close_on_exec(ctx->kq, TRUE); - ctx->fds_size = INITIAL_BUF_SIZE; - ctx->fds = p_new(ioloop->pool, struct fdrecord, ctx->fds_size); + 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) { if (close(ioloop->handler_context->kq) < 0) - i_error("close(kqueue) failed: %m"); - p_free(ioloop->pool, ioloop->handler_context->evbuf); - p_free(ioloop->pool, ioloop->handler_context->fds); - p_free(ioloop->pool, ioloop->handler_context); + i_error("close(kqueue) in io_loop_handler_deinit() failed: %m"); + 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_EOF, 0, 0, NULL }; - enum io_condition condition = io->condition & MASK; + struct ioloop_handler_context *ctx = ioloop->handler_context; + const int fd = io->fd; + struct kevent ev = { fd, 0, EV_ADD | EV_EOF, 0, 0, NULL }; + enum io_condition condition = io->condition & MASK; + + i_assert(io->callback != NULL); - /* grow ctx->fds array if necessary */ - if ((size_t)fd >= ctx->fds_size) { - size_t old_size = ctx->fds_size; + /* 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_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)); + } - 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_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); - } + if (condition & (IO_READ | IO_ERROR)) { + ctx->fds[fd].mode |= condition; + ev.filter = EVFILT_READ; + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) { + i_error("kevent(%d) in io_loop_handle_add() failed: %m", + fd); + } + } + if (condition & (IO_WRITE | IO_ERROR)) { + ctx->fds[fd].mode |= condition; + ev.filter = EVFILT_WRITE; + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) { + i_error("kevent(%d) in io_loop_handle_add() failed: %m", + fd); + } + } } void io_loop_handle_remove(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_DELETE, 0, 0, NULL }; - struct fdrecord *const fds = ctx->fds; - const enum io_condition condition = io->condition & MASK; + struct ioloop_handler_context *ctx = ioloop->handler_context; + const int fd = io->fd; + struct kevent ev = { fd, 0, EV_DELETE, 0, 0, NULL }; + struct fdrecord *const fds = ctx->fds; + const enum io_condition condition = io->condition & MASK; - i_assert((size_t)fd < ctx->fds_size); - i_assert(fds[fd].mode != 0); + i_assert((size_t)fd < ctx->fds_size); - 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); - } + 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) { + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) { + i_error("kevent(%d) in io_loop_handle_remove " + "failed: %m", fd); + } + } + } + if (condition & (IO_WRITE | IO_ERROR)) { + ev.filter = EVFILT_WRITE; + fds[fd].mode &= ~condition; + if ((fds[fd].mode & (IO_WRITE | IO_ERROR)) == 0) { + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) { + i_error("kevent(%d) in io_loop_handle_remove " + "failed: %m", fd); + } + } + } } 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; + 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; + /* 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"); + /* 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); + /* execute timeout handlers */ + io_loop_handle_timeouts(ioloop); - if (ret <= 0 || !ioloop->running) { - /* no I/O events */ - return; - } + if (ret <= 0 || !ioloop->running) { + /* no I/O events */ + return; + } - i_assert((size_t)ret <= ctx->evbuf_size); + 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; + /* 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); + i_assert(ctx->evbuf[i].ident < ctx->fds_size); if ((ctx->fds[ctx->evbuf[i].ident].mode & IO_ERROR) && (ctx->evbuf[i].flags & EV_EOF)) { - struct io *errio = ctx->fds[ctx->evbuf[i].ident].errio; + struct io *errio = ctx->fds[ctx->evbuf[i].ident].errio; - t_id = t_push(); - errio->callback(errio->context); + 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", + i_panic("Leaked a t_pop() call" + " in I/O handler %p", (void *)errio->callback); } - } else if (ctx->fds[ctx->evbuf[i].ident].mode - & (IO_WRITE | IO_READ)) { - t_id = t_push(); - io->callback(io->context); + } else 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", + i_panic("Leaked a t_pop() call" + " in I/O handler %p", (void *)io->callback); } - } else - i_panic("Unrecognized event"); - } + } else + i_panic("Unrecognized event"); + } } #endif
--- a/src/lib/ioloop-notify-kqueue.c Sun Jan 29 11:54:32 2006 +0200 +++ b/src/lib/ioloop-notify-kqueue.c Sun Jan 29 12:32:37 2006 +0200 @@ -15,11 +15,13 @@ #ifdef IOLOOP_NOTIFY_KQUEUE #include "ioloop-internal.h" +#include "fd-close-on-exec.h" #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/event.h> #include <sys/time.h> +#include <sys/stat.h> struct ioloop_notify_handler_context { int kq; @@ -53,7 +55,8 @@ ctx->event_io = NULL; ctx->kq = kqueue(); if (ctx->kq < 0) - i_fatal("kqueue() failed: %m"); + i_fatal("kqueue() in io_loop_notify_handler_init() failed: %m"); + fd_close_on_exec(ctx->kq, TRUE); } void io_loop_notify_handler_deinit(struct ioloop *ioloop) @@ -61,8 +64,8 @@ struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; - if (ctx->event_io) - io_remove(ctx->event_io); + if (ctx->event_io) + io_remove(&ctx->event_io); if (close(ctx->kq) < 0) i_error("close(kqueue notify) failed: %m"); p_free(ioloop->pool, ctx); @@ -75,6 +78,10 @@ for (io_p = &ioloop->notifys; *io_p != NULL; io_p = &(*io_p)->next) { if (*io_p == io) { *io_p = io->next; + if (io->next != NULL) + io->next->prev = io->prev; + io->prev = NULL; + io->next = NULL; break; } } @@ -90,25 +97,42 @@ | NOTE_REVOKE, 0, NULL }; struct io *io; int fd; + struct stat sb; + + i_assert(callback != NULL); fd = open(path, O_RDONLY); if (fd == -1) { - i_error("open(%s) for notify failed: %m", path); + i_error("open(%s) for kq notify failed: %m", path); return NULL; } - ev.ident = fd; - ev.udata = io; - if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) { - i_error("kevent(%s) for notify failed: %m", path); + if (fstat(fd, &sb) < 0) { + i_error("fstat(%d, %s) for kq notify failed: %m", fd, path); + (void)close(fd); return NULL; } + if (!S_ISDIR(sb.st_mode)) { + (void)close(fd); + return NULL; + } + fd_close_on_exec(fd, TRUE); io = p_new(ioloop->pool, struct io, 1); io->fd = fd; io->callback = callback; io->context = context; + ev.ident = fd; + ev.udata = io; + if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) { + i_error("kevent(%d, %s) for notify failed: %m", fd, path); + p_free(ioloop->pool, io); + return NULL; + } io->next = ioloop->notifys; + io->prev = NULL; + if (ioloop->notifys != NULL) + ioloop->notifys->prev = io; ioloop->notifys = io; if (ctx->event_io == NULL) { @@ -116,7 +140,6 @@ io_add(ctx->kq, IO_READ, event_callback, ioloop->notify_handler_context); } - return io; } @@ -124,15 +147,16 @@ { struct ioloop_notify_handler_context *ctx = ioloop->notify_handler_context; - struct kevent ev = { io->fd, 0, EV_DELETE, 0, 0, NULL }; - int ret; + struct kevent ev = { io->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL }; + + i_assert((io->condition & IO_NOTIFY) != 0); + if (kevent(ctx->kq, &ev, 1, NULL, 0, 0) < 0) + i_error("kevent(%d) for notify remove failed: %m", io->fd); + if (close(io->fd) < 0) + i_error("close(%d) failed: %m", io->fd); unchain_io(ioloop, io); p_free(ioloop->pool, io); - - ret = kevent(ctx->kq, &ev, 1, NULL, 0, 0); - if (ret == -1) - i_error("kevent() for notify failed: %m"); } #endif
--- a/src/lib/ioloop.c Sun Jan 29 11:54:32 2006 +0200 +++ b/src/lib/ioloop.c Sun Jan 29 12:32:37 2006 +0200 @@ -279,6 +279,8 @@ *_ioloop = NULL; + io_loop_notify_handler_deinit(ioloop); + while (ioloop->ios != NULL) { struct io *io = ioloop->ios; @@ -295,8 +297,7 @@ } timeout_destroy(ioloop, &ioloop->timeouts); } - - io_loop_notify_handler_deinit(ioloop); + io_loop_handler_deinit(ioloop); /* ->prev won't work unless loops are destroyed in create order */