ioloop.c revision 3809334f938c69df473f154978204e9044840c0b
89a126810703c666309310d0f3189e9834d70b5bTimo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic ARRAY(io_switch_callback_t *) io_switch_callbacks = ARRAY_INIT;
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainenstatic void io_loop_initialize_handler(struct ioloop *ioloop)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen initial_fd_count = ioloop->max_fd_count > 0 &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ioloop->max_fd_count < IOLOOP_INITIAL_FD_COUNT ?
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ioloop->max_fd_count : IOLOOP_INITIAL_FD_COUNT;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen io_loop_handler_init(ioloop, initial_fd_count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic struct io_file *
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainenio_add_file(int fd, enum io_condition condition,
ba00293b85c7fb4e7a2d100991c716e17b9daaaeTimo Sirainen /* we're adding an istream whose only way to get notified
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainen is to call i_stream_set_input_pending() */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct io *io_add(int fd, enum io_condition condition,
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen io = io_add_file(fd, condition, source_filename, source_linenum, callback, context);
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainenstruct io *io_add_istream(struct istream *input, const char *source_filename,
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen io = io_add_file(i_stream_get_fd(input), IO_READ, source_filename,
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen /* if we got here from an I/O handler callback, make sure we
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen don't try to handle this one next. */
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainenstatic void io_remove_full(struct io **_io, bool closed)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* make sure the callback doesn't get called anymore.
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen kqueue code relies on this. */
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen struct io_file *io_file = (struct io_file *)io;
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* remove io before it's freed */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* remove io from the ioloop before unreferencing the istream,
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen because a destroyed istream may automatically close the
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen i_assert(((*io)->condition & IO_NOTIFY) == 0);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainenstatic void timeout_update_next(struct timeout *timeout, struct timeval *tv_now)
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen if (gettimeofday(&timeout->next_run, NULL) < 0)
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* we don't want microsecond accuracy or this function will be
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen called all the time - millisecond is more than enough */
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen timeout->next_run.tv_usec -= timeout->next_run.tv_usec % 1000;
6825360d446542046757b06064282301c4c6b27cTimo Sirainen timeout->next_run.tv_sec += timeout->msecs/1000;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen timeout->next_run.tv_usec += (timeout->msecs%1000)*1000;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenstatic struct timeout *
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainentimeout_add_common(const char *source_filename, unsigned int source_linenum,
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenstruct timeout *timeout_add(unsigned int msecs, const char *source_filename,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen timeout = timeout_add_common(source_filename, source_linenum, callback, context);
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen /* start this timeout in the next run cycle */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen array_append(&timeout->ioloop->timeouts_new, &timeout, 1);
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen /* trigger zero timeouts as soon as possible */
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen timeout_update_next(timeout, timeout->ioloop->running ?
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainentimeout_add_short(unsigned int msecs, const char *source_filename,
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen return timeout_add(msecs, source_filename, source_linenum, callback, context);
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainentimeout_add_absolute(const struct timeval *time,
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen timeout = timeout_add_common(source_filename, source_linenum,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
e3689d0f073341e844638f34e1e4d0b7bb053cc8Timo Sirainenstatic struct timeout *
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen (old_to->source_filename, old_to->source_linenum,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen priorityq_add(new_to->ioloop->timeouts, &new_to->item);
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen array_append(&new_to->ioloop->timeouts_new, &new_to, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void timeout_free(struct timeout *timeout)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen else if (!timeout->one_shot && timeout->msecs > 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen array_foreach(&ioloop->timeouts_new, to_idx) {
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen array_foreach_idx(&ioloop->timeouts_new, to_idx), 1);
af1e2b2ab5d1c5ca5afe482ef8c8161c17acc190Timo Sirainentimeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* if we came here from io_loop_handle_timeouts(),
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen next_run must be larger than tv_now or we could go to
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen infinite loop. +1000 to get 1 ms further, another +1000 to
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen account for timeout_update_next()'s truncation. */
da5d50534cfca45d0aaaf0bdac17b287b4588809Timo Sirainen (timeout->next_run.tv_sec == tv_now->tv_sec &&
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen timeout->next_run.tv_usec > tv_now->tv_usec));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainenstatic int timeout_get_wait_time(struct timeout *timeout, struct timeval *tv_r,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen tv_r->tv_sec = timeout->next_run.tv_sec - tv_r->tv_sec;
99e8698f598d2b83da7c581584a538c0713fd11dTimo Sirainen tv_r->tv_usec = timeout->next_run.tv_usec - tv_r->tv_usec;
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen if (tv_r->tv_sec < 0 || (tv_r->tv_sec == 0 && tv_r->tv_usec < 1000)) {
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen /* round wait times up to next millisecond */
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen ret = tv_r->tv_sec * 1000 + (tv_r->tv_usec + 999) / 1000;
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen i_assert(ret > 0 && tv_r->tv_sec >= 0 && tv_r->tv_usec >= 0);
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainenint io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r)
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen /* we need to see if there are pending IO waiting,
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen if there is, we set msecs = 0 to ensure they are
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen processed without delay */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (timeout == NULL && ioloop->io_pending_count == 0) {
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen /* no timeouts. use INT_MAX msecs for timeval and
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen return -1 for poll/epoll infinity. */
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen ioloop->next_max_time = (1ULL << (TIME_T_MAX_BITS-1)) - 1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen msecs = timeout_get_wait_time(timeout, tv_r, &tv_now);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ioloop->next_max_time = (tv_now.tv_sec + msecs/1000) + 1;
e670e1783fe4541dc3fc6109a181d45b0a9c2635Timo Sirainen /* update ioloop_timeval - this is meant for io_loop_handle_timeouts()'s
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen ioloop_wait_usecs calculation. normally after this we go to the
e670e1783fe4541dc3fc6109a181d45b0a9c2635Timo Sirainen ioloop and after that we update ioloop_timeval immediately again. */
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainenstatic int timeout_cmp(const void *p1, const void *p2)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return timeval_cmp(&to1->next_run, &to2->next_run);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void io_loop_default_time_moved(time_t old_time, time_t new_time)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_warning("Time moved backwards by %ld seconds.",
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainenstatic void io_loop_timeouts_start_new(struct ioloop *ioloop)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen array_foreach(&ioloop->timeouts_new, to_idx) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen timeout_update_next(timeout, &ioloop_timeval);
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen priorityq_add(ioloop->timeouts, &timeout->item);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void io_loop_timeouts_update(struct ioloop *ioloop, long diff_secs)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen unsigned int i, count;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen for (i = 0; i < count; i++) {
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen struct timeout *to = (struct timeout *)items[i];
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void io_loops_timeouts_update(long diff_secs)
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen for (ioloop = current_ioloop; ioloop != NULL; ioloop = ioloop->prev)
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainenstatic void ioloop_add_wait_time(struct ioloop *ioloop)
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen timeval_diff_usecs(&ioloop_timeval, &ioloop->wait_started);
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainenstatic void io_loop_handle_timeouts_real(struct ioloop *ioloop)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* Don't bother comparing usecs. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (unlikely(ioloop_time > ioloop_timeval.tv_sec)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* time moved backwards */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen io_loops_timeouts_update(-(long)(ioloop_time -
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* the callback may have slept, so check the time again. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen io_loops_timeouts_update(ioloop_timeval.tv_sec -
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* time moved forwards */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ioloop->time_moved_callback(ioloop->next_max_time,
2469ed8e17534f6cb5f41493df8c7e6f3b2c9b61Timo Sirainen while ((item = priorityq_peek(ioloop->timeouts)) != NULL) {
c61d52810496376a2ea60b8f4e27bbcaa8754f3fTimo Sirainen struct timeout *timeout = (struct timeout *)item;
f4e0148e539c6dd4e12b8305ab0dd5e63c46ba67Timo Sirainen /* use tv_call to make sure we don't get to infinite loop in
f4e0148e539c6dd4e12b8305ab0dd5e63c46ba67Timo Sirainen case callbacks update ioloop_timeval. */
2bd96c58be42146cb84076331604cadb2994fce5Timo Sirainen if (timeout_get_wait_time(timeout, &tv, &tv_call) > 0)
27db4ce5fe399c981e09dcf9e885a1546afd34f4Timo Sirainen /* remove timeout from queue */
5cc772dc8b507be0bc1996b5717943ba13432e08Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
5cc772dc8b507be0bc1996b5717943ba13432e08Timo Sirainen /* update timeout's next_run and reposition it in the queue */
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen t_id = t_push_named("ioloop timeout handler %p",
ae14dfd895881f9d1c6899b0c09f1a8b51447d61Timo Sirainen i_panic("Leaked a t_pop() call in timeout handler %p",
31189eeac1ccaaf1201c60427f8c1087b0f5dfceTimo Sirainenvoid io_loop_handle_timeouts(struct ioloop *ioloop)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_panic("Leaked a t_pop() call in I/O handler %p",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* recursive io_loop_run() isn't allowed for the same ioloop.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen it can break backends. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void io_loop_call_pending(struct ioloop *ioloop)
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainenvoid io_loop_handler_run(struct ioloop *ioloop)
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainenvoid io_loop_set_running(struct ioloop *ioloop)
2b498cb82aaad8a11adb5a27a29c55b9c334a1ecTimo Sirainenvoid io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds)
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen /* initialize time */
2b498cb82aaad8a11adb5a27a29c55b9c334a1ecTimo Sirainen ioloop->timeouts = priorityq_init(timeout_cmp, 32);
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;