ioloop.c revision 3e41b3d0cf90bbc1c229abb98cad944373316362
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic ARRAY(io_switch_callback_t *) io_switch_callbacks = ARRAY_INIT;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void io_loop_initialize_handler(struct ioloop *ioloop)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen initial_fd_count = ioloop->max_fd_count > 0 &&
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ioloop->max_fd_count < IOLOOP_INITIAL_FD_COUNT ?
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ioloop->max_fd_count : IOLOOP_INITIAL_FD_COUNT;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen io_loop_handler_init(ioloop, initial_fd_count);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic struct io_file *
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenio_add_file(int fd, enum io_condition condition,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* we're adding an istream whose only way to get notified
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen is to call i_stream_set_input_pending() */
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainenstruct io *io_add(int fd, enum io_condition condition,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen io = io_add_file(fd, condition, source_filename, source_linenum, callback, context);
b104354c4a829e828c361e4a167bf7106d124174Timo Sirainenstruct io *io_add_istream(struct istream *input, const char *source_filename,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen io = io_add_file(i_stream_get_fd(input), IO_READ, source_filename,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* if we got here from an I/O handler callback, make sure we
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen don't try to handle this one next. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void io_remove_full(struct io **_io, bool closed)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* make sure the callback doesn't get called anymore.
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen kqueue code relies on this. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct io_file *io_file = (struct io_file *)io;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* remove io before it's freed */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* remove io from the ioloop before unreferencing the istream,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen because a destroyed istream may automatically close the
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_assert(((*io)->condition & IO_NOTIFY) == 0);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void timeout_update_next(struct timeout *timeout, struct timeval *tv_now)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (gettimeofday(&timeout->next_run, NULL) < 0)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* we don't want microsecond accuracy or this function will be
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen called all the time - millisecond is more than enough */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen timeout->next_run.tv_usec -= timeout->next_run.tv_usec % 1000;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen timeout->next_run.tv_sec += timeout->msecs/1000;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen timeout->next_run.tv_usec += (timeout->msecs%1000)*1000;
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainenstatic struct timeout *
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainentimeout_add_common(const char *source_filename, unsigned int source_linenum,
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainenstruct timeout *timeout_add(unsigned int msecs, const char *source_filename,
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen timeout = timeout_add_common(source_filename, source_linenum, callback, context);
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen /* start this timeout in the next run cycle */
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen array_append(&timeout->ioloop->timeouts_new, &timeout, 1);
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen /* trigger zero timeouts as soon as possible */
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen timeout_update_next(timeout, timeout->ioloop->running ?
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainentimeout_add_short(unsigned int msecs, const char *source_filename,
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen return timeout_add(msecs, source_filename, source_linenum, callback, context);
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainentimeout_add_absolute(const struct timeval *time,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen timeout = timeout_add_common(source_filename, source_linenum,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainenstatic struct timeout *
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen (old_to->source_filename, old_to->source_linenum,
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen priorityq_add(new_to->ioloop->timeouts, &new_to->item);
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen array_append(&new_to->ioloop->timeouts_new, &new_to, 1);
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainenstatic void timeout_free(struct timeout *timeout)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen else if (!timeout->one_shot && timeout->msecs > 0) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen array_foreach(&ioloop->timeouts_new, to_idx) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen array_foreach_idx(&ioloop->timeouts_new, to_idx), 1);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainentimeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* if we came here from io_loop_handle_timeouts(),
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen next_run must be larger than tv_now or we could go to
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen infinite loop. +1000 to get 1 ms further, another +1000 to
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen account for timeout_update_next()'s truncation. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen (timeout->next_run.tv_sec == tv_now->tv_sec &&
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen timeout->next_run.tv_usec > tv_now->tv_usec));
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic int timeout_get_wait_time(struct timeout *timeout, struct timeval *tv_r,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen tv_r->tv_sec = timeout->next_run.tv_sec - tv_r->tv_sec;
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen tv_r->tv_usec = timeout->next_run.tv_usec - tv_r->tv_usec;
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen if (tv_r->tv_sec < 0 || (tv_r->tv_sec == 0 && tv_r->tv_usec < 1000)) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* round wait times up to next millisecond */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ret = tv_r->tv_sec * 1000 + (tv_r->tv_usec + 999) / 1000;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_assert(ret > 0 && tv_r->tv_sec >= 0 && tv_r->tv_usec >= 0);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenint io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* we need to see if there are pending IO waiting,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if there is, we set msecs = 0 to ensure they are
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen processed without delay */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (timeout == NULL && ioloop->io_pending_count == 0) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* no timeouts. use INT_MAX msecs for timeval and
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen return -1 for poll/epoll infinity. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ioloop->next_max_time = (1ULL << (TIME_T_MAX_BITS-1)) - 1;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen msecs = timeout_get_wait_time(timeout, tv_r, &tv_now);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ioloop->next_max_time = (tv_now.tv_sec + msecs/1000) + 1;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* update ioloop_timeval - this is meant for io_loop_handle_timeouts()'s
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ioloop_wait_usecs calculation. normally after this we go to the
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ioloop and after that we update ioloop_timeval immediately again. */
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainenstatic int timeout_cmp(const void *p1, const void *p2)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen return timeval_cmp(&to1->next_run, &to2->next_run);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void io_loop_default_time_moved(time_t old_time, time_t new_time)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_warning("Time moved backwards by %ld seconds.",
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void io_loop_timeouts_start_new(struct ioloop *ioloop)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen array_foreach(&ioloop->timeouts_new, to_idx) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen timeout_update_next(timeout, &ioloop_timeval);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen priorityq_add(ioloop->timeouts, &timeout->item);
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainenstatic void io_loop_timeouts_update(struct ioloop *ioloop, long diff_secs)
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen unsigned int i, count;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (i = 0; i < count; i++) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct timeout *to = (struct timeout *)items[i];
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void io_loops_timeouts_update(long diff_secs)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (ioloop = current_ioloop; ioloop != NULL; ioloop = ioloop->prev)
08837f59c1466ec0f533f120b167f2a3e87da738Timo Sirainenstatic void io_loop_handle_timeouts_real(struct ioloop *ioloop)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct timeval tv, tv_call, prev_ioloop_timeval = ioloop_timeval;
115cf0320f679e4e63db25e7f44f47977b8338deTimo Sirainen /* Don't bother comparing usecs. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (unlikely(ioloop_time > ioloop_timeval.tv_sec)) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* time moved backwards */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen io_loops_timeouts_update(-(long)(ioloop_time -
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* the callback may have slept, so check the time again. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen io_loops_timeouts_update(ioloop_timeval.tv_sec -
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* time moved forwards */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ioloop->time_moved_callback(ioloop->next_max_time,
459f60325f94f486ef057241b42d8a9e9c376fb4Timo Sirainen timeval_diff_usecs(&ioloop_timeval, &prev_ioloop_timeval);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen while ((item = priorityq_peek(ioloop->timeouts)) != NULL) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct timeout *timeout = (struct timeout *)item;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* use tv_call to make sure we don't get to infinite loop in
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen case callbacks update ioloop_timeval. */
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen if (timeout_get_wait_time(timeout, &tv, &tv_call) > 0)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* remove timeout from queue */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* update timeout's next_run and reposition it in the queue */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen t_id = t_push_named("ioloop timeout handler %p",
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_panic("Leaked a t_pop() call in timeout handler %p",
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid io_loop_handle_timeouts(struct ioloop *ioloop)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_panic("Leaked a t_pop() call in I/O handler %p",
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* recursive io_loop_run() isn't allowed for the same ioloop.
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen it can break backends. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void io_loop_call_pending(struct ioloop *ioloop)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid io_loop_handler_run(struct ioloop *ioloop)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid io_loop_set_running(struct ioloop *ioloop)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenvoid io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds)
return ioloop;
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;
return conditions;