ioloop.c revision 33b0119d4effb14cd0f1bdd3ad5f2954e3b1e63e
2e37d45867d081db150ab78dad303b9077aea24fTimo Sirainen/* Copyright (c) 2002-2014 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "lib.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "array.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "time-util.h"
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen#include "istream-private.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "ioloop-private.h"
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include <unistd.h>
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#define timer_is_larger(tvp, uvp) \
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ((tvp)->tv_sec > (uvp)->tv_sec || \
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ((tvp)->tv_sec == (uvp)->tv_sec && \
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (tvp)->tv_usec > (uvp)->tv_usec))
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainentime_t ioloop_time = 0;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstruct timeval ioloop_timeval;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenstruct ioloop *current_ioloop = NULL;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic ARRAY(io_switch_callback_t *) io_switch_callbacks = ARRAY_INIT;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenstatic void io_loop_initialize_handler(struct ioloop *ioloop)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen unsigned int initial_fd_count;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen initial_fd_count = ioloop->max_fd_count > 0 &&
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen ioloop->max_fd_count < IOLOOP_INITIAL_FD_COUNT ?
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ioloop->max_fd_count : IOLOOP_INITIAL_FD_COUNT;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen io_loop_handler_init(ioloop, initial_fd_count);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic struct io_file *
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenio_add_file(int fd, enum io_condition condition,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen unsigned int source_linenum,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io_callback_t *callback, void *context)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct io_file *io;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen i_assert(fd >= 0);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_assert(callback != NULL);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen i_assert((condition & IO_NOTIFY) == 0);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen io = i_new(struct io_file, 1);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen io->io.condition = condition;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen io->io.callback = callback;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen io->io.context = context;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io->io.ioloop = current_ioloop;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io->io.source_linenum = source_linenum;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen io->refcount = 1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen io->fd = fd;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (io->io.ioloop->cur_ctx != NULL) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen io->io.ctx = io->io.ioloop->cur_ctx;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen io_loop_context_ref(io->io.ctx);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (io->io.ioloop->handler_context == NULL)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen io_loop_initialize_handler(io->io.ioloop);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen io_loop_handle_add(io);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (io->io.ioloop->io_files != NULL) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen io->io.ioloop->io_files->prev = io;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen io->next = io->io.ioloop->io_files;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen io->io.ioloop->io_files = io;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return io;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#undef io_add
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstruct io *io_add(int fd, enum io_condition condition,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned int source_linenum,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io_callback_t *callback, void *context)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct io_file *io;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io = io_add_file(fd, condition, source_linenum, callback, context);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return &io->io;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#undef io_add_istream
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstruct io *io_add_istream(struct istream *input, unsigned int source_linenum,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io_callback_t *callback, void *context)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct io_file *io;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen io = io_add_file(i_stream_get_fd(input), IO_READ, source_linenum,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen callback, context);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io->istream = input;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_ref(io->istream);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_set_io(io->istream, &io->io);
12ea3aeeb61df84632f04f86f47902a3750d61f8Timo Sirainen return &io->io;
12ea3aeeb61df84632f04f86f47902a3750d61f8Timo Sirainen}
12ea3aeeb61df84632f04f86f47902a3750d61f8Timo Sirainen
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainenstatic void io_file_unlink(struct io_file *io)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (io->prev != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io->prev->next = io->next;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen else
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io->io.ioloop->io_files = io->next;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (io->next != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io->next->prev = io->prev;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* if we got here from an I/O handler callback, make sure we
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen don't try to handle this one next. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (io->io.ioloop->next_io_file == io)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io->io.ioloop->next_io_file = io->next;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void io_remove_full(struct io **_io, bool closed)
26550dc10a8c2057d09894c4e3f9dc12b124e90cTimo Sirainen{
26550dc10a8c2057d09894c4e3f9dc12b124e90cTimo Sirainen struct io *io = *_io;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(io->callback != NULL);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen *_io = NULL;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen /* make sure the callback doesn't get called anymore.
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen kqueue code relies on this. */
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen io->callback = NULL;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (io->pending) {
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen i_assert(io->ioloop->io_pending_count > 0);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen io->ioloop->io_pending_count--;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen if (io->ctx != NULL)
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen io_loop_context_unref(&io->ctx);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen if ((io->condition & IO_NOTIFY) != 0)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen io_loop_notify_remove(io);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct io_file *io_file = (struct io_file *)io;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (io_file->istream != NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_unset_io(io_file->istream, io);
dcd50ecbfe796bd76f2d63483c534cc0e4e94164Timo Sirainen i_stream_unref(&io_file->istream);
dcd50ecbfe796bd76f2d63483c534cc0e4e94164Timo Sirainen io_file->istream = NULL;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io_file_unlink(io_file);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io_loop_handle_remove(io_file, closed);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenvoid io_remove(struct io **io)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen io_remove_full(io, FALSE);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenvoid io_remove_closed(struct io **io)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(((*io)->condition & IO_NOTIFY) == 0);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen io_remove_full(io, TRUE);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainenvoid io_set_pending(struct io *io)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert((io->condition & IO_NOTIFY) == 0);
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen if (!io->pending) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen io->pending = TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen io->ioloop->io_pending_count++;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen}
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainenstatic void timeout_update_next(struct timeout *timeout, struct timeval *tv_now)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
bd4d0a1a7c0626452b8d82f37e3ec07267ac9896Timo Sirainen if (tv_now == NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (gettimeofday(&timeout->next_run, NULL) < 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_fatal("gettimeofday(): %m");
263e4b2733768062cb0b8b8917cad78fa2a04ff9Timo Sirainen } else {
263e4b2733768062cb0b8b8917cad78fa2a04ff9Timo Sirainen timeout->next_run.tv_sec = tv_now->tv_sec;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout->next_run.tv_usec = tv_now->tv_usec;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen /* we don't want microsecond accuracy or this function will be
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen called all the time - millisecond is more than enough */
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen timeout->next_run.tv_usec -= timeout->next_run.tv_usec % 1000;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout->next_run.tv_sec += timeout->msecs/1000;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen timeout->next_run.tv_usec += (timeout->msecs%1000)*1000;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (timeout->next_run.tv_usec > 1000000) {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen timeout->next_run.tv_sec++;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen timeout->next_run.tv_usec -= 1000000;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen }
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen}
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen#undef timeout_add
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainenstruct timeout *timeout_add(unsigned int msecs, unsigned int source_linenum,
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen timeout_callback_t *callback, void *context)
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen{
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen struct timeout *timeout;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen timeout = i_new(struct timeout, 1);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen timeout->source_linenum = source_linenum;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen timeout->msecs = msecs;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen timeout->ioloop = current_ioloop;
b87436ebb957a9eb182be72ba00e2c8eae59a2e4Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen timeout->callback = callback;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen timeout->context = context;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen if (timeout->ioloop->cur_ctx != NULL) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen timeout->ctx = timeout->ioloop->cur_ctx;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen io_loop_context_ref(timeout->ctx);
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout_update_next(timeout, timeout->ioloop->running ?
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen NULL : &ioloop_timeval);
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return timeout;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen#undef timeout_add_short
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainenstruct timeout *
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainentimeout_add_short(unsigned int msecs, unsigned int source_linenum,
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen timeout_callback_t *callback, void *context)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return timeout_add(msecs, source_linenum, callback, context);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void timeout_free(struct timeout *timeout)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (timeout->ctx != NULL)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen io_loop_context_unref(&timeout->ctx);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_free(timeout);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
cf942dce0253075911a96cff323b5f30eb654ae0Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenvoid timeout_remove(struct timeout **_timeout)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen struct timeout *timeout = *_timeout;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen *_timeout = NULL;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen timeout_free(timeout);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainenstatic void ATTR_NULL(2)
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainentimeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now)
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen{
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen timeout_update_next(timeout, tv_now);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen if (timeout->msecs <= 1) {
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen /* if we came here from io_loop_handle_timeouts(),
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen next_run must be larger than tv_now or we could go to
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen infinite loop. +1000 to get 1 ms further, another +1000 to
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen account for timeout_update_next()'s truncation. */
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen timeout->next_run.tv_usec += 2000;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen if (timeout->next_run.tv_usec >= 1000000) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen timeout->next_run.tv_sec++;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen timeout->next_run.tv_usec -= 1000000;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_assert(tv_now == NULL ||
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen timeout->next_run.tv_sec > tv_now->tv_sec ||
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen (timeout->next_run.tv_sec == tv_now->tv_sec &&
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen timeout->next_run.tv_usec > tv_now->tv_usec));
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenvoid timeout_reset(struct timeout *timeout)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen timeout_reset_timeval(timeout, NULL);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen}
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainenstatic int timeout_get_wait_time(struct timeout *timeout, struct timeval *tv_r,
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen struct timeval *tv_now)
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen{
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen int ret;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen if (tv_now->tv_sec == 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (gettimeofday(tv_now, NULL) < 0)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_fatal("gettimeofday(): %m");
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_sec = tv_now->tv_sec;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_usec = tv_now->tv_usec;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_assert(tv_r->tv_sec > 0);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_assert(timeout->next_run.tv_sec > 0);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_sec = timeout->next_run.tv_sec - tv_r->tv_sec;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_usec = timeout->next_run.tv_usec - tv_r->tv_usec;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (tv_r->tv_usec < 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_sec--;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_usec += 1000000;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (tv_r->tv_sec < 0 || (tv_r->tv_sec == 0 && tv_r->tv_usec < 1000)) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_sec = 0;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_usec = 0;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return 0;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (tv_r->tv_sec > INT_MAX/1000-1)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_sec = INT_MAX/1000-1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* round wait times up to next millisecond */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ret = tv_r->tv_sec * 1000 + (tv_r->tv_usec + 999) / 1000;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_assert(ret > 0 && tv_r->tv_sec >= 0 && tv_r->tv_usec >= 0);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return ret;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenint io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen struct timeval tv_now;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen struct priorityq_item *item;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct timeout *timeout;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen int msecs;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen item = priorityq_peek(ioloop->timeouts);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen timeout = (struct timeout *)item;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (timeout == NULL) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* no timeouts. use INT_MAX msecs for timeval and
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return -1 for poll/epoll infinity. */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_r->tv_sec = INT_MAX / 1000;
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen tv_r->tv_usec = 0;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ioloop->next_max_time = (1ULL << (TIME_T_MAX_BITS-1)) - 1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return -1;
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
2584e86cc2d8c31ba30a4109cf4ba09d1e37e28aTimo Sirainen tv_now.tv_sec = 0;
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen msecs = timeout_get_wait_time(timeout, tv_r, &tv_now);
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen ioloop->next_max_time = (tv_now.tv_sec + msecs/1000) + 1;
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen return msecs;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen}
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic int timeout_cmp(const void *p1, const void *p2)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen const struct timeout *to1 = p1, *to2 = p2;
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen return timeval_cmp(&to1->next_run, &to2->next_run);
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen}
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainenstatic void io_loop_default_time_moved(time_t old_time, time_t new_time)
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen{
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen if (old_time > new_time) {
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen i_warning("Time moved backwards by %ld seconds.",
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen (long)(old_time - new_time));
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen }
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen}
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenstatic void io_loop_timeouts_update(struct ioloop *ioloop, long diff_secs)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen{
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen struct priorityq_item *const *items;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen unsigned int i, count;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen count = priorityq_count(ioloop->timeouts);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen items = priorityq_items(ioloop->timeouts);
b79a09b5915ff12c10d2e5accf4318776a152e80Timo Sirainen for (i = 0; i < count; i++) {
b79a09b5915ff12c10d2e5accf4318776a152e80Timo Sirainen struct timeout *to = (struct timeout *)items[i];
b79a09b5915ff12c10d2e5accf4318776a152e80Timo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen to->next_run.tv_sec += diff_secs;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen }
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen}
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenstatic void io_loops_timeouts_update(long diff_secs)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen{
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen struct ioloop *ioloop;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen for (ioloop = current_ioloop; ioloop != NULL; ioloop = ioloop->prev)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen io_loop_timeouts_update(ioloop, diff_secs);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen}
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenstatic void io_loop_handle_timeouts_real(struct ioloop *ioloop)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen{
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen struct priorityq_item *item;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen struct timeval tv, tv_call;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen unsigned int t_id;
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen i_fatal("gettimeofday(): %m");
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* Don't bother comparing usecs. */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (unlikely(ioloop_time > ioloop_timeval.tv_sec)) {
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* time moved backwards */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen io_loops_timeouts_update(-(long)(ioloop_time -
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen ioloop_timeval.tv_sec));
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen ioloop->time_moved_callback(ioloop_time,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen ioloop_timeval.tv_sec);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* the callback may have slept, so check the time again. */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen i_fatal("gettimeofday(): %m");
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen } else if (unlikely(ioloop_timeval.tv_sec >
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen ioloop->next_max_time)) {
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen io_loops_timeouts_update(ioloop_timeval.tv_sec -
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ioloop->next_max_time);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* time moved forwards */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ioloop->time_moved_callback(ioloop->next_max_time,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ioloop_timeval.tv_sec);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ioloop_time = ioloop_timeval.tv_sec;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen tv_call = ioloop_timeval;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen while ((item = priorityq_peek(ioloop->timeouts)) != NULL) {
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen struct timeout *timeout = (struct timeout *)item;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* use tv_call to make sure we don't get to infinite loop in
ea9d9d99948cff5f9b881f79b28fa3b80da0f2a7Timo Sirainen case callbacks update ioloop_timeval. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (timeout_get_wait_time(timeout, &tv, &tv_call) > 0)
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen break;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* update timeout's next_run and reposition it in the queue */
ea9d9d99948cff5f9b881f79b28fa3b80da0f2a7Timo Sirainen timeout_reset_timeval(timeout, &tv_call);
ea9d9d99948cff5f9b881f79b28fa3b80da0f2a7Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (timeout->ctx != NULL)
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen io_loop_context_activate(timeout->ctx);
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen t_id = t_push_named("ioloop timeout handler %p",
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen (void *)timeout->callback);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout->callback(timeout->context);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen if (t_pop() != t_id) {
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen i_panic("Leaked a t_pop() call in timeout handler %p",
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen (void *)timeout->callback);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen }
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen if (ioloop->cur_ctx != NULL)
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen io_loop_context_deactivate(ioloop->cur_ctx);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen }
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen}
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainenvoid io_loop_handle_timeouts(struct ioloop *ioloop)
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen{
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen T_BEGIN {
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen io_loop_handle_timeouts_real(ioloop);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen } T_END;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenvoid io_loop_call_io(struct io *io)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct ioloop *ioloop = io->ioloop;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen unsigned int t_id;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (io->pending) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_assert(ioloop->io_pending_count > 0);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ioloop->io_pending_count--;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen io->pending = FALSE;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (io->ctx != NULL)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen io_loop_context_activate(io->ctx);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen t_id = t_push_named("ioloop handler %p",
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen (void *)io->callback);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen io->callback(io->context);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (t_pop() != t_id) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen i_panic("Leaked a t_pop() call in I/O handler %p",
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen (void *)io->callback);
bb34c6538784b564c05eefccc74af2bd7c9505f5Timo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (ioloop->cur_ctx != NULL)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen io_loop_context_deactivate(ioloop->cur_ctx);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
bb34c6538784b564c05eefccc74af2bd7c9505f5Timo Sirainen
bb34c6538784b564c05eefccc74af2bd7c9505f5Timo Sirainenvoid io_loop_run(struct ioloop *ioloop)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (ioloop->handler_context == NULL)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen io_loop_initialize_handler(ioloop);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen if (ioloop->cur_ctx != NULL)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen io_loop_context_unref(&ioloop->cur_ctx);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen /* recursive io_loop_run() isn't allowed for the same ioloop.
4316355ca8b7698516272520a972291378698140Timo Sirainen it can break backends. */
4316355ca8b7698516272520a972291378698140Timo Sirainen i_assert(!ioloop->iolooping);
4316355ca8b7698516272520a972291378698140Timo Sirainen ioloop->iolooping = TRUE;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen ioloop->running = TRUE;
4316355ca8b7698516272520a972291378698140Timo Sirainen while (ioloop->running)
4316355ca8b7698516272520a972291378698140Timo Sirainen io_loop_handler_run(ioloop);
4316355ca8b7698516272520a972291378698140Timo Sirainen ioloop->iolooping = FALSE;
4316355ca8b7698516272520a972291378698140Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void io_loop_call_pending(struct ioloop *ioloop)
4316355ca8b7698516272520a972291378698140Timo Sirainen{
4316355ca8b7698516272520a972291378698140Timo Sirainen struct io_file *io;
4316355ca8b7698516272520a972291378698140Timo Sirainen
4316355ca8b7698516272520a972291378698140Timo Sirainen while (ioloop->io_pending_count > 0) {
4316355ca8b7698516272520a972291378698140Timo Sirainen io = ioloop->io_files;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen do {
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen ioloop->next_io_file = io->next;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen if (io->io.pending)
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen io_loop_call_io(&io->io);
4316355ca8b7698516272520a972291378698140Timo Sirainen if (ioloop->io_pending_count == 0)
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen break;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen io = ioloop->next_io_file;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen } while (io != NULL);
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenvoid io_loop_handler_run(struct ioloop *ioloop)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen{
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen io_loop_handler_run_internal(ioloop);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen io_loop_call_pending(ioloop);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen}
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenvoid io_loop_stop(struct ioloop *ioloop)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen{
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen ioloop->running = FALSE;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen}
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenvoid io_loop_set_running(struct ioloop *ioloop)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen{
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen ioloop->running = TRUE;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen}
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainenvoid io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds)
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen{
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen ioloop->max_fd_count = max_fds;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenbool io_loop_is_running(struct ioloop *ioloop)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen{
a9a77f4632b1f00cc3c6664c91ef5f23746e099bTimo Sirainen return ioloop->running;
a9a77f4632b1f00cc3c6664c91ef5f23746e099bTimo Sirainen}
a9a77f4632b1f00cc3c6664c91ef5f23746e099bTimo Sirainen
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainenvoid io_loop_time_refresh(void)
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen{
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen i_fatal("gettimeofday(): %m");
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen ioloop_time = ioloop_timeval.tv_sec;
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen}
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenstruct ioloop *io_loop_create(void)
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen{
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen struct ioloop *ioloop;
805872b9f885c1cc4bddf0b6ff71ca61f6cdd7bfTimo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen /* initialize time */
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (gettimeofday(&ioloop_timeval, NULL) < 0)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen i_fatal("gettimeofday(): %m");
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen ioloop_time = ioloop_timeval.tv_sec;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen ioloop = i_new(struct ioloop, 1);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen ioloop->timeouts = priorityq_init(timeout_cmp, 32);
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen
f7fcdca120f1b591f885408d08fc90cfd725bf94Timo Sirainen ioloop->time_moved_callback = current_ioloop != NULL ?
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen current_ioloop->time_moved_callback :
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen io_loop_default_time_moved;
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen
f7fcdca120f1b591f885408d08fc90cfd725bf94Timo Sirainen ioloop->prev = current_ioloop;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen io_loop_set_current(ioloop);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return ioloop;
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen}
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainenvoid io_loop_destroy(struct ioloop **_ioloop)
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainen{
f7fcdca120f1b591f885408d08fc90cfd725bf94Timo Sirainen struct ioloop *ioloop = *_ioloop;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen struct priorityq_item *item;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen *_ioloop = NULL;
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen /* ->prev won't work unless loops are destroyed in create order */
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen i_assert(ioloop == current_ioloop);
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen io_loop_set_current(current_ioloop->prev);
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen if (ioloop->notify_handler_context != NULL)
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen io_loop_notify_handler_deinit(ioloop);
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen while (ioloop->io_files != NULL) {
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen struct io_file *io = ioloop->io_files;
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen struct io *_io = &io->io;
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen i_warning("I/O leak: %p (line %u, fd %d)",
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen (void *)io->io.callback,
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen io->io.source_linenum, io->fd);
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen io_remove(&_io);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen }
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen i_assert(ioloop->io_pending_count == 0);
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen while ((item = priorityq_pop(ioloop->timeouts)) != NULL) {
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen struct timeout *to = (struct timeout *)item;
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen i_warning("Timeout leak: %p (line %u)", (void *)to->callback,
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen to->source_linenum);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen timeout_free(to);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen }
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen priorityq_deinit(&ioloop->timeouts);
a9a77f4632b1f00cc3c6664c91ef5f23746e099bTimo Sirainen
a9a77f4632b1f00cc3c6664c91ef5f23746e099bTimo Sirainen if (ioloop->handler_context != NULL)
a9a77f4632b1f00cc3c6664c91ef5f23746e099bTimo Sirainen io_loop_handler_deinit(ioloop);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (ioloop->cur_ctx != NULL)
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen io_loop_context_deactivate(ioloop->cur_ctx);
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen 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;
}