struct timeout *timeout;
timeout = i_new(struct timeout, 1);
- timeout->source_linenum = source_linenum;
+ timeout->item.idx = UINT_MAX;
+ timeout->source_linenum = source_linenum;
timeout->ioloop = current_ioloop;
timeout->callback = callback;
timeout = timeout_add_common(source_linenum, callback, context);
timeout->msecs = msecs;
- timeout_update_next(timeout, timeout->ioloop->running ?
+ if (msecs > 0) {
+ /* start this timeout in the next run cycle */
+ array_append(&timeout->ioloop->timeouts_new, &timeout, 1);
+ } else {
+ /* trigger zero timeouts as soon as possible */
+ timeout_update_next(timeout, timeout->ioloop->running ?
NULL : &ioloop_timeval);
- priorityq_add(timeout->ioloop->timeouts, &timeout->item);
+ priorityq_add(timeout->ioloop->timeouts, &timeout->item);
+ }
return timeout;
}
new_to->one_shot = old_to->one_shot;
new_to->msecs = old_to->msecs;
new_to->next_run = old_to->next_run;
- priorityq_add(new_to->ioloop->timeouts, &new_to->item);
+
+ if (old_to->item.idx != UINT_MAX)
+ priorityq_add(new_to->ioloop->timeouts, &new_to->item);
+ else if (!new_to->one_shot) {
+ i_assert(new_to->msecs > 0);
+ array_append(&new_to->ioloop->timeouts_new, &new_to, 1);
+ }
return new_to;
}
void timeout_remove(struct timeout **_timeout)
{
struct timeout *timeout = *_timeout;
+ struct ioloop *ioloop = timeout->ioloop;
*_timeout = NULL;
if (timeout->item.idx != UINT_MAX)
priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
+ else if (!timeout->one_shot && timeout->msecs > 0) {
+ struct timeout *const *to_idx;
+ array_foreach(&ioloop->timeouts_new, to_idx) {
+ if (*to_idx == timeout) {
+ array_delete(&ioloop->timeouts_new,
+ array_foreach_idx(&ioloop->timeouts_new, to_idx), 1);
+ break;
+ }
+ }
+ }
timeout_free(timeout);
}
static void ATTR_NULL(2)
timeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now)
{
+ if (timeout->item.idx == UINT_MAX)
+ return;
+
timeout_update_next(timeout, tv_now);
if (timeout->msecs <= 1) {
/* if we came here from io_loop_handle_timeouts(),
}
}
+static void io_loop_timeouts_start_new(struct ioloop *ioloop)
+{
+ struct timeout *const *to_idx;
+
+ if (array_count(&ioloop->timeouts_new) == 0)
+ return;
+
+ io_loop_time_refresh();
+
+ array_foreach(&ioloop->timeouts_new, to_idx) {
+ struct timeout *timeout= *to_idx;
+ i_assert(timeout->next_run.tv_sec == 0 &&
+ timeout->next_run.tv_usec == 0);
+ i_assert(!timeout->one_shot);
+ i_assert(timeout->msecs > 0);
+ timeout_update_next(timeout, &ioloop_timeval);
+ priorityq_add(ioloop->timeouts, &timeout->item);
+ }
+ array_clear(&ioloop->timeouts_new);
+}
+
static void io_loop_timeouts_update(struct ioloop *ioloop, long diff_secs)
{
struct priorityq_item *const *items;
void io_loop_handler_run(struct ioloop *ioloop)
{
+ io_loop_timeouts_start_new(ioloop);
io_loop_handler_run_internal(ioloop);
io_loop_call_pending(ioloop);
}
ioloop = i_new(struct ioloop, 1);
ioloop->timeouts = priorityq_init(timeout_cmp, 32);
+ i_array_init(&ioloop->timeouts_new, 8);
ioloop->time_moved_callback = current_ioloop != NULL ?
current_ioloop->time_moved_callback :
void io_loop_destroy(struct ioloop **_ioloop)
{
struct ioloop *ioloop = *_ioloop;
+ struct timeout *const *to_idx;
struct priorityq_item *item;
*_ioloop = NULL;
}
i_assert(ioloop->io_pending_count == 0);
+ array_foreach(&ioloop->timeouts_new, to_idx) {
+ struct timeout *to = *to_idx;
+
+ i_warning("Timeout leak: %p (line %u)", (void *)to->callback,
+ to->source_linenum);
+ timeout_free(to);
+ }
+ array_free(&ioloop->timeouts_new);
+
while ((item = priorityq_pop(ioloop->timeouts)) != NULL) {
struct timeout *to = (struct timeout *)item;
static void test_ioloop_timeout(void)
{
- struct ioloop *ioloop;
- struct timeout *to;
+ struct ioloop *ioloop, *ioloop2;
+ struct timeout *to, *to2;
struct timeval tv_start, tv_callback;
test_begin("ioloop timeout");
ioloop = io_loop_create();
+
+ /* add a timeout by moving it from another ioloop */
+ ioloop2 = io_loop_create();
+ to2 = timeout_add(1000, timeout_callback, &tv_callback);
+ io_loop_set_current(ioloop);
+ to2 = io_loop_move_timeout(&to2);
+ io_loop_set_current(ioloop2);
+ io_loop_destroy(&ioloop2);
+
sleep(1);
+
+ /* add & remove immediately */
to = timeout_add(1000, timeout_callback, &tv_callback);
timeout_remove(&to);
+
+ /* add the timeout we're actually testing below */
to = timeout_add(1000, timeout_callback, &tv_callback);
if (gettimeofday(&tv_start, NULL) < 0)
i_fatal("gettimeofday() failed: %m");
io_loop_run(ioloop);
test_assert(timeval_diff_msecs(&tv_callback, &tv_start) >= 500);
timeout_remove(&to);
+ timeout_remove(&to2);
io_loop_destroy(&ioloop);
test_end();