6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher/*
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher * BSD kqueue() based ioloop handler.
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher *
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher * Copyright (c) 2005 Vaclav Haisman <v.haisman@sh.cvut.cz>
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher */
fbeb1aba9e11e7aab8adac943276ca040f0c5311Jakub Hrozek
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek#include "lib.h"
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek#ifdef IOLOOP_KQUEUE
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek#include "array.h"
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek#include "ioloop-private.h"
2cb6f28b3a12bb714bf14494d31eb6b6fff64b8bJakub Hrozek
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher#include <unistd.h>
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher#include <fcntl.h>
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek#include <sys/types.h>
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher#include <sys/event.h>
7465d6a1ef6e83825dba3a4dc4dda7271671aba0Jakub Hrozek#include <sys/time.h>
ad805face83ba7d67b1cf2067a1982c7e63d1060Jakub Hrozek
ad805face83ba7d67b1cf2067a1982c7e63d1060Jakub Hrozek/* kevent.udata's type just has to be different in NetBSD than in
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek FreeBSD and OpenBSD.. */
a7797068c4deb6ce2bdbcda27c45ff1bbb4a8e78Jakub Hrozek#ifdef __NetBSD__
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher# define MY_EV_SET(a, b, c, d, e, f, g) \
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher EV_SET(a, b, c, d, e, f, (intptr_t)g)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher#else
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher# define MY_EV_SET(a, b, c, d, e, f, g) \
7a14e8f66c0e932fe2954d792614a3b61d444bd1Jakub Hrozek EV_SET(a, b, c, d, e, f, g)
36b56482ca1e53d832accef0354124fd79711172Jakub Hrozek#endif
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagherstruct ioloop_handler_context {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher int kq;
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek unsigned int deleted_count;
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek ARRAY(struct kevent) events;
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek};
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozekvoid io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count)
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek{
d25fa6f2608d5fe0617ada47f9d426f45deb96ffJakub Hrozek struct ioloop_handler_context *ctx;
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher ctx->kq = kqueue();
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (ctx->kq < 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_fatal("kqueue() in io_loop_handler_init() failed: %m");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher fd_close_on_exec(ctx->kq, TRUE);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_array_init(&ctx->events, initial_fd_count);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher}
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghervoid io_loop_handler_deinit(struct ioloop *ioloop)
f10ebaa51ecdcbbd10f171d19fe8e680e5bc74aaJakub Hrozek{
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek if (close(ioloop->handler_context->kq) < 0)
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek i_error("close(kqueue) in io_loop_handler_deinit() failed: %m");
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek array_free(&ioloop->handler_context->events);
261cdde02b40aa8dabb3d69e43586a5a220647e9Jakub Hrozek i_free(ioloop->handler_context);
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek}
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghervoid io_loop_handle_add(struct io_file *io)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher{
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher struct ioloop_handler_context *ctx = io->io.ioloop->handler_context;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher struct kevent ev;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if ((io->io.condition & (IO_READ | IO_ERROR)) != 0) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_ADD, 0, 0, io);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_panic("kevent(EV_ADD, READ, %d) failed: %m", io->fd);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher }
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher if ((io->io.condition & IO_WRITE) != 0) {
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_ADD, 0, 0, io);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_panic("kevent(EV_ADD, WRITE, %d) failed: %m", io->fd);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher }
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher /* allow kevent() to return the maximum number of events
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher by keeping space allocated for each handle */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (ctx->deleted_count > 0)
f10ebaa51ecdcbbd10f171d19fe8e680e5bc74aaJakub Hrozek ctx->deleted_count--;
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek else
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek array_append_zero(&ctx->events);
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek}
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozekvoid io_loop_handle_remove(struct io_file *io, bool closed)
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek{
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek struct ioloop_handler_context *ctx = io->io.ioloop->handler_context;
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek struct kevent ev;
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek i_assert(io->io.condition != 0);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if ((io->io.condition & (IO_READ | IO_ERROR)) != 0 && !closed) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_error("kevent(EV_DELETE, %d) failed: %m", io->fd);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher }
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if ((io->io.condition & IO_WRITE) != 0 && !closed) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_error("kevent(EV_DELETE, %d) failed: %m", io->fd);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher }
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher io->io.condition = 0;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* since we're not freeing memory in any case, just increase
f10ebaa51ecdcbbd10f171d19fe8e680e5bc74aaJakub Hrozek deleted counter so next handle_add() can just decrease it
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher instead of appending to the events array */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher ctx->deleted_count++;
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek i_assert(io->refcount > 0);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (--io->refcount == 0)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_free(io);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher}
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallaghervoid io_loop_handler_run_internal(struct ioloop *ioloop)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher{
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher struct ioloop_handler_context *ctx = ioloop->handler_context;
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher struct kevent *events;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher const struct kevent *event;
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher struct timeval tv;
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher struct timespec ts;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher struct io_file *io;
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher unsigned int events_count;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher int ret, i, msecs;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher /* get the time left for next timeout task */
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher msecs = io_loop_get_wait_time(ioloop, &tv);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher ts.tv_sec = tv.tv_sec;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher ts.tv_nsec = tv.tv_usec * 1000;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher /* wait for events */
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher events = array_get_modifiable(&ctx->events, &events_count);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher if (events_count > 0) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher ret = kevent (ctx->kq, NULL, 0, events, events_count, &ts);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (ret < 0 && errno != EINTR) {
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher i_panic("kevent(events=%u, ts=%ld.%u) failed: %m",
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher events_count, (long)ts.tv_sec,
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher (unsigned int)ts.tv_nsec);
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher }
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher } else {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (msecs < 0)
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher i_panic("BUG: No IOs or timeouts set. Not waiting for infinity.");
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher usleep(msecs * 1000);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher ret = 0;
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher }
1008001f34abb42df75f840db17f14a83f0c21d4Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* reference all IOs */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher for (i = 0; i < ret; i++) {
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher io = (void *)events[i].udata;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher i_assert(io->refcount > 0);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher io->refcount++;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher }
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* execute timeout handlers */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher io_loop_handle_timeouts(ioloop);
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek for (i = 0; i < ret; i++) {
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek /* io_loop_handle_add() may cause events array reallocation,
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek so we have use array_idx() */
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek event = array_idx(&ctx->events, i);
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek io = (void *)event->udata;
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher /* callback is NULL if io_remove() was already called */
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher if (io->io.callback != NULL)
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher io_loop_call_io(&io->io);
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek i_assert(io->refcount > 0);
b47fd11a259c50e63cd674c7cba0da3f2549cae0Jakub Hrozek if (--io->refcount == 0)
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek i_free(io);
9a839b29816c8906d4a6b074cf76df790cac9209Jakub Hrozek }
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher}
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher#endif
6b0f9cd2ee601121cb7fe1d9ad8ebce782aa8f39Stephen Gallagher