]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
maint: refactor tail.c to use iopoll
authorPádraig Brady <P@draigBrady.com>
Tue, 28 Feb 2023 18:36:02 +0000 (18:36 +0000)
committerPádraig Brady <P@draigBrady.com>
Wed, 1 Mar 2023 19:27:36 +0000 (19:27 +0000)
* src/tail.c (check_output_alive): Reuse iopoll()
rather than directly calling poll() or select().
* src/iopoll.c (iopoll): Refactor to support non blocking operation,
or ignoring descriptors by passing a negative value.
* src/iopoll.h (iopoll): Adjust to support a BLOCK parameter.
* src/tee.c (tee_files): Adjust iopoll() call to explicitly block.
* src/local.mk: Have tail depend on iopoll.c.

src/iopoll.c
src/iopoll.h
src/local.mk
src/tail.c
src/tee.c

index ceb1b43ad25bd7bbc8f4043d5ba39dd8cad4bde4..a73ce05bfc3096e207c16dd29ed0f684512da3e6 100644 (file)
@@ -18,6 +18,8 @@
 
 #include <config.h>
 
+#include <assert.h>
+
   /* poll(2) is needed on AIX (where 'select' gives a readable
      event immediately) and Solaris (where 'select' never gave
      a readable event).  Also use poll(2) on systems we know work
 #if defined _AIX || defined __sun || defined __APPLE__ || \
     defined __linux__ || defined __ANDROID__
 # define IOPOLL_USES_POLL 1
+  /* Check we've not enabled gnulib's poll module
+     as that will emulate poll() in a way not
+     currently compatible with our usage.  */
+# if defined HAVE_POLL
+#  error "gnulib's poll() replacement is currently incompatible"
+# endif
 #endif
 
 #if IOPOLL_USES_POLL
 #include "isapipe.h"
 
 
-/* Wait for fdin to become ready for reading or fdout to become a broken pipe.
-   Return 0 if fdin can be read() without blocking, or IOPOLL_BROKEN_OUTPUT if
-   fdout becomes a broken pipe, otherwise IOPOLL_ERROR if there is a poll()
+/* Wait for FDIN to become ready for reading or FDOUT to become a broken pipe.
+   If either of those are -1, then they're not checked.  Set BLOCK to true
+   to wait for an event, otherwise return the status immediately.
+   Return 0 if not BLOCKing and there is no event on the requested descriptors.
+   Return 0 if FDIN can be read() without blocking, or IOPOLL_BROKEN_OUTPUT if
+   FDOUT becomes a broken pipe, otherwise IOPOLL_ERROR if there is a poll()
    or select() error.  */
 
-#if IOPOLL_USES_POLL
-
 extern int
-iopoll (int fdin, int fdout)
+iopoll (int fdin, int fdout, bool block)
 {
+#if IOPOLL_USES_POLL
+
   struct pollfd pfds[2] = {  /* POLLRDBAND needed for illumos, macOS.  */
     { .fd = fdin,  .events = POLLIN | POLLRDBAND, .revents = 0 },
     { .fd = fdout, .events = POLLRDBAND, .revents = 0 },
   };
+  int ret = 0;
 
-  while (poll (pfds, 2, -1) > 0 || errno == EINTR)
+  while (0 <= ret || errno == EINTR)
     {
-      if (errno == EINTR)
+      ret = poll (pfds, 2, block ? -1 : 0);
+
+      if (ret < 0)
         continue;
+      if (ret == 0 && ! block)
+        return 0;
+      assert (0 < ret);
       if (pfds[0].revents) /* input available or pipe closed indicating EOF; */
         return 0;          /* should now be able to read() without blocking  */
       if (pfds[1].revents)            /* POLLERR, POLLHUP (or POLLNVAL) */
         return IOPOLL_BROKEN_OUTPUT;  /* output error or broken pipe    */
     }
-  return IOPOLL_ERROR;  /* poll error */
-}
 
 #else  /* fall back to select()-based implementation */
 
-extern int
-iopoll (int fdin, int fdout)
-{
   int nfds = (fdin > fdout ? fdin : fdout) + 1;
   int ret = 0;
 
   /* If fdout has an error condition (like a broken pipe) it will be seen
      as ready for reading.  Assumes fdout is not actually readable.  */
-  while (ret >= 0 || errno == EINTR)
+  while (0 <= ret || errno == EINTR)
     {
       fd_set rfds;
       FD_ZERO (&rfds);
-      FD_SET (fdin, &rfds);
-      FD_SET (fdout, &rfds);
-      ret = select (nfds, &rfds, NULL, NULL, NULL);
+      if (0 <= fdin)
+        FD_SET (fdin, &rfds);
+      if (0 <= fdout)
+        FD_SET (fdout, &rfds);
+
+      struct timeval delay = { .tv_sec = 0, .tv_usec = 0 };
+      ret = select (nfds, &rfds, NULL, NULL, block ? NULL : &delay);
 
       if (ret < 0)
         continue;
-      if (FD_ISSET (fdin, &rfds))  /* input available or EOF; should now */
-        return 0;                  /* be able to read() without blocking */
-      if (FD_ISSET (fdout, &rfds))     /* POLLERR, POLLHUP (or POLLIN)   */
-        return IOPOLL_BROKEN_OUTPUT;   /* output error or broken pipe    */
+      if (ret == 0 && ! block)
+        return 0;
+      assert (0 < ret);
+      if (0 <= fdin && FD_ISSET (fdin, &rfds))   /* input available or EOF; */
+        return 0;          /* should now be able to read() without blocking */
+      if (0 <= fdout && FD_ISSET (fdout, &rfds)) /* equiv to POLLERR        */
+        return IOPOLL_BROKEN_OUTPUT;      /* output error or broken pipe    */
     }
-  return IOPOLL_ERROR;  /* select error */
-}
 
 #endif
