/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ioloop-private.h" #ifdef IOLOOP_SELECT #ifdef HAVE_SYS_SELECT_H # include /* According to POSIX 1003.1-2001 */ #endif #include #include struct ioloop_handler_context { int highest_fd; fd_set read_fds, write_fds, except_fds; fd_set tmp_read_fds, tmp_write_fds, tmp_except_fds; }; static void update_highest_fd(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct io_file *io; int max_highest_fd; max_highest_fd = ctx->highest_fd-1; ctx->highest_fd = -1; for (io = ioloop->io_files; io != NULL; io = io->next) { if (io->fd <= ctx->highest_fd) continue; ctx->highest_fd = io->fd; if (ctx->highest_fd == max_highest_fd) break; } } void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count ATTR_UNUSED) { struct ioloop_handler_context *ctx; ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1); ctx->highest_fd = -1; FD_ZERO(&ctx->read_fds); FD_ZERO(&ctx->write_fds); FD_ZERO(&ctx->except_fds); } void io_loop_handler_deinit(struct ioloop *ioloop) { i_free(ioloop->handler_context); } void io_loop_handle_add(struct io_file *io) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; enum io_condition condition = io->io.condition; int fd = io->fd; i_assert(fd >= 0); if (fd >= FD_SETSIZE) i_fatal("fd %d too large for select()", fd); if ((condition & (IO_READ | IO_ERROR)) != 0) FD_SET(fd, &ctx->read_fds); if ((condition & IO_WRITE) != 0) FD_SET(fd, &ctx->write_fds); FD_SET(fd, &ctx->except_fds); if (io->fd > ctx->highest_fd) ctx->highest_fd = io->fd; } void io_loop_handle_remove(struct io_file *io, bool closed ATTR_UNUSED) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; enum io_condition condition = io->io.condition; int fd = io->fd; i_assert(fd >= 0 && fd < FD_SETSIZE); if ((condition & (IO_READ | IO_ERROR)) != 0) FD_CLR(fd, &ctx->read_fds); if ((condition & IO_WRITE) != 0) FD_CLR(fd, &ctx->write_fds); if (!FD_ISSET(fd, &ctx->read_fds) && !FD_ISSET(fd, &ctx->write_fds)) { FD_CLR(fd, &ctx->except_fds); /* check if we removed the highest fd */ if (io->fd == ctx->highest_fd) update_highest_fd(io->io.ioloop); } i_free(io); } #define io_check_condition(ctx, fd, cond) \ ((FD_ISSET((fd), &(ctx)->tmp_read_fds) && ((cond) & (IO_READ|IO_ERROR)) != 0) || \ (FD_ISSET((fd), &(ctx)->tmp_write_fds) && ((cond) & IO_WRITE) != 0) || \ (FD_ISSET((fd), &(ctx)->tmp_except_fds))) void io_loop_handler_run_internal(struct ioloop *ioloop) { struct ioloop_handler_context *ctx = ioloop->handler_context; struct timeval tv; struct io_file *io; int ret; /* get the time left for next timeout task */ io_loop_run_get_wait_time(ioloop, &tv); memcpy(&ctx->tmp_read_fds, &ctx->read_fds, sizeof(fd_set)); memcpy(&ctx->tmp_write_fds, &ctx->write_fds, sizeof(fd_set)); memcpy(&ctx->tmp_except_fds, &ctx->except_fds, sizeof(fd_set)); ret = select(ctx->highest_fd + 1, &ctx->tmp_read_fds, &ctx->tmp_write_fds, &ctx->tmp_except_fds, &tv); if (ret < 0 && errno != EINTR) i_warning("select() : %m"); /* execute timeout handlers */ io_loop_handle_timeouts(ioloop); if (ret <= 0 || !ioloop->running) { /* no I/O events */ return; } io = ioloop->io_files; for (; io != NULL && ret > 0; io = ioloop->next_io_file) { ioloop->next_io_file = io->next; if (io->fd == -1) { /* io_add_istream() without fd */ } else if (io_check_condition(ctx, io->fd, io->io.condition)) { ret--; io_loop_call_io(&io->io); if (!ioloop->running) break; } } } #endif