]> Savannah Git Hosting - gnulib.git/commitdiff
select: fix waiting on anonymous pipes on MS-Windows
authorEli Zaretskii <eliz@gnu.org>
Tue, 10 Jun 2014 21:19:13 +0000 (22:19 +0100)
committerPádraig Brady <P@draigBrady.com>
Tue, 10 Jun 2014 22:49:53 +0000 (23:49 +0100)
The existing select() implementation for MS-Windows returned
immediately with a zero value when it is called to wait for input
from an anonymous pipe (e.g., a pipe created by a call to 'pipe' or
'pipe2'), as discussed at:

  http://lists.gnu.org/archive/html/bug-gnulib/2011-06/msg00008.html

This was noticed while running Guile's test suite on MS-Windows.
Guile uses 'select', among other places, in its implementation
of 'sleep' and 'usleep' primitives.  It calls 'select'
with a file descriptor of a signal delivery pipe, which is written to
(by another thread) when Guile is interrupted by a signal.  But due to
the above-mentioned problem, these two functions never sleep, and
instead return immediately.

* lib/select.c (rpl_select): Fall back to polling when select()
indicates there is nothing to check, while due to the timeout not
expiring, activity is indicated on one of the handles.
Also clear the TIMEOUT argument if the timer does expire.

ChangeLog
lib/select.c

index 105befe73471ea0b9fe594825ed14a91347852d4..d2a42feb6ceb3e41c4329e82a7781b59f87d0b05 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2014-06-10  Eli Zaretskii  <eliz@gnu.org>
+
+       select: fix waiting on anonymous pipes on MS-Windows
+       * lib/select.c (rpl_select): Fall back to polling when select()
+       indicates there is nothing to check, while due to the timeout not
+       expiring, activity is indicated on one of the handles.
+       Also clear the TIMEOUT argument if the timer does expire.
+
 2014-06-10  Eli Zaretskii  <eliz@gnu.org>
 
        times: fix to return non constant value on MS-Windows
index 1ca0d352f79244f25e90dfbe421e4cf69e86b31a..a12f37288a88e39ad3833001e979984e343425c6 100644 (file)
@@ -252,6 +252,7 @@ rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
   DWORD ret, wait_timeout, nhandles, nsock, nbuffer;
   MSG msg;
   int i, fd, rc;
+  clock_t tend;
 
   if (nfds > FD_SETSIZE)
     nfds = FD_SETSIZE;
@@ -388,6 +389,10 @@ rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
   /* Place a sentinel at the end of the array.  */
   handle_array[nhandles] = NULL;
 
+  /* When will the waiting period expire?  */
+  if (wait_timeout != INFINITE)
+    tend = clock () + wait_timeout;
+
 restart:
   if (wait_timeout == 0 || nsock == 0)
     rc = 0;
@@ -408,6 +413,16 @@ restart:
         wait_timeout = 0;
     }
 
+  /* How much is left to wait?  */
+  if (wait_timeout != INFINITE)
+    {
+      clock_t tnow = clock ();
+      if (tend >= tnow)
+        wait_timeout = tend - tnow;
+      else
+        wait_timeout = 0;
+    }
+
   for (;;)
     {
       ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
@@ -453,7 +468,16 @@ restart:
             }
         }
 
-      if (rc == 0 && wait_timeout == INFINITE)
+      if (rc == 0
+          && (wait_timeout == INFINITE
+              /* If NHANDLES > 1, but no bits are set, it means we've
+                 been told incorrectly that some handle was signaled.
+                 This happens with anonymous pipes, which always cause
+                 MsgWaitForMultipleObjects to exit immediately, but no
+                 data is found ready to be read by windows_poll_handle.
+                 To avoid a total failure (whereby we return zero and
+                 don't wait at all), let's poll in a more busy loop.  */
+              || (wait_timeout != 0 && nhandles > 1)))
         {
           /* Sleep 1 millisecond to avoid busy wait and retry with the
              original fd_sets.  */
@@ -463,6 +487,8 @@ restart:
           SleepEx (1, TRUE);
           goto restart;
         }
+      if (timeout && wait_timeout == 0 && rc == 0)
+        timeout->tv_sec = timeout->tv_usec = 0;
     }
 
   /* Now fill in the results.  */