ioloop.c revision cc80e458f2e3f13106299794376e9be4d3e8ef80
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2002-2014 Dovecot authors, see the included COPYING file */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "lib.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "array.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "time-util.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "istream-private.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "ioloop-private.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <unistd.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#define timer_is_larger(tvp, uvp) \
7a7d2aa11e46195e2d92d6c337d7e78052a5ce67Timo Sirainen ((tvp)->tv_sec > (uvp)->tv_sec || \
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ((tvp)->tv_sec == (uvp)->tv_sec && \
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen (tvp)->tv_usec > (uvp)->tv_usec))
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainentime_t ioloop_time = 0;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstruct timeval ioloop_timeval;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstruct ioloop *current_ioloop = NULL;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic ARRAY(io_switch_callback_t *) io_switch_callbacks = ARRAY_INIT;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic void io_loop_initialize_handler(struct ioloop *ioloop)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen unsigned int initial_fd_count;
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen initial_fd_count = ioloop->max_fd_count > 0 &&
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen ioloop->max_fd_count < IOLOOP_INITIAL_FD_COUNT ?
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen ioloop->max_fd_count : IOLOOP_INITIAL_FD_COUNT;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io_loop_handler_init(ioloop, initial_fd_count);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen}
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenstatic struct io_file *
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenio_add_file(int fd, enum io_condition condition,
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen unsigned int source_linenum,
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io_callback_t *callback, void *context)
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen{
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen struct io_file *io;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen i_assert(callback != NULL);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen i_assert((condition & IO_NOTIFY) == 0);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io = i_new(struct io_file, 1);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen io->io.condition = condition;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen io->io.callback = callback;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io->io.context = context;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io->io.ioloop = current_ioloop;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io->io.source_linenum = source_linenum;
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen io->refcount = 1;
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen io->fd = fd;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen if (io->io.ioloop->cur_ctx != NULL) {
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen io->io.ctx = io->io.ioloop->cur_ctx;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io_loop_context_ref(io->io.ctx);
11352dc3e4b29f3d2763c82f8ea4f99e8daf4fa3Timo Sirainen }
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen if (io->io.ioloop->handler_context == NULL)
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen io_loop_initialize_handler(io->io.ioloop);
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen if (fd != -1)
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen io_loop_handle_add(io);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen else {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* we're adding an istream whose only way to get notified
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen is to call i_stream_set_input_pending() */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
cd8507179823de33d6e8242e10dbc15d136245b5Timo Sirainen if (io->io.ioloop->io_files != NULL) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io->io.ioloop->io_files->prev = io;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io->next = io->io.ioloop->io_files;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen io->io.ioloop->io_files = io;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return io;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#undef io_add
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstruct io *io_add(int fd, enum io_condition condition,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen unsigned int source_linenum,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen io_callback_t *callback, void *context)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct io_file *io;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_assert(fd >= 0);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen io = io_add_file(fd, condition, source_linenum, callback, context);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return &io->io;
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen}
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen#undef io_add_istream
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainenstruct io *io_add_istream(struct istream *input, unsigned int source_linenum,
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen io_callback_t *callback, void *context)
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen{
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen struct io_file *io;
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen io = io_add_file(i_stream_get_fd(input), IO_READ, source_linenum,
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen callback, context);
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen io->istream = input;
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen i_stream_ref(io->istream);
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen i_stream_set_io(io->istream, &io->io);
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen return &io->io;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic void io_file_unlink(struct io_file *io)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (io->prev != NULL)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io->prev->next = io->next;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen else
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io->io.ioloop->io_files = io->next;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (io->next != NULL)
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen io->next->prev = io->prev;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* if we got here from an I/O handler callback, make sure we
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen don't try to handle this one next. */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (io->io.ioloop->next_io_file == io)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io->io.ioloop->next_io_file = io->next;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainenstatic void io_remove_full(struct io **_io, bool closed)
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen{
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen struct io *io = *_io;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen i_assert(io->callback != NULL);
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen *_io = NULL;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen /* make sure the callback doesn't get called anymore.
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen kqueue code relies on this. */
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen io->callback = NULL;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen if (io->pending) {
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen i_assert(io->ioloop->io_pending_count > 0);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io->ioloop->io_pending_count--;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen }
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen if (io->ctx != NULL)
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io_loop_context_unref(&io->ctx);
11352dc3e4b29f3d2763c82f8ea4f99e8daf4fa3Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen if ((io->condition & IO_NOTIFY) != 0)
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io_loop_notify_remove(io);
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen else {
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen struct io_file *io_file = (struct io_file *)io;
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen if (io_file->istream != NULL) {
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen i_stream_unset_io(io_file->istream, io);
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen i_stream_unref(&io_file->istream);
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen io_file->istream = NULL;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen }
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io_file_unlink(io_file);
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen if (io_file->fd != -1)
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen io_loop_handle_remove(io_file, closed);
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen }
8305127d1074cf9a1e25dec9be2735276462079dTimo Sirainen}
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenvoid io_remove(struct io **io)
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen{
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen io_remove_full(io, FALSE);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen}
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenvoid io_remove_closed(struct io **io)
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen{
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen i_assert(((*io)->condition & IO_NOTIFY) == 0);
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io_remove_full(io, TRUE);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenvoid io_set_pending(struct io *io)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
596433ccbca59ce2328dc1d029586154cd937155Timo Sirainen i_assert((io->condition & IO_NOTIFY) == 0);
596433ccbca59ce2328dc1d029586154cd937155Timo Sirainen
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen if (!io->pending) {
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen io->pending = TRUE;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io->ioloop->io_pending_count++;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen }
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen}
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainenstatic void timeout_update_next(struct timeout *timeout, struct timeval *tv_now)
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen{
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen if (tv_now == NULL) {
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen if (gettimeofday(&timeout->next_run, NULL) < 0)
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen i_fatal("gettimeofday(): %m");
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen } else {
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen timeout->next_run.tv_sec = tv_now->tv_sec;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen timeout->next_run.tv_usec = tv_now->tv_usec;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen }
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen /* we don't want microsecond accuracy or this function will be
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen called all the time - millisecond is more than enough */
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen timeout->next_run.tv_usec -= timeout->next_run.tv_usec % 1000;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen timeout->next_run.tv_sec += timeout->msecs/1000;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen timeout->next_run.tv_usec += (timeout->msecs%1000)*1000;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
7f3b826a89bcb7a72759912e99f574b28309fe1bTimo Sirainen if (timeout->next_run.tv_usec > 1000000) {
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen timeout->next_run.tv_sec++;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen timeout->next_run.tv_usec -= 1000000;
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen }
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen}
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen#undef timeout_add
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainenstruct timeout *timeout_add(unsigned int msecs, unsigned int source_linenum,
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen timeout_callback_t *callback, void *context)
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct timeout *timeout;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen timeout = i_new(struct timeout, 1);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen timeout->source_linenum = source_linenum;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen timeout->msecs = msecs;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen timeout->ioloop = current_ioloop;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen timeout->callback = callback;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen timeout->context = context;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (timeout->ioloop->cur_ctx != NULL) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen timeout->ctx = timeout->ioloop->cur_ctx;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io_loop_context_ref(timeout->ctx);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen timeout_update_next(timeout, timeout->ioloop->running ?
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen NULL : &ioloop_timeval);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
18ccd19c244f49665fe03cda785efa066d2c38dfTimo Sirainen return timeout;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#undef timeout_add_short
c529313e1cbc22244d4528e80aa3e485f8806cd3Timo Sirainenstruct timeout *
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainentimeout_add_short(unsigned int msecs, unsigned int source_linenum,
d22301419109ed4a38351715e6760011421dadecTimo Sirainen timeout_callback_t *callback, void *context)
d22301419109ed4a38351715e6760011421dadecTimo Sirainen{
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return timeout_add(msecs, source_linenum, callback, context);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen}
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainenstatic void timeout_free(struct timeout *timeout)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen{
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (timeout->ctx != NULL)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen io_loop_context_unref(&timeout->ctx);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_free(timeout);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainenvoid timeout_remove(struct timeout **_timeout)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct timeout *timeout = *_timeout;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen *_timeout = NULL;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen timeout_free(timeout);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen}
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainenstatic void ATTR_NULL(2)
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainentimeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now)
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen{
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen timeout_update_next(timeout, tv_now);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen if (timeout->msecs <= 1) {
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen /* if we came here from io_loop_handle_timeouts(),
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen next_run must be larger than tv_now or we could go to
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen infinite loop. +1000 to get 1 ms further, another +1000 to
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen account for timeout_update_next()'s truncation. */
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen timeout->next_run.tv_usec += 2000;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen if (timeout->next_run.tv_usec >= 1000000) {
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen timeout->next_run.tv_sec++;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen timeout->next_run.tv_usec -= 1000000;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen }
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen }
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen i_assert(tv_now == NULL ||
c4bb0320ab43ea35fa6df88fc745fdad906cee44Timo Sirainen timeout->next_run.tv_sec > tv_now->tv_sec ||
c4bb0320ab43ea35fa6df88fc745fdad906cee44Timo Sirainen (timeout->next_run.tv_sec == tv_now->tv_sec &&
c4bb0320ab43ea35fa6df88fc745fdad906cee44Timo Sirainen timeout->next_run.tv_usec > tv_now->tv_usec));
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
c4bb0320ab43ea35fa6df88fc745fdad906cee44Timo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen}
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainenvoid timeout_reset(struct timeout *timeout)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen timeout_reset_timeval(timeout, NULL);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen}
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic int timeout_get_wait_time(struct timeout *timeout, struct timeval *tv_r,
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen struct timeval *tv_now)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen{
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen int ret;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (tv_now->tv_sec == 0) {
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (gettimeofday(tv_now, NULL) < 0)
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen i_fatal("gettimeofday(): %m");
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen }
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen tv_r->tv_sec = tv_now->tv_sec;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen tv_r->tv_usec = tv_now->tv_usec;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen i_assert(tv_r->tv_sec > 0);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen i_assert(timeout->next_run.tv_sec > 0);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen tv_r->tv_sec = timeout->next_run.tv_sec - tv_r->tv_sec;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen tv_r->tv_usec = timeout->next_run.tv_usec - tv_r->tv_usec;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen if (tv_r->tv_usec < 0) {
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen tv_r->tv_sec--;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen tv_r->tv_usec += 1000000;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (tv_r->tv_sec < 0 || (tv_r->tv_sec == 0 && tv_r->tv_usec < 1000)) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen tv_r->tv_sec = 0;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen tv_r->tv_usec = 0;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen return 0;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (tv_r->tv_sec > INT_MAX/1000-1)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen tv_r->tv_sec = INT_MAX/1000-1;
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen /* round wait times up to next millisecond */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ret = tv_r->tv_sec * 1000 + (tv_r->tv_usec + 999) / 1000;
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen i_assert(ret > 0 && tv_r->tv_sec >= 0 && tv_r->tv_usec >= 0);
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen return ret;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen}
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
18ccd19c244f49665fe03cda785efa066d2c38dfTimo Sirainenint io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r)
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen{
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen struct timeval tv_now;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen struct priorityq_item *item;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen struct timeout *timeout;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen int msecs;
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen item = priorityq_peek(ioloop->timeouts);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen timeout = (struct timeout *)item;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen if (timeout == NULL) {
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen /* no timeouts. use INT_MAX msecs for timeval and
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen return -1 for poll/epoll infinity. */
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen tv_r->tv_sec = INT_MAX / 1000;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen tv_r->tv_usec = 0;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen ioloop->next_max_time = (1ULL << (TIME_T_MAX_BITS-1)) - 1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return -1;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen tv_now.tv_sec = 0;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen msecs = timeout_get_wait_time(timeout, tv_r, &tv_now);
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen ioloop->next_max_time = (tv_now.tv_sec + msecs/1000) + 1;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen return msecs;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen}
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainenstatic int timeout_cmp(const void *p1, const void *p2)
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen{
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen const struct timeout *to1 = p1, *to2 = p2;
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen return timeval_cmp(&to1->next_run, &to2->next_run);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen}
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainenstatic void io_loop_default_time_moved(time_t old_time, time_t new_time)
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen{
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen if (old_time > new_time) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen i_warning("Time moved backwards by %ld seconds.",
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen (long)(old_time - new_time));
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen }
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen}
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainenstatic void io_loop_timeouts_update(struct ioloop *ioloop, long diff_secs)
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen{
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen struct priorityq_item *const *items;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen unsigned int i, count;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen count = priorityq_count(ioloop->timeouts);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen items = priorityq_items(ioloop->timeouts);
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen for (i = 0; i < count; i++) {
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen struct timeout *to = (struct timeout *)items[i];
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen to->next_run.tv_sec += diff_secs;
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainen }
d859478e8b106de6cea54f26861bd4232c92f62cTimo Sirainen}
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainenstatic void io_loops_timeouts_update(long diff_secs)
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen{
d859478e8b106de6cea54f26861bd4232c92f62cTimo Sirainen struct ioloop *ioloop;
206ed2f6fa3a6fb291498627b2da626581c07a18Timo Sirainen
206ed2f6fa3a6fb291498627b2da626581c07a18Timo Sirainen for (ioloop = current_ioloop; ioloop != NULL; ioloop = ioloop->prev)
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen io_loop_timeouts_update(ioloop, diff_secs);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen}
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainenstatic void io_loop_handle_timeouts_real(struct ioloop *ioloop)
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen{
206ed2f6fa3a6fb291498627b2da626581c07a18Timo Sirainen struct priorityq_item *item;
d859478e8b106de6cea54f26861bd4232c92f62cTimo Sirainen struct timeval tv, tv_call;
d859478e8b106de6cea54f26861bd4232c92f62cTimo Sirainen unsigned int t_id;
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen i_fatal("gettimeofday(): %m");
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen /* Don't bother comparing usecs. */
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen if (unlikely(ioloop_time > ioloop_timeval.tv_sec)) {
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen /* time moved backwards */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io_loops_timeouts_update(-(long)(ioloop_time -
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen ioloop_timeval.tv_sec));
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen ioloop->time_moved_callback(ioloop_time,
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen ioloop_timeval.tv_sec);
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen /* the callback may have slept, so check the time again. */
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen i_fatal("gettimeofday(): %m");
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen } else if (unlikely(ioloop_timeval.tv_sec >
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen ioloop->next_max_time)) {
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen io_loops_timeouts_update(ioloop_timeval.tv_sec -
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen ioloop->next_max_time);
9438ecaf1caee1bb33c8d7f638742875ac42c4e5Timo Sirainen /* time moved forwards */
9438ecaf1caee1bb33c8d7f638742875ac42c4e5Timo Sirainen ioloop->time_moved_callback(ioloop->next_max_time,
77d8223da3da23b731257596abefa77e4485b77dTimo Sirainen ioloop_timeval.tv_sec);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen ioloop_time = ioloop_timeval.tv_sec;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen tv_call = ioloop_timeval;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen while ((item = priorityq_peek(ioloop->timeouts)) != NULL) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen struct timeout *timeout = (struct timeout *)item;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* use tv_call to make sure we don't get to infinite loop in
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen case callbacks update ioloop_timeval. */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (timeout_get_wait_time(timeout, &tv, &tv_call) > 0)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen break;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* update timeout's next_run and reposition it in the queue */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen timeout_reset_timeval(timeout, &tv_call);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (timeout->ctx != NULL)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen io_loop_context_activate(timeout->ctx);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen t_id = t_push_named("ioloop timeout handler %p",
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen (void *)timeout->callback);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen timeout->callback(timeout->context);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (t_pop() != t_id) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i_panic("Leaked a t_pop() call in timeout handler %p",
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen (void *)timeout->callback);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (ioloop->cur_ctx != NULL)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen io_loop_context_deactivate(ioloop->cur_ctx);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen}
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainenvoid io_loop_handle_timeouts(struct ioloop *ioloop)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen{
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen T_BEGIN {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen io_loop_handle_timeouts_real(ioloop);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen } T_END;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen}
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenvoid io_loop_call_io(struct io *io)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen{
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen struct ioloop *ioloop = io->ioloop;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen unsigned int t_id;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (io->pending) {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen i_assert(ioloop->io_pending_count > 0);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen ioloop->io_pending_count--;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen io->pending = FALSE;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen }
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (io->ctx != NULL)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen io_loop_context_activate(io->ctx);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen t_id = t_push_named("ioloop handler %p",
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen (void *)io->callback);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen io->callback(io->context);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (t_pop() != t_id) {
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i_panic("Leaked a t_pop() call in I/O handler %p",
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen (void *)io->callback);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (ioloop->cur_ctx != NULL)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen io_loop_context_deactivate(ioloop->cur_ctx);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen}
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenvoid io_loop_run(struct ioloop *ioloop)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen{
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen if (ioloop->handler_context == NULL)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen io_loop_initialize_handler(ioloop);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (ioloop->cur_ctx != NULL)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen io_loop_context_unref(&ioloop->cur_ctx);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* recursive io_loop_run() isn't allowed for the same ioloop.
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen it can break backends. */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i_assert(!ioloop->iolooping);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen ioloop->iolooping = TRUE;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen ioloop->running = TRUE;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen while (ioloop->running)
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen io_loop_handler_run(ioloop);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen ioloop->iolooping = FALSE;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen}
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainenstatic void io_loop_call_pending(struct ioloop *ioloop)
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen{
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen struct io_file *io;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen while (ioloop->io_pending_count > 0) {
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen io = ioloop->io_files;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen do {
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen ioloop->next_io_file = io->next;
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen if (io->io.pending)
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen io_loop_call_io(&io->io);
3852872e6954b7132e637294132005e86b8ebd4aTimo Sirainen if (ioloop->io_pending_count == 0)
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen break;
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen io = ioloop->next_io_file;
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen } while (io != NULL);
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen }
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen}
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainenvoid io_loop_handler_run(struct ioloop *ioloop)
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen{
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen io_loop_handler_run_internal(ioloop);
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen io_loop_call_pending(ioloop);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainenvoid io_loop_stop(struct ioloop *ioloop)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen ioloop->running = FALSE;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenvoid io_loop_set_running(struct ioloop *ioloop)
c6afd726060aae56b6622c6c52aec10231c4bf1cTimo Sirainen{
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen ioloop->running = TRUE;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenvoid io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds)
34f4c7610b846a945779b6be78d1ef575c7d0ca8Timo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ioloop->max_fd_count = max_fds;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenbool io_loop_is_running(struct ioloop *ioloop)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return ioloop->running;
db0735f9b388c5bcfb781b1b25015e898d63d953Timo Sirainen}
047e3bbb00e68a0d43355e11a67b2e912e06de19Timo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainenvoid io_loop_time_refresh(void)
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen{
eca30f1fe8556c46abc75c94d03f59b2e89d4162Timo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen i_fatal("gettimeofday(): %m");
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen ioloop_time = ioloop_timeval.tv_sec;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen}
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen
421d30619384e72a27e2a5d13ff6525aff4d17feTimo Sirainenstruct ioloop *io_loop_create(void)
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen{
6469cf211a57433335641725dc236ebb2b9fdd3bTimo Sirainen struct ioloop *ioloop;
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen /* initialize time */
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen i_fatal("gettimeofday(): %m");
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen ioloop_time = ioloop_timeval.tv_sec;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
fbd918f47f591f8084fd52b207ef29515ddd11b9Timo Sirainen ioloop = i_new(struct ioloop, 1);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ioloop->timeouts = priorityq_init(timeout_cmp, 32);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ioloop->time_moved_callback = current_ioloop != NULL ?
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen current_ioloop->time_moved_callback :
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen io_loop_default_time_moved;
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen ioloop->prev = current_ioloop;
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen io_loop_set_current(ioloop);
8a13b020f90e080570658b18c042e7e352c8b14fTimo Sirainen return ioloop;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainenvoid io_loop_destroy(struct ioloop **_ioloop)
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen{
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen struct ioloop *ioloop = *_ioloop;
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen struct priorityq_item *item;
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen *_ioloop = NULL;
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen /* ->prev won't work unless loops are destroyed in create order */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen i_assert(ioloop == current_ioloop);
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen io_loop_set_current(current_ioloop->prev);
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen if (ioloop->notify_handler_context != NULL)
4cf5f0934a25f1fd58f2780108f9d6498c455a1fTimo Sirainen io_loop_notify_handler_deinit(ioloop);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen while (ioloop->io_files != NULL) {
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen struct io_file *io = ioloop->io_files;
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen struct io *_io = &io->io;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen i_warning("I/O leak: %p (line %u, fd %d)",
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen (void *)io->io.callback,
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen io->io.source_linenum, io->fd);
io_remove(&_io);
}
i_assert(ioloop->io_pending_count == 0);
while ((item = priorityq_pop(ioloop->timeouts)) != NULL) {
struct timeout *to = (struct timeout *)item;
i_warning("Timeout leak: %p (line %u)", (void *)to->callback,
to->source_linenum);
timeout_free(to);
}
priorityq_deinit(&ioloop->timeouts);
if (ioloop->handler_context != NULL)
io_loop_handler_deinit(ioloop);
if (ioloop->cur_ctx != NULL)
io_loop_context_deactivate(ioloop->cur_ctx);
i_free(ioloop);
}
void io_loop_set_time_moved_callback(struct ioloop *ioloop,
io_loop_time_moved_callback_t *callback)
{
ioloop->time_moved_callback = callback;
}
static void io_switch_callbacks_free(void)
{
array_free(&io_switch_callbacks);
}
void io_loop_set_current(struct ioloop *ioloop)
{
io_switch_callback_t *const *callbackp;
struct ioloop *prev_ioloop = current_ioloop;
current_ioloop = ioloop;
if (array_is_created(&io_switch_callbacks)) {
array_foreach(&io_switch_callbacks, callbackp)
(*callbackp)(prev_ioloop);
}
}
void io_loop_add_switch_callback(io_switch_callback_t *callback)
{
if (!array_is_created(&io_switch_callbacks)) {
i_array_init(&io_switch_callbacks, 4);
lib_atexit(io_switch_callbacks_free);
}
array_append(&io_switch_callbacks, &callback, 1);
}
void io_loop_remove_switch_callback(io_switch_callback_t *callback)
{
io_switch_callback_t *const *callbackp;
unsigned int idx;
array_foreach(&io_switch_callbacks, callbackp) {
if (*callbackp == callback) {
idx = array_foreach_idx(&io_switch_callbacks, callbackp);
array_delete(&io_switch_callbacks, idx, 1);
return;
}
}
i_unreached();
}
struct ioloop_context *io_loop_context_new(struct ioloop *ioloop)
{
struct ioloop_context *ctx;
ctx = i_new(struct ioloop_context, 1);
ctx->refcount = 2;
ctx->ioloop = ioloop;
i_array_init(&ctx->callbacks, 4);
if (ioloop->cur_ctx != NULL)
io_loop_context_unref(&ioloop->cur_ctx);
ioloop->cur_ctx = ctx;
return ctx;
}
void io_loop_context_ref(struct ioloop_context *ctx)
{
i_assert(ctx->refcount > 0);
ctx->refcount++;
}
void io_loop_context_unref(struct ioloop_context **_ctx)
{
struct ioloop_context *ctx = *_ctx;
*_ctx = NULL;
i_assert(ctx->refcount > 0);
if (--ctx->refcount > 0)
return;
/* cur_ctx itself keeps a reference */
i_assert(ctx->ioloop->cur_ctx != ctx);
array_free(&ctx->callbacks);
i_free(ctx);
}
void io_loop_context_add_callbacks(struct ioloop_context *ctx,
io_callback_t *activate,
io_callback_t *deactivate, void *context)
{
struct ioloop_context_callback cb;
memset(&cb, 0, sizeof(cb));
cb.activate = activate;
cb.deactivate = deactivate;
cb.context = context;
array_append(&ctx->callbacks, &cb, 1);
}
void io_loop_context_remove_callbacks(struct ioloop_context *ctx,
io_callback_t *activate,
io_callback_t *deactivate, void *context)
{
struct ioloop_context_callback *cb;
array_foreach_modifiable(&ctx->callbacks, cb) {
if (cb->context == context &&
cb->activate == activate && cb->deactivate == deactivate) {
/* simply mark it as deleted, since we could get
here from activate/deactivate loop */
cb->activate = NULL;
cb->deactivate = NULL;
cb->context = NULL;
return;
}
}
i_panic("io_loop_context_remove_callbacks() context not found");
}
static void
io_loop_context_remove_deleted_callbacks(struct ioloop_context *ctx)
{
const struct ioloop_context_callback *cbs;
unsigned int i, count;
cbs = array_get(&ctx->callbacks, &count);
for (i = 0; i < count; ) {
if (cbs[i].activate != NULL)
i++;
else {
array_delete(&ctx->callbacks, i, 1);
cbs = array_get(&ctx->callbacks, &count);
}
}
}
void io_loop_context_activate(struct ioloop_context *ctx)
{
const struct ioloop_context_callback *cb;
i_assert(ctx->ioloop->cur_ctx == NULL);
ctx->ioloop->cur_ctx = ctx;
io_loop_context_ref(ctx);
array_foreach(&ctx->callbacks, cb) {
if (cb->activate != NULL)
cb->activate(cb->context);
}
}
void io_loop_context_deactivate(struct ioloop_context *ctx)
{
const struct ioloop_context_callback *cb;
array_foreach(&ctx->callbacks, cb) {
if (cb->deactivate != NULL)
cb->deactivate(cb->context);
}
ctx->ioloop->cur_ctx = NULL;
io_loop_context_remove_deleted_callbacks(ctx);
io_loop_context_unref(&ctx);
}
struct ioloop_context *io_loop_get_current_context(struct ioloop *ioloop)
{
return ioloop->cur_ctx;
}
struct io *io_loop_move_io(struct io **_io)
{
struct io *old_io = *_io;
struct io_file *old_io_file, *new_io_file;
i_assert((old_io->condition & IO_NOTIFY) == 0);
if (old_io->ioloop == current_ioloop)
return old_io;
old_io_file = (struct io_file *)old_io;
new_io_file = io_add_file(old_io_file->fd, old_io->condition,
old_io->source_linenum,
old_io->callback, old_io->context);
if (old_io_file->istream != NULL) {
/* reference before io_remove() */
new_io_file->istream = old_io_file->istream;
i_stream_ref(new_io_file->istream);
}
if (old_io->pending)
io_set_pending(&new_io_file->io);
io_remove(_io);
if (new_io_file->istream != NULL) {
/* update istream io after it was removed with io_remove() */
i_stream_set_io(new_io_file->istream, &new_io_file->io);
}
return &new_io_file->io;
}
struct timeout *io_loop_move_timeout(struct timeout **_timeout)
{
struct timeout *new_to, *old_to = *_timeout;
if (old_to->ioloop == current_ioloop)
return old_to;
new_to = timeout_add(old_to->msecs, old_to->source_linenum,
old_to->callback, old_to->context);
timeout_remove(_timeout);
return new_to;
}
bool io_loop_have_ios(struct ioloop *ioloop)
{
return ioloop->io_files != NULL;
}
bool io_loop_have_immediate_timeouts(struct ioloop *ioloop)
{
struct timeval tv;
return io_loop_get_wait_time(ioloop, &tv) == 0;
}