From 5f219f03f6d650df52d18ecbc01d6d53ecea05d8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Oct 2025 14:07:53 +1030 Subject: [PATCH] io: make poll fair, by rotating through fds and io_always() callbacks. In particular, if an fd defers work to an io_always(), it would get serviced immediately. Now we rotate through, not only removing biassing to earlier fds but also interleaving those io_always(). Signed-off-by: Rusty Russell --- ccan/io/poll.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/ccan/io/poll.c b/ccan/io/poll.c index 7fe9e2c5..656cc0e3 100644 --- a/ccan/io/poll.c +++ b/ccan/io/poll.c @@ -373,6 +373,8 @@ static void restore_pollfds(void) void *io_loop(struct timers *timers, struct timer **expired) { void *ret; + /* This ensures we don't always service lower fds first */ + static int fairness_counter; /* if timers is NULL, expired must be. If not, not. */ assert(!timers == !expired); @@ -384,17 +386,12 @@ void *io_loop(struct timers *timers, struct timer **expired) while (!io_loop_return) { int i, r, ms_timeout = -1; - if (handle_always()) { - /* Could have started/finished more. */ - continue; - } - /* Everything closed? */ if (num_fds == 0) break; /* You can't tell them all to go to sleep! */ - assert(num_waiting); + assert(num_waiting || num_always); if (timers) { struct timemono now, first; @@ -417,6 +414,10 @@ void *io_loop(struct timers *timers, struct timer **expired) } } + /* Don't wait if we have always requests pending! */ + if (num_always != 0) + ms_timeout = 0; + /* We do this temporarily, assuming exclusive is unusual */ exclude_pollfds(); r = pollfn(pollfds, num_fds, ms_timeout); @@ -430,15 +431,29 @@ void *io_loop(struct timers *timers, struct timer **expired) break; } - for (i = 0; i < num_fds && !io_loop_return; i++) { - struct io_conn *c = (void *)fds[i]; - int events = pollfds[i].revents; + fairness_counter++; + for (size_t rotation = 0; rotation < num_fds && !io_loop_return; rotation++) { + struct io_conn *c; + int events; + + i = (rotation + fairness_counter) % num_fds; + c = (void *)fds[i]; /* Clear so we don't get confused if exclusive next time */ + events = pollfds[i].revents; pollfds[i].revents = 0; - if (r == 0) + /* Timeout? */ + if (r == 0) { + handle_always(); break; + } + + /* We interleave always before the first fd */ + if (i == 0 && handle_always()) { + /* Could have started/finished more. */ + break; + } if (fds[i]->listener) { struct io_listener *l = (void *)fds[i]; -- 2.47.3