Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib/ioloop-epoll.c @ 3749:194295062e5e HEAD
Added kqueue support. Patch by Vaclav Haisman.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 14 Dec 2005 20:51:52 +0200 |
parents | dd71819db08e |
children | 55df57c028d4 |
line wrap: on
line source
/* * Linux epoll() based ioloop handler. * * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru> * * 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 "array.h" #include "fd-close-on-exec.h" #include "ioloop-internal.h" #ifdef IOLOOP_EPOLL #include <sys/epoll.h> #include <unistd.h> #define INITIAL_EPOLL_EVENTS 128 enum { EPOLL_LIST_INPUT, EPOLL_LIST_OUTPUT, EPOLL_LIST_ERROR, EPOLL_IOS_PER_FD }; struct ioloop_handler_context { int epfd; int events_size, events_pos; struct epoll_event *events; unsigned int idx_size; array_t ARRAY_DEFINE(fd_index, struct io_list *); }; struct io_list { struct io *ios[EPOLL_IOS_PER_FD]; }; 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->events_pos = 0; ctx->events_size = INITIAL_EPOLL_EVENTS; ctx->events = p_new(ioloop->pool, struct epoll_event, ctx->events_size); ctx->idx_size = INITIAL_EPOLL_EVENTS; ARRAY_CREATE(&ctx->fd_index, ioloop->pool, struct io_list *, ctx->idx_size); ctx->epfd = epoll_create(INITIAL_EPOLL_EVENTS); if (ctx->epfd < 0) i_fatal("epoll_create(): %m"); fd_close_on_exec(ctx->epfd, TRUE); } void io_loop_handler_deinit(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; if (close(ctx->epfd) < 0) i_error("close(epoll) failed: %m"); array_free(&ioloop->handler_context->fd_index); p_free(ioloop->pool, ioloop->handler_context->events); p_free(ioloop->pool, ioloop->handler_context); } #define IO_EPOLL_ERROR (EPOLLERR | EPOLLHUP) #define IO_EPOLL_INPUT (EPOLLIN | EPOLLPRI | IO_EPOLL_ERROR) #define IO_EPOLL_OUTPUT (EPOLLOUT | IO_EPOLL_ERROR) static int epoll_event_mask(struct io_list *list) { int events = 0, i; struct io *io; for (i = 0; i < EPOLL_IOS_PER_FD; i++) { io = list->ios[i]; if (io == NULL) continue; if (io->condition & IO_READ) events |= IO_EPOLL_INPUT; if (io->condition & IO_WRITE) events |= IO_EPOLL_OUTPUT; if (io->condition & IO_ERROR) events |= IO_EPOLL_ERROR; } return events; } static int iolist_add(struct io_list *list, struct io *io) { int i, idx; if ((io->condition & IO_READ) != 0) idx = EPOLL_LIST_INPUT; else if ((io->condition & IO_WRITE) != 0) idx = EPOLL_LIST_OUTPUT; else if ((io->condition & IO_ERROR) != 0) idx = EPOLL_LIST_ERROR; else { i_unreached(); } i_assert(list->ios[idx] == NULL); list->ios[idx] = io; /* check if this was the first one */ for (i = 0; i < EPOLL_IOS_PER_FD; i++) { if (i != idx && list->ios[i] != NULL) return FALSE; } return TRUE; } static int iolist_del(struct io_list *list, struct io *io) { int i, last = TRUE; for (i = 0; i < EPOLL_IOS_PER_FD; i++) { if (list->ios[i] != NULL) { if (list->ios[i] == io) list->ios[i] = NULL; else last = TRUE; } } return last; } void io_loop_handle_add(struct ioloop *ioloop, struct io *io) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct io_list **list; struct epoll_event event; int ret, first, op, fd = io->fd; list = array_idx_modifyable(&ctx->fd_index, fd); if (*list == NULL) *list = p_new(ioloop->pool, struct io_list, 1); first = iolist_add(*list, io); event.data.ptr = *list; event.events = epoll_event_mask(*list); op = first ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; ret = epoll_ctl(ctx->epfd, op, fd, &event); if (ret < 0) i_fatal("io_loop_handle_add: epoll_ctl(%d, %d): %m", op, fd); if (ctx->events_pos >= ctx->events_size) { ctx->events_size = nearest_power(ctx->events_size + 1); p_free(ioloop->pool, ctx->events); ctx->events = p_new(ioloop->pool, struct epoll_event, ctx->events_size); } ctx->events_pos++; } void io_loop_handle_remove(struct ioloop *ioloop, struct io *io) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct io_list **list; struct epoll_event event; int ret, last, op; list = array_idx_modifyable(&ctx->fd_index, io->fd); last = iolist_del(*list, io); event.data.ptr = *list; event.events = epoll_event_mask(*list); op = last ? EPOLL_CTL_DEL : EPOLL_CTL_MOD; ret = epoll_ctl(ctx->epfd, op, io->fd, &event); if (ret < 0 && errno != EBADF) { i_fatal("io_loop_handle_remove: epoll_ctl(%d, %d): %m", op, io->fd); } ctx->events_pos--; } void io_loop_handler_run(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct epoll_event *event; struct io_list *list; struct io *io; struct timeval tv; unsigned int t_id; int msecs, ret, i, call; /* get the time left for next timeout task */ msecs = io_loop_get_wait_time(ioloop->timeouts, &tv, NULL); ret = epoll_wait(ctx->epfd, ctx->events, ctx->events_size, msecs); if (ret < 0 && errno != EINTR) i_fatal("epoll_wait(): %m"); /* execute timeout handlers */ io_loop_handle_timeouts(ioloop); if (ret <= 0 || !ioloop->running) { /* No events */ return; } event = ctx->events; while (ret-- > 0) { list = event->data.ptr; for (i = 0; i < EPOLL_IOS_PER_FD; i++) { io = list->ios[i]; if (io == NULL) continue; call = FALSE; if ((event->events & (EPOLLHUP | EPOLLERR)) != 0) call = TRUE; else if ((io->condition & IO_READ) != 0) call = (event->events & EPOLLIN) != 0; else if ((io->condition & IO_WRITE) != 0) call = (event->events & EPOLLOUT) != 0; else if ((io->condition & IO_ERROR) != 0) call = (event->events & IO_EPOLL_ERROR) != 0; if (call) { 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); } } } event++; } } #endif /* IOLOOP_EPOLL */