/* start this timeout in the next run cycle */
array_push_back(&timeout->ioloop->timeouts_new, &timeout);
} else {
- /* trigger zero timeouts as soon as possible */
+ /* Trigger zero timeouts as soon as possible. When ioloop is
+ running, refresh the timestamp to prevent infinite loops
+ in case a timeout callback keeps recreating the 0-timeout. */
timeout_update_next(timeout, timeout->ioloop->running ?
NULL : &ioloop_timeval);
priorityq_add(timeout->ioloop->timeouts, &timeout->item);
test_end();
}
+struct zero_timeout_recreate_ctx {
+ struct timeout *to;
+ unsigned int counter;
+};
+
+static void
+zero_timeout_recreate_callback(struct zero_timeout_recreate_ctx *ctx)
+{
+ timeout_remove(&ctx->to);
+ ctx->to = timeout_add_short(0, zero_timeout_recreate_callback, ctx);
+ ctx->counter++;
+}
+
+static void test_ioloop_zero_timeout_recreate(void)
+{
+ struct ioloop *ioloop;
+ struct io *io;
+ struct zero_timeout_recreate_ctx ctx = { .counter = 0 };
+ int fd[2];
+
+ test_begin("ioloop zero timeout recreate");
+
+ if (pipe(fd) < 0)
+ i_fatal("pipe() failed: %m");
+ switch (fork()) {
+ case (pid_t)-1:
+ i_fatal("fork() failed: %m");
+ case 0:
+ sleep(1);
+ char c = 0;
+ if (write(fd[1], &c, 1) < 0)
+ i_fatal("write(pipe) failed: %m");
+ test_exit(0);
+ default:
+ break;
+ }
+
+ ioloop = io_loop_create();
+ ctx.to = timeout_add_short(0, zero_timeout_recreate_callback, &ctx);
+ io = io_add(fd[0], IO_READ, io_loop_stop, ioloop);
+
+ io_loop_run(ioloop);
+ test_assert_ucmp(ctx.counter, >, 1000);
+
+ timeout_remove(&ctx.to);
+ io_remove(&io);
+ io_loop_destroy(&ioloop);
+ test_end();
+}
+
static void io_callback(void *context ATTR_UNUSED)
{
}
{
test_ioloop_timeout();
test_ioloop_zero_timeout();
+ test_ioloop_zero_timeout_recreate();
test_ioloop_find_fd_conditions();
test_ioloop_pending_io();
test_ioloop_fd();