ioloop-kqueue.c revision ac042a14c663acdc16e98946ed22d53c3bfbd4fa
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen/*
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen * BSD kqueue() based ioloop handler.
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen *
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen * Copyright (c) 2005 Vaclav Haisman <v.haisman@sh.cvut.cz>
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen *
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen * This library is free software; you can redistribute it and/or modify
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen * it under the terms of the GNU Lesser General Public License as published
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen * by the Free Software Foundation; either version 2 of the License, or
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen * (at your option) any later version.
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen/* @UNSAFE: whole file */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include "lib.h"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include "ioloop-internal.h"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#ifdef IOLOOP_KQUEUE
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include <unistd.h>
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include <sys/types.h>
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include <sys/event.h>
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#include <sys/time.h>
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#ifndef INITIAL_BUF_SIZE
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen# define INITIAL_BUF_SIZE 128
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#endif
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#define MASK (IO_READ | IO_WRITE | IO_ERROR)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstruct ioloop_handler_context {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int kq;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size_t evbuf_size;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct kevent *evbuf;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size_t fds_size;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct fdrecord *fds;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen};
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenstruct fdrecord {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct io *errio;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen enum io_condition mode;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen};
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenvoid io_loop_handler_init(struct ioloop *ioloop)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ioloop_handler_context *ctx;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ioloop->handler_context = ctx =
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen p_new(ioloop->pool, struct ioloop_handler_context, 1);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->evbuf_size = INITIAL_BUF_SIZE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->evbuf = p_new(ioloop->pool, struct kevent, ctx->evbuf_size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->kq = kqueue();
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ctx->kq < 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_fatal("kqueue(): %m");
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->fds_size = INITIAL_BUF_SIZE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->fds = p_new(ioloop->pool, struct fdrecord, ctx->fds_size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenvoid io_loop_handler_deinit(struct ioloop *ioloop)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (close(ioloop->handler_context->kq) < 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_error("close(kqueue) failed: %m");
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen p_free(ioloop->pool, ioloop->handler_context->evbuf);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen p_free(ioloop->pool, ioloop->handler_context->fds);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen p_free(ioloop->pool, ioloop->handler_context);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenvoid io_loop_handle_add(struct ioloop *ioloop, struct io *io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ioloop_handler_context *ctx = ioloop->handler_context;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen const int fd = io->fd;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct kevent ev = { fd, 0, EV_ADD | EV_EOF, 0, 0, NULL };
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen enum io_condition condition = io->condition & MASK;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* grow ctx->fds array if necessary */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if ((size_t)fd >= ctx->fds_size) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen size_t old_size = ctx->fds_size;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->fds_size = nearest_power((unsigned int)fd+1);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert(ctx->fds_size < (size_t)-1 / sizeof(int));
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->fds = p_realloc(ioloop->pool, ctx->fds,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sizeof(struct fdrecord) * old_size,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sizeof(struct fdrecord) * ctx->fds_size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen memset(ctx->fds + old_size, 0,
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen sizeof(struct fdrecord) * (ctx->fds_size - old_size));
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (condition & (IO_READ | IO_WRITE))
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ev.udata = io;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (condition & IO_ERROR)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->fds[fd].errio = io;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (condition & (IO_READ | IO_ERROR)) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->fds[fd].mode |= condition;
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen ev.filter = EVFILT_READ;
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (condition & (IO_WRITE | IO_ERROR)) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ctx->fds[fd].mode |= condition;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ev.filter = EVFILT_WRITE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenvoid io_loop_handle_remove(struct ioloop *ioloop, struct io *io)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ioloop_handler_context *ctx = ioloop->handler_context;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen const int fd = io->fd;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct kevent ev = { fd, 0, EV_DELETE, 0, 0, NULL };
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct fdrecord *const fds = ctx->fds;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen const enum io_condition condition = io->condition & MASK;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert((size_t)fd < ctx->fds_size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert(fds[fd].mode != 0);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (condition & IO_ERROR)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen fds[fd].errio = NULL;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (condition & (IO_READ | IO_ERROR)) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ev.filter = EVFILT_READ;
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen fds[fd].mode &= ~condition;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if ((fds[fd].mode & (IO_READ | IO_ERROR)) == 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (condition & (IO_WRITE | IO_ERROR)) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ev.filter = EVFILT_WRITE;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen fds[fd].mode &= ~condition;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if ((fds[fd].mode & (IO_WRITE | IO_ERROR)) == 0)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen kevent(ctx->kq, &ev, 1, NULL, 0, NULL);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainenvoid io_loop_handler_run(struct ioloop *ioloop)
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen{
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct ioloop_handler_context *ctx = ioloop->handler_context;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct timeval tv;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct timespec ts;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen unsigned int t_id;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen int msecs, ret, i;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* get the time left for next timeout task */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen msecs = io_loop_get_wait_time(ioloop->timeouts, &tv, NULL);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ts.tv_sec = tv.tv_sec;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen ts.tv_nsec = tv.tv_usec * 1000;
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen /* wait for events */
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen ret = kevent (ctx->kq, NULL, 0, ctx->evbuf, ctx->evbuf_size, &ts);
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen if (ret < 0 && errno != EINTR)
1c4f8e4c4e5f3a5f05692a2d5c57f96a5b612f3dTimo Sirainen i_fatal("kevent(): %m");
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* execute timeout handlers */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen io_loop_handle_timeouts(ioloop);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (ret <= 0 || !ioloop->running) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* no I/O events */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen return;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert((size_t)ret <= ctx->evbuf_size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen /* loop through all received events */
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen for (i = 0; i < ret; ++i) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct io *io = ctx->evbuf[i].udata;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_assert(ctx->evbuf[i].ident < ctx->fds_size);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if ((ctx->fds[ctx->evbuf[i].ident].mode & IO_ERROR) &&
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen (ctx->evbuf[i].flags & EV_EOF)) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen struct io *errio = ctx->fds[ctx->evbuf[i].ident].errio;
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen t_id = t_push();
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen errio->callback(errio->context);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (t_pop() != t_id) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_panic("Leaked a t_pop() call"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen " in I/O handler %p",
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen (void *)errio->callback);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen }
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen } else if (ctx->fds[ctx->evbuf[i].ident].mode
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen & (IO_WRITE | IO_READ)) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen t_id = t_push();
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen io->callback(io->context);
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen if (t_pop() != t_id) {
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen i_panic("Leaked a t_pop() call"
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen " in I/O handler %p",
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen (void *)io->callback);
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen }
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen } else
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen i_panic("Unrecognized event");
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen }
21fed972adb354b92771eefad27f8ac8cbd5dd45Timo Sirainen}
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen#endif
e98de01b5644c88b6053e2921eb5e9a506fe263fTimo Sirainen