ioloop.c revision ae800c8a965688ab17415397dbc759a429e78199
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainenstatic void io_loop_initialize_handler(struct ioloop *ioloop)
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen initial_fd_count = ioloop->max_fd_count > 0 &&
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen ioloop->max_fd_count < IOLOOP_INITIAL_FD_COUNT ?
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen ioloop->max_fd_count : IOLOOP_INITIAL_FD_COUNT;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen io_loop_handler_init(ioloop, initial_fd_count);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstruct io *io_add(int fd, enum io_condition condition,
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen /* if we got here from an I/O handler callback, make sure we
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen don't try to handle this one next. */
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainenstatic void io_remove_full(struct io **_io, bool closed)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* make sure the callback doesn't get called anymore.
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen kqueue code relies on this. */
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen struct io_file *io_file = (struct io_file *)io;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen i_assert(((*io)->condition & IO_NOTIFY) == 0);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainenstatic void timeout_update_next(struct timeout *timeout, struct timeval *tv_now)
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen if (gettimeofday(&timeout->next_run, NULL) < 0)
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen /* we don't want microsecond accuracy or this function will be
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen called all the time - millisecond is more than enough */
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen timeout->next_run.tv_usec -= timeout->next_run.tv_usec % 1000;
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen timeout->next_run.tv_sec += timeout->msecs/1000;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen timeout->next_run.tv_usec += (timeout->msecs%1000)*1000;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenstruct timeout *timeout_add(unsigned int msecs, timeout_callback_t *callback,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen timeout_update_next(timeout, timeout->ioloop->running ?
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainenstatic void timeout_free(struct timeout *timeout)
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainentimeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now)
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen /* if we came here from io_loop_handle_timeouts(),
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen next_run must be larger than tv_now or we could go to
2524ef7b34965a1b1895d6140fd8296bf57c78d2Timo Sirainen infinite loop. +1000 to get 1 ms further, another +1000 to
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen account for timeout_update_next()'s truncation. */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen (timeout->next_run.tv_sec == tv_now->tv_sec &&
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen timeout->next_run.tv_usec > tv_now->tv_usec));
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen priorityq_add(timeout->ioloop->timeouts, &timeout->item);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen timeout_reset_timeval(timeout, timeout->ioloop->running ? NULL :
66dc739bb67d678770e1b7a7bc75f4f6f9523d2aTimo Sirainenstatic int timeout_get_wait_time(struct timeout *timeout, struct timeval *tv_r,
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen tv_r->tv_sec = timeout->next_run.tv_sec - tv_r->tv_sec;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen tv_r->tv_usec = timeout->next_run.tv_usec - tv_r->tv_usec;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (tv_r->tv_sec < 0 || (tv_r->tv_sec == 0 && tv_r->tv_usec < 1000)) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* round wait times up to next millisecond */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ret = tv_r->tv_sec * 1000 + (tv_r->tv_usec + 999) / 1000;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(ret > 0 && tv_r->tv_sec >= 0 && tv_r->tv_usec >= 0);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenint io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* no timeouts. use INT_MAX msecs for timeval and
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen return -1 for poll/epoll infinity. */
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen ioloop->next_max_time = (1ULL << (TIME_T_MAX_BITS-1)) - 1;
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen msecs = timeout_get_wait_time(timeout, tv_r, &tv_now);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen ioloop->next_max_time = (tv_now.tv_sec + msecs/1000) + 1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int timeout_cmp(const void *p1, const void *p2)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return timeval_cmp(&to1->next_run, &to2->next_run);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenstatic void io_loop_default_time_moved(time_t old_time, time_t new_time)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen i_warning("Time moved backwards by %ld seconds.",
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainenstatic void io_loop_timeouts_update(struct ioloop *ioloop, long diff_secs)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen unsigned int i, count;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen for (i = 0; i < count; i++) {
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen struct timeout *to = (struct timeout *)items[i];
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstatic void io_loops_timeouts_update(long diff_secs)
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen for (ioloop = current_ioloop; ioloop != NULL; ioloop = ioloop->prev)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenstatic void io_loop_handle_timeouts_real(struct ioloop *ioloop)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen unsigned int t_id;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen /* Don't bother comparing usecs. */
548e394330621952db0f03dd667b70184c4a37b6Timo Sirainen if (unlikely(ioloop_time > ioloop_timeval.tv_sec)) {
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen /* time moved backwards */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen io_loops_timeouts_update(-(long)(ioloop_time -
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* the callback may have slept, so check the time again. */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen io_loops_timeouts_update(ioloop_timeval.tv_sec -
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* time moved forwards */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen ioloop->time_moved_callback(ioloop->next_max_time,
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen while ((item = priorityq_peek(ioloop->timeouts)) != NULL) {
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen struct timeout *timeout = (struct timeout *)item;
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen /* use tv_call to make sure we don't get to infinite loop in
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen case callbacks update ioloop_timeval. */
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen if (timeout_get_wait_time(timeout, &tv, &tv_call) > 0)
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen /* update timeout's next_run and reposition it in the queue */
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen i_panic("Leaked a t_pop() call in timeout handler %p",
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainenvoid io_loop_handle_timeouts(struct ioloop *ioloop)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen unsigned int t_id;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen i_panic("Leaked a t_pop() call in I/O handler %p",
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainenvoid io_loop_set_running(struct ioloop *ioloop)
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainenvoid io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds)
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen /* initialize time */
c0b1543512bc3e0a3a9f526056a3678a07ce32f5Timo Sirainen ioloop->timeouts = priorityq_init(timeout_cmp, 32);
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen ioloop->time_moved_callback = current_ioloop != NULL ?
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen i_warning("I/O leak: %p (%d)", (void *)io->io.callback, io->fd);
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen while ((item = priorityq_pop(ioloop->timeouts)) != NULL) {
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainen i_warning("Timeout leak: %p", (void *)to->callback);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* ->prev won't work unless loops are destroyed in create order */
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainenvoid io_loop_set_time_moved_callback(struct ioloop *ioloop,
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainenvoid io_loop_set_current(struct ioloop *ioloop)
f5982bb5b0a704e88fa2b44b0b74e365d13103b9Timo Sirainenstruct ioloop_context *io_loop_context_new(struct ioloop *ioloop)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenvoid io_loop_context_ref(struct ioloop_context *ctx)
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainenvoid io_loop_context_unref(struct ioloop_context **_ctx)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* cur_ctx itself keeps a reference */
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainenvoid io_loop_context_add_callbacks(struct ioloop_context *ctx,
abe8754852e70763e92f74caabbcc13d0917714cTimo Sirainenvoid io_loop_context_remove_callbacks(struct ioloop_context *ctx,
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen array_foreach_modifiable(&ctx->callbacks, cb) {
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen cb->activate == activate && cb->deactivate == deactivate) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen /* simply mark it as deleted, since we could get
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen here from activate/deactivate loop */
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen i_panic("io_loop_context_remove_callbacks() context not found");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenio_loop_context_remove_deleted_callbacks(struct ioloop_context *ctx)
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen unsigned int i, count;
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen for (i = 0; i < count; ) {
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainenvoid io_loop_context_activate(struct ioloop_context *ctx)
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainenvoid io_loop_context_deactivate(struct ioloop_context *ctx)
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen io_loop_context_remove_deleted_callbacks(ctx);
14175321ddb88619015866978c05a27786ca4814Timo Sirainen i_assert((old_io->condition & IO_NOTIFY) == 0);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen new_io = io_add(old_io_file->fd, old_io->condition,
14175321ddb88619015866978c05a27786ca4814Timo Sirainenstruct timeout *io_loop_move_timeout(struct timeout **_timeout)