ioloop.c revision 33b0119d4effb14cd0f1bdd3ad5f2954e3b1e63e
2e37d45867d081db150ab78dad303b9077aea24fTimo Sirainen/* Copyright (c) 2002-2014 Dovecot authors, see the included COPYING file */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic ARRAY(io_switch_callback_t *) io_switch_callbacks = ARRAY_INIT;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenstatic void io_loop_initialize_handler(struct ioloop *ioloop)
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 Sirainenstatic struct io_file *
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenio_add_file(int fd, enum io_condition condition,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstruct io *io_add(int fd, enum io_condition condition,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io = io_add_file(fd, condition, source_linenum, callback, context);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstruct io *io_add_istream(struct istream *input, unsigned int source_linenum,
96308127e006bb3b1108093bcf4cc1fd9481cb7aTimo Sirainen io = io_add_file(i_stream_get_fd(input), IO_READ, source_linenum,
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. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void io_remove_full(struct io **_io, bool closed)
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen /* make sure the callback doesn't get called anymore.
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen kqueue code relies on this. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct io_file *io_file = (struct io_file *)io;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(((*io)->condition & IO_NOTIFY) == 0);
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainenstatic void timeout_update_next(struct timeout *timeout, struct timeval *tv_now)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (gettimeofday(&timeout->next_run, NULL) < 0)
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;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout->next_run.tv_sec += timeout->msecs/1000;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen timeout->next_run.tv_usec += (timeout->msecs%1000)*1000;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainenstruct timeout *timeout_add(unsigned int msecs, unsigned int source_linenum,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout_update_next(timeout, timeout->ioloop->running ?
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainentimeout_add_short(unsigned int msecs, unsigned int source_linenum,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return timeout_add(msecs, source_linenum, callback, context);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void timeout_free(struct timeout *timeout)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainentimeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now)
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. */
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);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainenstatic int timeout_get_wait_time(struct timeout *timeout, struct timeval *tv_r,
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;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (tv_r->tv_sec < 0 || (tv_r->tv_sec == 0 && tv_r->tv_usec < 1000)) {
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 Sirainenint io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* no timeouts. use INT_MAX msecs for timeval and
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return -1 for poll/epoll infinity. */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ioloop->next_max_time = (1ULL << (TIME_T_MAX_BITS-1)) - 1;
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;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic int timeout_cmp(const void *p1, const void *p2)
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen return timeval_cmp(&to1->next_run, &to2->next_run);
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainenstatic void io_loop_default_time_moved(time_t old_time, time_t new_time)
1e9c53531a31e3ad0168872e513046a3222a1afdTimo Sirainen i_warning("Time moved backwards by %ld seconds.",
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenstatic void io_loop_timeouts_update(struct ioloop *ioloop, long diff_secs)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen unsigned int i, count;
b79a09b5915ff12c10d2e5accf4318776a152e80Timo Sirainen for (i = 0; i < count; i++) {
b79a09b5915ff12c10d2e5accf4318776a152e80Timo Sirainen struct timeout *to = (struct timeout *)items[i];
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenstatic void io_loops_timeouts_update(long diff_secs)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen for (ioloop = current_ioloop; ioloop != NULL; ioloop = ioloop->prev)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenstatic void io_loop_handle_timeouts_real(struct ioloop *ioloop)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen unsigned int t_id;
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 /* the callback may have slept, so check the time again. */
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen io_loops_timeouts_update(ioloop_timeval.tv_sec -
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* time moved forwards */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ioloop->time_moved_callback(ioloop->next_max_time,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen while ((item = priorityq_peek(ioloop->timeouts)) != NULL) {
e03d986a74128f5ba30fcfda9f6e36578f5d8decTimo Sirainen struct timeout *timeout = (struct timeout *)item;
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)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* update timeout's next_run and reposition it in the queue */
40c24f8fe31037b61f2ac594a6dfcd123080b4b5Timo Sirainen t_id = t_push_named("ioloop timeout handler %p",
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen i_panic("Leaked a t_pop() call in timeout handler %p",
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainenvoid io_loop_handle_timeouts(struct ioloop *ioloop)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen unsigned int t_id;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen i_panic("Leaked a t_pop() call in I/O handler %p",
4316355ca8b7698516272520a972291378698140Timo Sirainen /* recursive io_loop_run() isn't allowed for the same ioloop.
4316355ca8b7698516272520a972291378698140Timo Sirainen it can break backends. */
4316355ca8b7698516272520a972291378698140Timo Sirainenstatic void io_loop_call_pending(struct ioloop *ioloop)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenvoid io_loop_handler_run(struct ioloop *ioloop)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainenvoid io_loop_set_running(struct ioloop *ioloop)
d7c2ae49f036ef256f63bd2d437c53da122c665eTimo Sirainenvoid io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds)
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen /* initialize time */
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen ioloop->timeouts = priorityq_init(timeout_cmp, 32);
f7fcdca120f1b591f885408d08fc90cfd725bf94Timo Sirainen ioloop->time_moved_callback = current_ioloop != NULL ?
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen /* ->prev won't work unless loops are destroyed in create order */
eafecb7ad0fbbde0da8be7e8ae735459b7bb688bTimo Sirainen while ((item = priorityq_pop(ioloop->timeouts)) != NULL) {
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen i_warning("Timeout leak: %p (line %u)", (void *)to->callback,
static void io_switch_callbacks_free(void)
unsigned int idx;
i_unreached();
return ctx;
here from activate/deactivate loop */
unsigned int i, count;
for (i = 0; i < count; ) {
return old_io;
return old_to;
return new_to;