+  return IOPOLL_ERROR;
+}
+
 
 
 /* Return true if fdin is relevant for iopoll().
index 85935e960d9c9770de84a1bb38128bfc42fd86a5..00fc99836dc0939ffba3f232315407f0b7465ac4 100644 (file)
@@ -1,6 +1,6 @@
 #define IOPOLL_BROKEN_OUTPUT -2
 #define IOPOLL_ERROR         -3
 
-int iopoll (int fdin, int fdout);
+int iopoll (int fdin, int fdout, bool block);
 bool iopoll_input_ok (int fdin);
 bool iopoll_output_ok (int fdout);
index 8269a2f6887166c7bd70804c6bee8f6c24d04620..13eeea8e0f400005b057f44e0b38bf43a8799a09 100644 (file)
@@ -396,7 +396,9 @@ src_arch_SOURCES = src/uname.c src/uname-arch.c
 src_cut_SOURCES = src/cut.c src/set-fields.c
 src_numfmt_SOURCES = src/numfmt.c src/set-fields.c
 
+src_tail_SOURCES = src/tail.c src/iopoll.c
 src_tee_SOURCES = src/tee.c src/iopoll.c
+
 src_sum_SOURCES = src/sum.c src/sum.h src/digest.c
 src_sum_CPPFLAGS = -DHASH_ALGO_SUM=1 $(AM_CPPFLAGS)
 
index 78022fc4b5edce1edbd6e280707513adcada4c62..92534a592c8a11c315f44a8df683da88d1fb5559 100644 (file)
@@ -28,7 +28,6 @@
 #include <stdio.h>
 #include <assert.h>
 #include <getopt.h>
-#include <sys/select.h>
 #include <sys/types.h>
 #include <signal.h>
 
@@ -38,6 +37,7 @@
 #include "die.h"
 #include "error.h"
 #include "fcntl--.h"
+#include "iopoll.h"
 #include "isapipe.h"
 #include "posixver.h"
 #include "quote.h"
 
 #if HAVE_INOTIFY
 # include "hash.h"
-# include <sys/inotify.h>
-#endif
-
-#if defined _AIX || defined __sun || defined __APPLE__ || HAVE_INOTIFY
 # include <poll.h>
+# include <sys/inotify.h>
 #endif
 
 /* Linux can optimize the handling of local files.  */
@@ -352,39 +349,8 @@ check_output_alive (void)
   if (! monitor_output)
     return;
 
-  /* Check we've not enabled gnulib's poll module
-     as that will emulate poll() in a way not
-     currently compatible with tail's usage.  */
-#if defined HAVE_POLL
-# error "gnulib's poll() replacement is currently incompatible"
-#endif
-
-  /* poll(2) is needed on AIX (where 'select' gives a readable
-     event immediately) and Solaris (where 'select' never gave
-     a readable event).  Also use poll(2) on systems we know work
-     and/or are already using poll (inotify).  */
-#if defined _AIX || defined __sun || defined __APPLE__ || HAVE_INOTIFY
-  struct pollfd pfd;
-  pfd.fd = STDOUT_FILENO;
-  pfd.events = pfd.revents = 0;
-  pfd.events |= POLLRDBAND; /* Needed for illumos, macOS.  */
-
-  if (poll (&pfd, 1, 0) > 0 && (pfd.revents & (POLLERR | POLLHUP)))
+  if (iopoll (-1, STDOUT_FILENO, false) == IOPOLL_BROKEN_OUTPUT)
     die_pipe ();
-#else
-  struct timeval delay;
-  delay.tv_sec = delay.tv_usec = 0;
-
-  fd_set rfd;
-  FD_ZERO (&rfd);
-  FD_SET (STDOUT_FILENO, &rfd);
-
-  /* readable event on STDOUT is equivalent to POLLERR,
-     and implies an error condition on output like broken pipe.  */
-  if (select (STDOUT_FILENO + 1, &rfd, NULL, NULL, &delay) == 1)
-    die_pipe ();
-#endif
-
 }
 
 static bool
index e328e6f04dd425ac012a938c522e346237dcf7ab..dcf773e162723b648835591b6167234e5d4cb9ae 100644 (file)
--- a/src/tee.c
+++ b/src/tee.c
@@ -290,7 +290,8 @@ tee_files (int nfiles, char **files)
       if (pipe_check && out_pollable[first_out])
         {
           /* Monitor for input, or errors on first valid output.  */
-          int err = iopoll (STDIN_FILENO, fileno (descriptors[first_out]));
+          int err = iopoll (STDIN_FILENO, fileno (descriptors[first_out]),
+                            true);
 
           /* Close the output if it became a broken pipe.  */
           if (err == IOPOLL_BROKEN_OUTPUT)