summaryrefslogtreecommitdiffstats
path: root/src/lib/ioloop-poll.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib/ioloop-poll.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/src/lib/ioloop-poll.c b/src/lib/ioloop-poll.c
new file mode 100644
index 0000000..02cdce6
--- /dev/null
+++ b/src/lib/ioloop-poll.c
@@ -0,0 +1,221 @@
+/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
+
+/* @UNSAFE: whole file */
+
+#include "lib.h"
+#include "ioloop-private.h"
+
+#ifdef IOLOOP_POLL
+
+#include <fcntl.h>
+#include <sys/poll.h>
+
+struct ioloop_handler_context {
+ unsigned int fds_count, fds_pos;
+ struct pollfd *fds;
+
+ unsigned int idx_count;
+ int *fd_index;
+};
+
+void io_loop_handler_init(struct ioloop *ioloop, unsigned int initial_fd_count)
+{
+ struct ioloop_handler_context *ctx;
+
+ ioloop->handler_context = ctx = i_new(struct ioloop_handler_context, 1);
+ ctx->fds_count = initial_fd_count;
+ ctx->fds = i_new(struct pollfd, ctx->fds_count);
+
+ ctx->idx_count = initial_fd_count;
+ ctx->fd_index = i_new(int, ctx->idx_count);
+ memset(ctx->fd_index, 0xff, sizeof(int) * ctx->idx_count);
+}
+
+void io_loop_handler_deinit(struct ioloop *ioloop)
+{
+ i_free(ioloop->handler_context->fds);
+ i_free(ioloop->handler_context->fd_index);
+ i_free(ioloop->handler_context);
+}
+
+#define IO_POLL_ERROR (POLLERR | POLLHUP | POLLNVAL)
+#define IO_POLL_INPUT (POLLIN | POLLPRI | IO_POLL_ERROR)
+#define IO_POLL_OUTPUT (POLLOUT | IO_POLL_ERROR)
+
+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;
+ unsigned int old_count;
+ int index, old_events, fd = io->fd;
+
+ if ((unsigned int)fd >= ctx->idx_count) {
+ /* grow the fd -> index array */
+ old_count = ctx->idx_count;
+
+ ctx->idx_count = nearest_power((unsigned int) fd+1);
+
+ ctx->fd_index = i_realloc_type(ctx->fd_index, int,
+ old_count, ctx->idx_count);
+ memset(ctx->fd_index + old_count, 0xff,
+ sizeof(int) * (ctx->idx_count-old_count));
+ }
+
+ if (ctx->fds_pos >= ctx->fds_count) {
+ /* grow the fd array */
+ old_count = ctx->fds_count;
+
+ ctx->fds_count = nearest_power(ctx->fds_count+1);
+
+ ctx->fds = i_realloc_type(ctx->fds, struct pollfd,
+ old_count, ctx->fds_count);
+ }
+
+ 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;
+ }
+
+ old_events = ctx->fds[index].events;
+ if ((condition & IO_READ) != 0)
+ ctx->fds[index].events |= IO_POLL_INPUT;
+ if ((condition & IO_WRITE) != 0)
+ ctx->fds[index].events |= IO_POLL_OUTPUT;
+ if ((condition & IO_ERROR) != 0)
+ ctx->fds[index].events |= IO_POLL_ERROR;
+ i_assert(ctx->fds[index].events != old_events);
+}
+
+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 index, fd = io->fd;
+
+ index = ctx->fd_index[fd];
+ i_assert(index >= 0 && (unsigned int) index < ctx->fds_count);
+
+#ifdef DEBUG
+ if (!closed) {
+ /* io_remove() is required to be called before fd is closed.
+ This is required by epoll/kqueue, but since poll is more
+ commonly used while developing, this check here should catch
+ the error early enough not to cause problems for kqueue
+ users. */
+ if (fcntl(io->fd, F_GETFD, 0) < 0) {
+ if (errno == EBADF)
+ i_panic("io_remove(%d) called too late", io->fd);
+ else
+ i_error("fcntl(%d, F_GETFD) failed: %m", io->fd);
+ }
+ }
+#endif
+ i_free(io);
+
+ if ((condition & IO_READ) != 0) {
+ ctx->fds[index].events &= ENUM_NEGATE(POLLIN | POLLPRI);
+ ctx->fds[index].revents &= ENUM_NEGATE(POLLIN | POLLPRI);
+ }
+ if ((condition & IO_WRITE) != 0) {
+ ctx->fds[index].events &= ENUM_NEGATE(POLLOUT);
+ ctx->fds[index].revents &= ENUM_NEGATE(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_internal(struct ioloop *ioloop)
+{
+ struct ioloop_handler_context *ctx = ioloop->handler_context;
+ struct pollfd *pollfd;
+ struct timeval tv;
+ struct io_file *io;
+ int msecs, ret;
+ bool call;
+
+ /* get the time left for next timeout task */
+ msecs = io_loop_run_get_wait_time(ioloop, &tv);
+#ifdef _AIX
+ if (msecs > 1000) {
+ /* AIX seems to check IO_POLL_ERRORs only at the beginning of
+ the poll() call, not during it. keep timeouts short enough
+ so that we'll notice them pretty quickly. */
+ msecs = 1000;
+ }
+#endif
+
+ 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;
+ }
+
+ 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 */
+ continue;
+ }
+ 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->io.callback);
+ pollfd->events = 0;
+ pollfd->revents = 0;
+ call = TRUE;
+ } else if ((io->io.condition &
+ (IO_READ|IO_WRITE)) == (IO_READ|IO_WRITE)) {
+ call = TRUE;
+ pollfd->revents = 0;
+ } else if ((io->io.condition & IO_READ) != 0) {
+ call = (pollfd->revents & IO_POLL_INPUT) != 0;
+ pollfd->revents &= ENUM_NEGATE(IO_POLL_INPUT);
+ } else if ((io->io.condition & IO_WRITE) != 0) {
+ call = (pollfd->revents & IO_POLL_OUTPUT) != 0;
+ pollfd->revents &= ENUM_NEGATE(IO_POLL_OUTPUT);
+ } else if ((io->io.condition & IO_ERROR) != 0) {
+ call = (pollfd->revents & IO_POLL_ERROR) != 0;
+ pollfd->revents &= ENUM_NEGATE(IO_POLL_ERROR);
+ } else {
+ call = FALSE;
+ }
+
+ if (pollfd->revents == 0)
+ ret--;
+
+ if (call) {
+ io_loop_call_io(&io->io);
+ if (!ioloop->running)
+ return;
+ }
+ }
+ }
+}
+
+#endif