]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: test-ioloop - Make sure recreating 0-timeout in callback works
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 1 Mar 2021 10:56:21 +0000 (12:56 +0200)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Tue, 23 Mar 2021 16:02:32 +0000 (16:02 +0000)
src/lib/ioloop.c
src/lib/test-ioloop.c

index f3be87480d4b48bc4a58dc43516ce8cfec87d37b..43251cd3f6739b53684e35173c9577961d457337 100644 (file)
@@ -288,7 +288,9 @@ struct timeout *timeout_add_to(struct ioloop *ioloop, unsigned int msecs,
                /* 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);
index 57fdacd3e549bbf4cf97e63f7e13b7f1e1f3d618..91f38e0ea27c6f860249d559fd7066a576ea7fa2 100644 (file)
@@ -168,6 +168,56 @@ static void test_ioloop_zero_timeout(void)
        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)
 {
 }
@@ -241,6 +291,7 @@ void test_ioloop(void)
 {
        test_ioloop_timeout();
        test_ioloop_zero_timeout();
+       test_ioloop_zero_timeout_recreate();
        test_ioloop_find_fd_conditions();
        test_ioloop_pending_io();
        test_ioloop_fd();