ioloop-epoll.c revision 720692523ece4a549f7c589508d5693ee310f6b3
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/*
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen * Linux epoll() based ioloop handler.
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen *
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen *
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen * This software is released under the MIT license.
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen */
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen#include "lib.h"
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen#include "array.h"
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen#include "fd-close-on-exec.h"
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen#include "ioloop-internal.h"
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen#include "ioloop-iolist.h"
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen#ifdef IOLOOP_EPOLL
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen#include <sys/epoll.h>
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen#include <unistd.h>
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainenstruct ioloop_handler_context {
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen int epfd;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen unsigned int deleted_count;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen ARRAY_DEFINE(fd_index, struct io_list *);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen ARRAY_DEFINE(events, struct epoll_event);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen};
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainenvoid io_loop_handler_init(struct ioloop *ioloop)
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen{
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen struct ioloop_handler_context *ctx;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen ioloop->handler_context = ctx =
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen p_new(ioloop->pool, struct ioloop_handler_context, 1);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen p_array_init(&ctx->events, ioloop->pool, IOLOOP_INITIAL_FD_COUNT);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen p_array_init(&ctx->fd_index, ioloop->pool, IOLOOP_INITIAL_FD_COUNT);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen ctx->epfd = epoll_create(IOLOOP_INITIAL_FD_COUNT);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen if (ctx->epfd < 0)
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen i_fatal("epoll_create(): %m");
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen fd_close_on_exec(ctx->epfd, TRUE);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen}
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainenvoid io_loop_handler_deinit(struct ioloop *ioloop)
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen{
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen struct ioloop_handler_context *ctx = ioloop->handler_context;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen struct io_list **list;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen unsigned int i, count;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen list = array_get_modifyable(&ctx->fd_index, &count);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen for (i = 0; i < count; i++)
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen p_free(ioloop->pool, list[i]);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen if (close(ctx->epfd) < 0)
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen i_error("close(epoll) failed: %m");
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen array_free(&ioloop->handler_context->fd_index);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen array_free(&ioloop->handler_context->events);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen p_free(ioloop->pool, ioloop->handler_context);
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen}
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
0ddff4c8aefa66f7e19eddc6d61cd040fb803321Timo Sirainen#define IO_EPOLL_ERROR (EPOLLERR | EPOLLHUP)
906520ee2cece20c875835697db08cd5e29b919bTimo Sirainen#define IO_EPOLL_INPUT (EPOLLIN | EPOLLPRI | IO_EPOLL_ERROR)
3efdcb59492bd1e0602340a4204003a32b34654aTimo Sirainen#define IO_EPOLL_OUTPUT (EPOLLOUT | IO_EPOLL_ERROR)
906520ee2cece20c875835697db08cd5e29b919bTimo Sirainen
906520ee2cece20c875835697db08cd5e29b919bTimo Sirainenstatic int epoll_event_mask(struct io_list *list)
906520ee2cece20c875835697db08cd5e29b919bTimo Sirainen{
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen int events = 0, i;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen struct io *io;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
3efdcb59492bd1e0602340a4204003a32b34654aTimo Sirainen for (i = 0; i < IOLOOP_IOLIST_IOS_PER_FD; i++) {
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen io = list->ios[i];
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
3efdcb59492bd1e0602340a4204003a32b34654aTimo Sirainen if (io == NULL)
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen continue;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen if (io->condition & IO_READ)
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen events |= IO_EPOLL_INPUT;
906520ee2cece20c875835697db08cd5e29b919bTimo Sirainen if (io->condition & IO_WRITE)
3efdcb59492bd1e0602340a4204003a32b34654aTimo Sirainen events |= IO_EPOLL_OUTPUT;
906520ee2cece20c875835697db08cd5e29b919bTimo Sirainen if (io->condition & IO_ERROR)
906520ee2cece20c875835697db08cd5e29b919bTimo Sirainen events |= IO_EPOLL_ERROR;
906520ee2cece20c875835697db08cd5e29b919bTimo Sirainen }
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen return events;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen}
3efdcb59492bd1e0602340a4204003a32b34654aTimo Sirainen
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainenvoid io_loop_handle_add(struct ioloop *ioloop, struct io *io)
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen{
3efdcb59492bd1e0602340a4204003a32b34654aTimo Sirainen struct ioloop_handler_context *ctx = ioloop->handler_context;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen struct io_list **list;
f877ee033a0737100c2f661a7ca4c559ea2ddb8aTimo Sirainen struct epoll_event event;
int op;
bool first;
list = array_idx_modifiable(&ctx->fd_index, io->fd);
if (*list == NULL)
*list = p_new(ioloop->pool, struct io_list, 1);
first = ioloop_iolist_add(*list, io);
memset(&event, 0, sizeof(event));
event.data.ptr = *list;
event.events = epoll_event_mask(*list);
op = first ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
if (epoll_ctl(ctx->epfd, op, io->fd, &event) < 0) {
i_fatal("io_loop_handle_add: epoll_ctl(%d, %d): %m",
op, io->fd);
}
if (first) {
/* allow epoll_wait() to return the maximum number of events
by keeping space allocated for each file descriptor */
if (ctx->deleted_count > 0)
ctx->deleted_count--;
else
(void)array_append_space(&ctx->events);
}
}
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 op;
bool last;
list = array_idx_modifiable(&ctx->fd_index, io->fd);
last = ioloop_iolist_del(*list, io);
memset(&event, 0, sizeof(event));
event.data.ptr = *list;
event.events = epoll_event_mask(*list);
op = last ? EPOLL_CTL_DEL : EPOLL_CTL_MOD;
if (epoll_ctl(ctx->epfd, op, io->fd, &event) < 0) {
i_error("io_loop_handle_remove: epoll_ctl(%d, %d): %m",
op, io->fd);
}
if (last) {
/* since we're not freeing memory in any case, just increase
deleted counter so next handle_add() can just decrease it
insteading of appending to the events array */
ctx->deleted_count++;
}
}
void io_loop_handler_run(struct ioloop *ioloop)
{
struct ioloop_handler_context *ctx = ioloop->handler_context;
struct epoll_event *events;
const struct epoll_event *event;
struct io_list *list;
struct io *io;
struct timeval tv;
unsigned int events_count, t_id;
int msecs, ret, i, j;
bool call;
/* get the time left for next timeout task */
msecs = io_loop_get_wait_time(ioloop->timeouts, &tv, NULL);
events = array_get_modifiable(&ctx->events, &events_count);
ret = epoll_wait(ctx->epfd, events, events_count, msecs);
if (ret < 0 && errno != EINTR)
i_fatal("epoll_wait(): %m");
/* execute timeout handlers */
io_loop_handle_timeouts(ioloop);
if (!ioloop->running)
return;
for (i = 0; i < ret; i++) {
/* io_loop_handle_add() may cause events array reallocation,
so we have use array_idx() */
event = array_idx(&ctx->events, i);
list = event->data.ptr;
for (j = 0; j < IOLOOP_IOLIST_IOS_PER_FD; j++) {
io = list->ios[j];
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);
}
}
}
}
}
#endif /* IOLOOP_EPOLL */