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;
}
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 {
+ 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 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;
--- /dev/null
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "time-util.h"
+#include "ioloop.h"
+
+#include <unistd.h>
+
+static void timeout_callback(struct timeval *tv)
+{
+ if (gettimeofday(tv, NULL) < 0)
+ i_fatal("gettimeofday() failed: %m");
+ io_loop_stop(current_ioloop);
+}
+
+static void test_ioloop_timeout(void)
+{
+ struct ioloop *ioloop;
+ struct timeout *to;
+ struct timeval tv_start, tv_callback;
+
+ test_begin("ioloop timeout");
+
+ ioloop = io_loop_create();
+ sleep(1);
+ to = timeout_add(1000, timeout_callback, &tv_callback);
+ timeout_remove(&to);
+ 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);
+ io_loop_destroy(&ioloop);
+
+ test_end();
+}
+
+void test_ioloop(void)
+{
+ test_ioloop_timeout();
+}