base: Fix fdReady() potentially running forever on Windows.
authorNiklas Hamb├╝chen <mail@nh2.me>
Tue, 19 Sep 2017 19:10:00 +0000 (15:10 -0400)
committerBen Gamari <ben@smart-cactus.org>
Tue, 19 Sep 2017 19:58:45 +0000 (15:58 -0400)
This fixes #13497 for Windows -- at least for the `if (isSock)` part; I
haven't investigated the case where it's not a socket yet.

Solved by copying the new current-time based waiting logic from the
non-Windows implementation above.

Reviewers: bgamari, austin, hvr

Reviewed By: bgamari

Subscribers: rwbarton, thomie

Differential Revision: https://phabricator.haskell.org/D3954

libraries/base/cbits/inputReady.c

index dbfdb28..14f1c54 100644 (file)
 int
 fdReady(int fd, int write, int msecs, int isSock)
 {
-
-#if !defined(_WIN32)
-    struct pollfd fds[1];
-
     // if we need to track the time then record the end time in case we are
     // interrupted.
     Time endTime = 0;
@@ -32,6 +28,9 @@ fdReady(int fd, int write, int msecs, int isSock)
         endTime = getProcessElapsedTime() + MSToTime(msecs);
     }
 
+#if !defined(_WIN32)
+    struct pollfd fds[1];
+
     fds[0].fd = fd;
     fds[0].events = write ? POLLOUT : POLLIN;
     fds[0].revents = 0;
@@ -59,7 +58,8 @@ fdReady(int fd, int write, int msecs, int isSock)
     if (isSock) {
         int maxfd, ready;
         fd_set rfd, wfd;
-        struct timeval tv;
+        struct timeval remaining_tv;
+
         if ((fd >= (int)FD_SETSIZE) || (fd < 0)) {
             fprintf(stderr, "fdReady: fd is too big");
             abort();
@@ -76,12 +76,22 @@ fdReady(int fd, int write, int msecs, int isSock)
          * (maxfd-1)
          */
         maxfd = fd + 1;
-        tv.tv_sec  = msecs / 1000;
-        tv.tv_usec = (msecs % 1000) * 1000;
 
-        while ((ready = select(maxfd, &rfd, &wfd, NULL, &tv)) < 0 ) {
-            if (errno != EINTR ) {
-                return -1;
+        Time remaining = MSToTime(msecs);
+        remaining_tv.tv_sec  = TimeToMS(remaining) / 1000;
+        remaining_tv.tv_usec = TimeToUS(remaining) % 1000000;
+
+        while ((ready = select(maxfd, &rfd, &wfd, NULL, &remaining_tv)) < 0 ) {
+            if (errno == EINTR) {
+                if (msecs > 0) {
+                    Time now = getProcessElapsedTime();
+                    if (now >= endTime) return 0;
+                    remaining = endTime - now;
+                    remaining_tv.tv_sec  = TimeToMS(remaining) / 1000;
+                    remaining_tv.tv_usec = TimeToUS(remaining) % 1000000;
+                }
+            } else {
+                return (-1);
             }
         }