ioloop-poll.c revision a718b37c944315db5d7189db214d7325466253d7
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2003 Timo Sirainen */
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen/* @UNSAFE: whole file */
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen#include "lib.h"
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen#include "ioloop-internal.h"
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen#ifdef IOLOOP_POLL
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen#include <sys/poll.h>
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen#ifndef INITIAL_POLL_FDS
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen# define INITIAL_POLL_FDS 128
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen#endif
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainenstruct ioloop_handler_context {
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen unsigned int fds_size, fds_pos;
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen struct pollfd *fds;
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen unsigned int idx_size;
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen int *fd_index;
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen};
a4985564b81d51caaddd38376792432428fd449bStephan Bosch
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainenvoid io_loop_handler_init(struct ioloop *ioloop)
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen{
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen struct ioloop_handler_context *ctx;
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen ioloop->handler_context = ctx =
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen p_new(ioloop->pool, struct ioloop_handler_context, 1);
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen ctx->fds_size = INITIAL_POLL_FDS;
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen ctx->fds = p_new(ioloop->pool, struct pollfd, ctx->fds_size);
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
a4985564b81d51caaddd38376792432428fd449bStephan Bosch ctx->idx_size = INITIAL_POLL_FDS;
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen ctx->fd_index = p_new(ioloop->pool, int, ctx->idx_size);
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen memset(ctx->fd_index, 0xff, sizeof(int) * ctx->idx_size);
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen}
fddfa139c8f7621bd13d44537698ba58d1965078Phil Carmody
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainenvoid io_loop_handler_deinit(struct ioloop *ioloop)
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen{
a4985564b81d51caaddd38376792432428fd449bStephan Bosch p_free(ioloop->pool, ioloop->handler_context->fds);
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen p_free(ioloop->pool, ioloop->handler_context->fd_index);
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen p_free(ioloop->pool, ioloop->handler_context);
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen}
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen#define IO_POLL_ERROR (POLLERR | POLLHUP | POLLNVAL)
fddfa139c8f7621bd13d44537698ba58d1965078Phil Carmody#define IO_POLL_INPUT (POLLIN | POLLPRI | IO_POLL_ERROR)
fddfa139c8f7621bd13d44537698ba58d1965078Phil Carmody#define IO_POLL_OUTPUT (POLLOUT | IO_POLL_ERROR)
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainen
289bb39856d74484c1622d096922acf126dd90fcTimo Sirainenvoid io_loop_handle_add(struct ioloop *ioloop, struct io *io)
{
struct ioloop_handler_context *ctx = ioloop->handler_context;
enum io_condition condition = io->condition;
unsigned int old_size;
int index, fd = io->fd;
if ((unsigned int)fd >= ctx->idx_size) {
/* grow the fd -> index array */
old_size = ctx->idx_size;
ctx->idx_size = nearest_power((unsigned int) fd+1);
i_assert(ctx->idx_size < (size_t)-1 / sizeof(int));
ctx->fd_index = p_realloc(ioloop->pool, ctx->fd_index,
sizeof(int) * old_size,
sizeof(int) * ctx->idx_size);
memset(ctx->fd_index + old_size, 0xff,
sizeof(int) * (ctx->idx_size-old_size));
}
if (ctx->fds_pos >= ctx->fds_size) {
/* grow the fd array */
old_size = ctx->fds_size;
ctx->fds_size = nearest_power(ctx->fds_size+1);
i_assert(ctx->fds_size < (size_t)-1 / sizeof(struct pollfd));
ctx->fds = p_realloc(ioloop->pool, ctx->fds,
sizeof(struct pollfd) * old_size,
sizeof(struct pollfd) * ctx->fds_size);
}
if (ctx->fd_index[fd] != -1) {
/* update existing pollfd */
index = ctx->fd_index[fd];
} else {
/* add new pollfd */
index = ctx->fds_pos++;
ctx->fd_index[fd] = index;
ctx->fds[index].fd = fd;
ctx->fds[index].events = 0;
ctx->fds[index].revents = 0;
}
if (condition & IO_READ)
ctx->fds[index].events |= IO_POLL_INPUT;
if (condition & IO_WRITE)
ctx->fds[index].events |= IO_POLL_OUTPUT;
if (condition & IO_ERROR)
ctx->fds[index].events |= IO_POLL_ERROR;
}
void io_loop_handle_remove(struct ioloop *ioloop, struct io *io)
{
struct ioloop_handler_context *ctx = ioloop->handler_context;
enum io_condition condition = io->condition;
int index, fd = io->fd;
index = ctx->fd_index[fd];
i_assert(index >= 0 && (unsigned int) index < ctx->fds_size);
if (condition & IO_READ) {
ctx->fds[index].events &= ~(POLLIN|POLLPRI);
ctx->fds[index].revents &= ~(POLLIN|POLLPRI);
}
if (condition & IO_WRITE) {
ctx->fds[index].events &= ~POLLOUT;
ctx->fds[index].revents &= ~POLLOUT;
}
if ((ctx->fds[index].events & (POLLIN|POLLOUT)) == 0) {
/* remove the whole pollfd */
ctx->fd_index[ctx->fds[index].fd] = -1;
if (--ctx->fds_pos == (unsigned int) index)
return; /* removing last one */
/* move the last pollfd over the removed one */
ctx->fds[index] = ctx->fds[ctx->fds_pos];
ctx->fd_index[ctx->fds[index].fd] = index;
}
}
void io_loop_handler_run(struct ioloop *ioloop)
{
struct ioloop_handler_context *ctx = ioloop->handler_context;
struct pollfd *pollfd;
struct timeval tv;
struct io *io;
unsigned int t_id;
int msecs, ret, call;
/* get the time left for next timeout task */
msecs = io_loop_get_wait_time(ioloop->timeouts, &tv, NULL);
ret = poll(ctx->fds, ctx->fds_pos, msecs);
if (ret < 0 && errno != EINTR)
i_fatal("poll(): %m");
/* execute timeout handlers */
io_loop_handle_timeouts(ioloop);
if (ret <= 0 || !ioloop->running) {
/* no I/O events */
return;
}
for (io = ioloop->ios; io != NULL && ret > 0; io = ioloop->next_io) {
ioloop->next_io = io->next;
pollfd = &ctx->fds[ctx->fd_index[io->fd]];
if (pollfd->revents != 0) {
if (pollfd->revents & POLLNVAL) {
i_error("invalid I/O fd %d, callback %p",
io->fd, (void *) io->callback);
pollfd->events &= ~POLLNVAL;
pollfd->revents = 0;
call = TRUE;
} else if ((io->condition &
(IO_READ|IO_WRITE)) == (IO_READ|IO_WRITE)) {
call = TRUE;
pollfd->revents = 0;
} else if (io->condition & IO_READ) {
call = (pollfd->revents & IO_POLL_INPUT) != 0;
pollfd->revents &= ~IO_POLL_INPUT;
} else if (io->condition & IO_WRITE) {
call = (pollfd->revents & IO_POLL_OUTPUT) != 0;
pollfd->revents &= ~IO_POLL_OUTPUT;
} else if (io->condition & IO_ERROR) {
call = (pollfd->revents & IO_POLL_ERROR) != 0;
pollfd->revents &= ~IO_POLL_ERROR;
} else {
call = FALSE;
}
if (pollfd->revents == 0)
ret--;
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