]> git.ozlabs.org Git - ccan/commitdiff
io: make poll fair, by rotating through fds and io_always() callbacks. master
authorRusty Russell <rusty@rustcorp.com.au>
Mon, 20 Oct 2025 03:37:53 +0000 (14:07 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 20 Oct 2025 03:53:24 +0000 (14:23 +1030)
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 <rusty@rustcorp.com.au>
ccan/io/poll.c

index 7fe9e2c5e0bbb4da297c408c742c3e512c244c5d..656cc0e3a33c4c62ee93cbb5c1b0db5b0dbaf06e 100644 (file)
@@ -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];