Base code merged to SPIN 2.4
[platform/upstream/curl.git] / lib / select.c
index 2e25ade..1c6481c 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
- * $Id$
  ***************************************************************************/
 
-#include "setup.h"
-
-#include <errno.h>
+#include "curl_setup.h"
 
 #ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
 #endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
 
 #if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE)
 #error "We can't compile without select() or poll() support."
 #endif
 
-#ifdef __BEOS__
+#if defined(__BEOS__) && !defined(__HAIKU__)
 /* BeOS has FD_SET defined in socket.h */
 #include <socket.h>
 #endif
 
-#ifdef __MSDOS__
+#ifdef MSDOS
 #include <dos.h>  /* delay() */
 #endif
 
+#ifdef __VXWORKS__
+#include <strings.h>  /* bzero() in FD_SET */
+#endif
+
 #include <curl/curl.h>
 
 #include "urldata.h"
 #include "connect.h"
 #include "select.h"
-
-#ifdef USE_WINSOCK
-#  undef  EBADF
-#  define EBADF  WSAEBADF
-#  undef  EINTR
-#  define EINTR  WSAEINTR
-#  undef  EINVAL
-#  define EINVAL WSAEINVAL
-#endif
-
-/* Winsock and TPF sockets are not in range [0..FD_SETSIZE-1] */
-
-#if defined(USE_WINSOCK) || defined(TPF)
-#define VERIFY_SOCK(x) do { } while (0)
-#define VERIFY_NFDS(x) do { } while (0)
-#else
-#define VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE))
-#define VERIFY_SOCK(x) do { \
-  if(!VALID_SOCK(x)) { \
-    SET_SOCKERRNO(EINVAL); \
-    return -1; \
-  } \
-} while(0)
-#define VALID_NFDS(n) (((n) >= 0) && ((n) <= FD_SETSIZE))
-#define VERIFY_NFDS(x) do { \
-  if(!VALID_NFDS(x)) { \
-    SET_SOCKERRNO(EINVAL); \
-    return -1; \
-  } \
-} while(0)
-#endif
+#include "warnless.h"
 
 /* Convenience local macros */
 
 #define elapsed_ms  (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
 
-#ifdef CURL_ACKNOWLEDGE_EINTR
-#define error_not_EINTR  (error != EINTR)
-#else
-#define error_not_EINTR  (1)
-#endif
-
-#define SMALL_POLLNFDS  0X20
+int Curl_ack_eintr = 0;
+#define error_not_EINTR (Curl_ack_eintr || error != EINTR)
 
 /*
  * Internal function used for waiting a specific amount of ms
  * Waiting indefinitely with this function is not allowed, a
  * zero or negative timeout value will return immediately.
  * Timeout resolution, accuracy, as well as maximum supported
- * value is system dependant, neither factor is a citical issue
+ * value is system dependent, neither factor is a citical issue
  * for the intended use of this function in the library.
- * On non-DOS and non-Winsock platforms, when compiled with
- * CURL_ACKNOWLEDGE_EINTR defined, EINTR condition is honored
- * and function might exit early without awaiting full timeout,
- * otherwise EINTR will be ignored and full timeout will elapse.
  *
  * Return values:
  *   -1 = system call error, invalid timeout value, or interrupted
  *    0 = specified timeout has elapsed
  */
-static int wait_ms(int timeout_ms)
+int Curl_wait_ms(int timeout_ms)
 {
-#if !defined(__MSDOS__) && !defined(USE_WINSOCK)
+#if !defined(MSDOS) && !defined(USE_WINSOCK)
 #ifndef HAVE_POLL_FINE
   struct timeval pending_tv;
 #endif
@@ -126,13 +85,13 @@ static int wait_ms(int timeout_ms)
 #endif
   int r = 0;
 
-  if (!timeout_ms)
+  if(!timeout_ms)
     return 0;
-  if (timeout_ms < 0) {
+  if(timeout_ms < 0) {
     SET_SOCKERRNO(EINVAL);
     return -1;
   }
-#if defined(__MSDOS__)
+#if defined(MSDOS)
   delay(timeout_ms);
 #elif defined(USE_WINSOCK)
   Sleep(timeout_ms);
@@ -147,37 +106,50 @@ static int wait_ms(int timeout_ms)
     pending_tv.tv_usec = (pending_ms % 1000) * 1000;
     r = select(0, NULL, NULL, NULL, &pending_tv);
 #endif /* HAVE_POLL_FINE */
-  } while ((r == -1) && (error = SOCKERRNO) &&
-           (error != EINVAL) && error_not_EINTR &&
-           ((pending_ms = timeout_ms - elapsed_ms) > 0));
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    pending_ms = timeout_ms - elapsed_ms;
+    if(pending_ms <= 0) {
+      r = 0;  /* Simulate a "call timed out" case */
+      break;
+    }
+  } while(r == -1);
 #endif /* USE_WINSOCK */
-  if (r)
+  if(r)
     r = -1;
   return r;
 }
 
 /*
- * This is an internal function used for waiting for read or write
- * events on a pair of file descriptors.  It uses poll() when a fine
- * poll() is available, in order to avoid limits with FD_SETSIZE,
- * otherwise select() is used.  An error is returned if select() is
- * being used and a file descriptor is too large for FD_SETSIZE.
+ * Wait for read or write events on a set of file descriptors. It uses poll()
+ * when a fine poll() is available, in order to avoid limits with FD_SETSIZE,
+ * otherwise select() is used.  An error is returned if select() is being used
+ * and a file descriptor is too large for FD_SETSIZE.
+ *
  * A negative timeout value makes this function wait indefinitely,
  * unles no valid file descriptor is given, when this happens the
  * negative timeout is ignored and the function times out immediately.
- * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition
- * is honored and function might exit early without awaiting timeout,
- * otherwise EINTR will be ignored.
  *
  * Return values:
  *   -1 = system call error or fd >= FD_SETSIZE
  *    0 = timeout
- *    CSELECT_IN | CSELECT_OUT | CSELECT_ERR
+ *    [bitmask] = action as described below
+ *
+ * CURL_CSELECT_IN - first socket is readable
+ * CURL_CSELECT_IN2 - second socket is readable
+ * CURL_CSELECT_OUT - write socket is writable
+ * CURL_CSELECT_ERR - an error condition occurred
  */
-int Curl_socket_ready(curl_socket_t readfd, curl_socket_t writefd, int timeout_ms)
+int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
+                      curl_socket_t readfd1,
+                      curl_socket_t writefd, /* socket to write to */
+                      long timeout_ms)       /* milliseconds to wait */
 {
 #ifdef HAVE_POLL_FINE
-  struct pollfd pfd[2];
+  struct pollfd pfd[3];
   int num;
 #else
   struct timeval pending_tv;
@@ -187,70 +159,97 @@ int Curl_socket_ready(curl_socket_t readfd, curl_socket_t writefd, int timeout_m
   fd_set fds_err;
   curl_socket_t maxfd;
 #endif
-  struct timeval initial_tv;
-  int pending_ms;
+  struct timeval initial_tv = {0,0};
+  int pending_ms = 0;
   int error;
   int r;
   int ret;
 
-  if((readfd == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) {
-    r = wait_ms(timeout_ms);
+  if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) &&
+     (writefd == CURL_SOCKET_BAD)) {
+    /* no sockets, just wait */
+    r = Curl_wait_ms((int)timeout_ms);
     return r;
   }
 
-  pending_ms = timeout_ms;
-  initial_tv = curlx_tvnow();
+  /* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed
+     time in this function does not need to be measured. This happens
+     when function is called with a zero timeout or a negative timeout
+     value indicating a blocking call should be performed. */
+
+  if(timeout_ms > 0) {
+    pending_ms = (int)timeout_ms;
+    initial_tv = curlx_tvnow();
+  }
 
 #ifdef HAVE_POLL_FINE
 
   num = 0;
-  if (readfd != CURL_SOCKET_BAD) {
-    pfd[num].fd = readfd;
-    pfd[num].events = POLLIN;
+  if(readfd0 != CURL_SOCKET_BAD) {
+    pfd[num].fd = readfd0;
+    pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
+    pfd[num].revents = 0;
+    num++;
+  }
+  if(readfd1 != CURL_SOCKET_BAD) {
+    pfd[num].fd = readfd1;
+    pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
     pfd[num].revents = 0;
     num++;
   }
-  if (writefd != CURL_SOCKET_BAD) {
+  if(writefd != CURL_SOCKET_BAD) {
     pfd[num].fd = writefd;
-    pfd[num].events = POLLOUT;
+    pfd[num].events = POLLWRNORM|POLLOUT;
     pfd[num].revents = 0;
     num++;
   }
 
   do {
-    if (timeout_ms < 0)
+    if(timeout_ms < 0)
       pending_ms = -1;
+    else if(!timeout_ms)
+      pending_ms = 0;
     r = poll(pfd, num, pending_ms);
-  } while ((r == -1) && (error = SOCKERRNO) &&
-           (error != EINVAL) && error_not_EINTR &&
-           ((timeout_ms < 0) || ((pending_ms = timeout_ms - elapsed_ms) > 0)));
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    if(timeout_ms > 0) {
+      pending_ms = (int)(timeout_ms - elapsed_ms);
+      if(pending_ms <= 0) {
+        r = 0;  /* Simulate a "call timed out" case */
+        break;
+      }
+    }
+  } while(r == -1);
 
-  if (r < 0)
+  if(r < 0)
     return -1;
-  if (r == 0)
+  if(r == 0)
     return 0;
 
   ret = 0;
   num = 0;
-  if (readfd != CURL_SOCKET_BAD) {
-    if (pfd[num].revents & (POLLIN|POLLHUP))
-      ret |= CSELECT_IN;
-    if (pfd[num].revents & POLLERR) {
-#ifdef __CYGWIN__
-      /* Cygwin 1.5.21 needs this hack to pass test 160 */
-      if (ERRNO == EINPROGRESS)
-        ret |= CSELECT_IN;
-      else
-#endif
-        ret |= CSELECT_ERR;
-    }
+  if(readfd0 != CURL_SOCKET_BAD) {
+    if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
+      ret |= CURL_CSELECT_IN;
+    if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
+      ret |= CURL_CSELECT_ERR;
     num++;
   }
-  if (writefd != CURL_SOCKET_BAD) {
-    if (pfd[num].revents & POLLOUT)
-      ret |= CSELECT_OUT;
-    if (pfd[num].revents & (POLLERR|POLLHUP))
-      ret |= CSELECT_ERR;
+  if(readfd1 != CURL_SOCKET_BAD) {
+    if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
+      ret |= CURL_CSELECT_IN2;
+    if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
+      ret |= CURL_CSELECT_ERR;
+    num++;
+  }
+  if(writefd != CURL_SOCKET_BAD) {
+    if(pfd[num].revents & (POLLWRNORM|POLLOUT))
+      ret |= CURL_CSELECT_OUT;
+    if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
+      ret |= CURL_CSELECT_ERR;
   }
 
   return ret;
@@ -261,51 +260,108 @@ int Curl_socket_ready(curl_socket_t readfd, curl_socket_t writefd, int timeout_m
   maxfd = (curl_socket_t)-1;
 
   FD_ZERO(&fds_read);
-  if (readfd != CURL_SOCKET_BAD) {
-    VERIFY_SOCK(readfd);
-    FD_SET(readfd, &fds_read);
-    FD_SET(readfd, &fds_err);
-    maxfd = readfd;
+  if(readfd0 != CURL_SOCKET_BAD) {
+    VERIFY_SOCK(readfd0);
+    FD_SET(readfd0, &fds_read);
+    FD_SET(readfd0, &fds_err);
+    maxfd = readfd0;
+  }
+  if(readfd1 != CURL_SOCKET_BAD) {
+    VERIFY_SOCK(readfd1);
+    FD_SET(readfd1, &fds_read);
+    FD_SET(readfd1, &fds_err);
+    if(readfd1 > maxfd)
+      maxfd = readfd1;
   }
 
   FD_ZERO(&fds_write);
-  if (writefd != CURL_SOCKET_BAD) {
+  if(writefd != CURL_SOCKET_BAD) {
     VERIFY_SOCK(writefd);
     FD_SET(writefd, &fds_write);
     FD_SET(writefd, &fds_err);
-    if (writefd > maxfd)
+    if(writefd > maxfd)
       maxfd = writefd;
   }
 
   ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
 
   do {
-    if (ptimeout) {
+    if(timeout_ms > 0) {
       pending_tv.tv_sec = pending_ms / 1000;
       pending_tv.tv_usec = (pending_ms % 1000) * 1000;
     }
-    r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
-  } while ((r == -1) && (error = SOCKERRNO) &&
-           (error != EINVAL) && (error != EBADF) && error_not_EINTR &&
-           ((timeout_ms < 0) || ((pending_ms = timeout_ms - elapsed_ms) > 0)));
+    else if(!timeout_ms) {
+      pending_tv.tv_sec = 0;
+      pending_tv.tv_usec = 0;
+    }
 
-  if (r < 0)
+    /* WinSock select() must not be called with an fd_set that contains zero
+       fd flags, or it will return WSAEINVAL.  But, it also can't be called
+       with no fd_sets at all!  From the documentation:
+
+         Any two of the parameters, readfds, writefds, or exceptfds, can be
+         given as null. At least one must be non-null, and any non-null
+         descriptor set must contain at least one handle to a socket.
+
+       We know that we have at least one bit set in at least two fd_sets in
+       this case, but we may have no bits set in either fds_read or fd_write,
+       so check for that and handle it.  Luckily, with WinSock, we can _also_
+       ask how many bits are set on an fd_set.
+
+       It is unclear why WinSock doesn't just handle this for us instead of
+       calling this an error.
+
+       Note also that WinSock ignores the first argument, so we don't worry
+       about the fact that maxfd is computed incorrectly with WinSock (since
+       curl_socket_t is unsigned in such cases and thus -1 is the largest
+       value).
+    */
+    r = select((int)maxfd + 1,
+#ifndef USE_WINSOCK
+               &fds_read,
+               &fds_write,
+#else
+               fds_read.fd_count ? &fds_read : NULL,
+               fds_write.fd_count ? &fds_write : NULL,
+#endif
+               &fds_err, ptimeout);
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    if(timeout_ms > 0) {
+      pending_ms = timeout_ms - elapsed_ms;
+      if(pending_ms <= 0) {
+        r = 0;  /* Simulate a "call timed out" case */
+        break;
+      }
+    }
+  } while(r == -1);
+
+  if(r < 0)
     return -1;
-  if (r == 0)
+  if(r == 0)
     return 0;
 
   ret = 0;
-  if (readfd != CURL_SOCKET_BAD) {
-    if (FD_ISSET(readfd, &fds_read))
-      ret |= CSELECT_IN;
-    if (FD_ISSET(readfd, &fds_err))
-      ret |= CSELECT_ERR;
+  if(readfd0 != CURL_SOCKET_BAD) {
+    if(FD_ISSET(readfd0, &fds_read))
+      ret |= CURL_CSELECT_IN;
+    if(FD_ISSET(readfd0, &fds_err))
+      ret |= CURL_CSELECT_ERR;
+  }
+  if(readfd1 != CURL_SOCKET_BAD) {
+    if(FD_ISSET(readfd1, &fds_read))
+      ret |= CURL_CSELECT_IN2;
+    if(FD_ISSET(readfd1, &fds_err))
+      ret |= CURL_CSELECT_ERR;
   }
-  if (writefd != CURL_SOCKET_BAD) {
-    if (FD_ISSET(writefd, &fds_write))
-      ret |= CSELECT_OUT;
-    if (FD_ISSET(writefd, &fds_err))
-      ret |= CSELECT_ERR;
+  if(writefd != CURL_SOCKET_BAD) {
+    if(FD_ISSET(writefd, &fds_write))
+      ret |= CURL_CSELECT_OUT;
+    if(FD_ISSET(writefd, &fds_err))
+      ret |= CURL_CSELECT_ERR;
   }
 
   return ret;
@@ -321,9 +377,6 @@ int Curl_socket_ready(curl_socket_t readfd, curl_socket_t writefd, int timeout_m
  * A negative timeout value makes this function wait indefinitely,
  * unles no valid file descriptor is given, when this happens the
  * negative timeout is ignored and the function times out immediately.
- * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition
- * is honored and function might exit early without awaiting timeout,
- * otherwise EINTR will be ignored.
  *
  * Return values:
  *   -1 = system call error or fd >= FD_SETSIZE
@@ -340,38 +393,71 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
   fd_set fds_err;
   curl_socket_t maxfd;
 #endif
-  struct timeval initial_tv;
+  struct timeval initial_tv = {0,0};
   bool fds_none = TRUE;
   unsigned int i;
-  int pending_ms;
+  int pending_ms = 0;
   int error;
   int r;
 
-  if (ufds) {
-    for (i = 0; i < nfds; i++) {
-      if (ufds[i].fd != CURL_SOCKET_BAD) {
+  if(ufds) {
+    for(i = 0; i < nfds; i++) {
+      if(ufds[i].fd != CURL_SOCKET_BAD) {
         fds_none = FALSE;
         break;
       }
     }
   }
-  if (fds_none) {
-    r = wait_ms(timeout_ms);
+  if(fds_none) {
+    r = Curl_wait_ms(timeout_ms);
     return r;
   }
 
-  pending_ms = timeout_ms;
-  initial_tv = curlx_tvnow();
+  /* Avoid initial timestamp, avoid curlx_tvnow() call, when elapsed
+     time in this function does not need to be measured. This happens
+     when function is called with a zero timeout or a negative timeout
+     value indicating a blocking call should be performed. */
+
+  if(timeout_ms > 0) {
+    pending_ms = timeout_ms;
+    initial_tv = curlx_tvnow();
+  }
 
 #ifdef HAVE_POLL_FINE
 
   do {
-    if (timeout_ms < 0)
+    if(timeout_ms < 0)
       pending_ms = -1;
+    else if(!timeout_ms)
+      pending_ms = 0;
     r = poll(ufds, nfds, pending_ms);
-  } while ((r == -1) && (error = SOCKERRNO) &&
-           (error != EINVAL) && error_not_EINTR &&
-           ((timeout_ms < 0) || ((pending_ms = timeout_ms - elapsed_ms) > 0)));
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    if(timeout_ms > 0) {
+      pending_ms = timeout_ms - elapsed_ms;
+      if(pending_ms <= 0) {
+        r = 0;  /* Simulate a "call timed out" case */
+        break;
+      }
+    }
+  } while(r == -1);
+
+  if(r < 0)
+    return -1;
+  if(r == 0)
+    return 0;
+
+  for(i = 0; i < nfds; i++) {
+    if(ufds[i].fd == CURL_SOCKET_BAD)
+      continue;
+    if(ufds[i].revents & POLLHUP)
+      ufds[i].revents |= POLLIN;
+    if(ufds[i].revents & POLLERR)
+      ufds[i].revents |= (POLLIN|POLLOUT);
+  }
 
 #else  /* HAVE_POLL_FINE */
 
@@ -380,52 +466,89 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
   FD_ZERO(&fds_err);
   maxfd = (curl_socket_t)-1;
 
-  for (i = 0; i < nfds; i++) {
+  for(i = 0; i < nfds; i++) {
     ufds[i].revents = 0;
-    if (ufds[i].fd == CURL_SOCKET_BAD)
+    if(ufds[i].fd == CURL_SOCKET_BAD)
       continue;
     VERIFY_SOCK(ufds[i].fd);
-    if (ufds[i].events & (POLLIN|POLLOUT|POLLERR)) {
-      if (ufds[i].fd > maxfd)
+    if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI|
+                          POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
+      if(ufds[i].fd > maxfd)
         maxfd = ufds[i].fd;
-      if (ufds[i].events & POLLIN)
+      if(ufds[i].events & (POLLRDNORM|POLLIN))
         FD_SET(ufds[i].fd, &fds_read);
-      if (ufds[i].events & POLLOUT)
+      if(ufds[i].events & (POLLWRNORM|POLLOUT))
         FD_SET(ufds[i].fd, &fds_write);
-      if (ufds[i].events & POLLERR)
+      if(ufds[i].events & (POLLRDBAND|POLLPRI))
         FD_SET(ufds[i].fd, &fds_err);
     }
   }
 
+#ifdef USE_WINSOCK
+  /* WinSock select() can't handle zero events.  See the comment about this in
+     Curl_check_socket(). */
+  if(fds_read.fd_count == 0 && fds_write.fd_count == 0
+     && fds_err.fd_count == 0) {
+    r = Curl_wait_ms(timeout_ms);
+    return r;
+  }
+#endif
+
   ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
 
   do {
-    if (ptimeout) {
+    if(timeout_ms > 0) {
       pending_tv.tv_sec = pending_ms / 1000;
       pending_tv.tv_usec = (pending_ms % 1000) * 1000;
     }
-    r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
-  } while ((r == -1) && (error = SOCKERRNO) &&
-           (error != EINVAL) && (error != EBADF) && error_not_EINTR &&
-           ((timeout_ms < 0) || ((pending_ms = timeout_ms - elapsed_ms) > 0)));
+    else if(!timeout_ms) {
+      pending_tv.tv_sec = 0;
+      pending_tv.tv_usec = 0;
+    }
+    r = select((int)maxfd + 1,
+#ifndef USE_WINSOCK
+               &fds_read, &fds_write, &fds_err,
+#else
+               /* WinSock select() can't handle fd_sets with zero bits set, so
+                  don't give it such arguments.  See the comment about this in
+                  Curl_check_socket().
+               */
+               fds_read.fd_count ? &fds_read : NULL,
+               fds_write.fd_count ? &fds_write : NULL,
+               fds_err.fd_count ? &fds_err : NULL,
+#endif
+               ptimeout);
+    if(r != -1)
+      break;
+    error = SOCKERRNO;
+    if(error && error_not_EINTR)
+      break;
+    if(timeout_ms > 0) {
+      pending_ms = timeout_ms - elapsed_ms;
+      if(pending_ms <= 0) {
+        r = 0;  /* Simulate a "call timed out" case */
+        break;
+      }
+    }
+  } while(r == -1);
 
-  if (r < 0)
+  if(r < 0)
     return -1;
-  if (r == 0)
+  if(r == 0)
     return 0;
 
   r = 0;
-  for (i = 0; i < nfds; i++) {
+  for(i = 0; i < nfds; i++) {
     ufds[i].revents = 0;
-    if (ufds[i].fd == CURL_SOCKET_BAD)
+    if(ufds[i].fd == CURL_SOCKET_BAD)
       continue;
-    if (FD_ISSET(ufds[i].fd, &fds_read))
+    if(FD_ISSET(ufds[i].fd, &fds_read))
       ufds[i].revents |= POLLIN;
-    if (FD_ISSET(ufds[i].fd, &fds_write))
+    if(FD_ISSET(ufds[i].fd, &fds_write))
       ufds[i].revents |= POLLOUT;
-    if (FD_ISSET(ufds[i].fd, &fds_err))
-      ufds[i].revents |= POLLERR;
-    if (ufds[i].revents != 0)
+    if(FD_ISSET(ufds[i].fd, &fds_err))
+      ufds[i].revents |= POLLPRI;
+    if(ufds[i].revents != 0)
       r++;
   }
 
@@ -434,189 +557,6 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
   return r;
 }
 
-/*
- * This is a wrapper around select().  It uses poll() when a fine
- * poll() is available, in order to avoid limits with FD_SETSIZE,
- * otherwise select() is used.  An error is returned if select() is
- * being used and a the number of file descriptors is larger than
- * FD_SETSIZE.  A NULL timeout pointer makes this function wait
- * indefinitely, unles no valid file descriptor is given, when this
- * happens the NULL timeout is ignored and the function times out
- * immediately.  When compiled with CURL_ACKNOWLEDGE_EINTR defined,
- * EINTR condition is honored and function might exit early without
- * awaiting timeout, otherwise EINTR will be ignored.
- *
- * Return values:
- *   -1 = system call error or nfds > FD_SETSIZE
- *    0 = timeout
- *    N = number of file descriptors kept in file descriptor sets.
- */
-int Curl_select(int nfds,
-                fd_set *fds_read, fd_set *fds_write, fd_set *fds_excep,
-                struct timeval *timeout)
-{
-  struct timeval initial_tv;
-  int timeout_ms;
-  int pending_ms;
-  int error;
-  int r;
-#ifdef HAVE_POLL_FINE
-  struct pollfd small_fds[SMALL_POLLNFDS];
-  struct pollfd *poll_fds;
-  int ix;
-  int fd;
-  int poll_nfds = 0;
-#else
-  struct timeval pending_tv;
-  struct timeval *ptimeout;
-#endif
-  int ret = 0;
-
-  if ((nfds < 0) ||
-     ((nfds > 0) && (!fds_read && !fds_write && !fds_excep))) {
-    SET_SOCKERRNO(EINVAL);
-    return -1;
-  }
-
-  if (timeout) {
-    if ((timeout->tv_sec < 0) ||
-        (timeout->tv_usec < 0) ||
-        (timeout->tv_usec >= 1000000)) {
-      SET_SOCKERRNO(EINVAL);
-      return -1;
-    }
-    timeout_ms = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000);
-  }
-  else {
-    timeout_ms = -1;
-  }
-
-  if ((!nfds) || (!fds_read && !fds_write && !fds_excep)) {
-    r = wait_ms(timeout_ms);
-    return r;
-  }
-
-  pending_ms = timeout_ms;
-  initial_tv = curlx_tvnow();
-
-#ifdef HAVE_POLL_FINE
-
-  if (fds_read || fds_write || fds_excep) {
-    fd = nfds;
-    while (fd--) {
-      if ((fds_read && (0 != FD_ISSET(fd, fds_read))) ||
-          (fds_write && (0 != FD_ISSET(fd, fds_write))) ||
-          (fds_excep && (0 != FD_ISSET(fd, fds_excep))))
-        poll_nfds++;
-    }
-  }
-
-  if (!poll_nfds)
-    poll_fds = NULL;
-  else if (poll_nfds <= SMALL_POLLNFDS)
-    poll_fds = small_fds;
-  else {
-    poll_fds = calloc((size_t)poll_nfds, sizeof(struct pollfd));
-    if (!poll_fds) {
-      SET_SOCKERRNO(ENOBUFS);
-      return -1;
-    }
-  }
-
-  if (poll_fds) {
-    ix = 0;
-    fd = nfds;
-    while (fd--) {
-      poll_fds[ix].events = 0;
-      if (fds_read && (0 != FD_ISSET(fd, fds_read)))
-        poll_fds[ix].events |= (POLLRDNORM|POLLIN);
-      if (fds_write && (0 != FD_ISSET(fd, fds_write)))
-        poll_fds[ix].events |= (POLLWRNORM|POLLOUT);
-      if (fds_excep && (0 != FD_ISSET(fd, fds_excep)))
-        poll_fds[ix].events |= (POLLRDBAND|POLLPRI);
-      if (poll_fds[ix].events) {
-        poll_fds[ix].fd = fd;
-        poll_fds[ix].revents = 0;
-        ix++;
-      }
-    }
-  }
-
-  do {
-    if (timeout_ms < 0)
-      pending_ms = -1;
-    r = poll(poll_fds, poll_nfds, pending_ms);
-  } while ((r == -1) && (error = SOCKERRNO) &&
-           (error != EINVAL) && error_not_EINTR &&
-           ((timeout_ms < 0) || ((pending_ms = timeout_ms - elapsed_ms) > 0)));
-
-  if (r < 0)
-    ret = -1;
-
-  if (r > 0) {
-    ix = poll_nfds;
-    while (ix--) {
-      if (poll_fds[ix].revents & POLLNVAL) {
-        SET_SOCKERRNO(EBADF);
-        ret = -1;
-        break;
-      }
-    }
-  }
-
-  if (!ret) {
-    ix = poll_nfds;
-    while (ix--) {
-      if (fds_read && (0 != FD_ISSET(poll_fds[ix].fd, fds_read))) {
-        if (0 == (poll_fds[ix].revents & (POLLRDNORM|POLLERR|POLLHUP|POLLIN)))
-          FD_CLR(poll_fds[ix].fd, fds_read);
-        else
-          ret++;
-      }
-      if (fds_write && (0 != FD_ISSET(poll_fds[ix].fd, fds_write))) {
-        if (0 == (poll_fds[ix].revents & (POLLWRNORM|POLLERR|POLLHUP|POLLOUT)))
-          FD_CLR(poll_fds[ix].fd, fds_write);
-        else
-          ret++;
-      }
-      if (fds_excep && (0 != FD_ISSET(poll_fds[ix].fd, fds_excep))) {
-        if (0 == (poll_fds[ix].revents & (POLLRDBAND|POLLERR|POLLHUP|POLLPRI)))
-          FD_CLR(poll_fds[ix].fd, fds_excep);
-        else
-          ret++;
-      }
-    }
-  }
-
-  if (poll_fds && (poll_nfds > SMALL_POLLNFDS))
-    free(poll_fds);
-
-#else  /* HAVE_POLL_FINE */
-
-  VERIFY_NFDS(nfds);
-
-  ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
-
-  do {
-    if (ptimeout) {
-      pending_tv.tv_sec = pending_ms / 1000;
-      pending_tv.tv_usec = (pending_ms % 1000) * 1000;
-    }
-    r = select(nfds, fds_read, fds_write, fds_excep, ptimeout);
-  } while ((r == -1) && (error = SOCKERRNO) &&
-           (error != EINVAL) && (error != EBADF) && error_not_EINTR &&
-           ((timeout_ms < 0) || ((pending_ms = timeout_ms - elapsed_ms) > 0)));
-
-  if (r < 0)
-    ret = -1;
-  else
-    ret = r;
-
-#endif  /* HAVE_POLL_FINE */
-
-  return ret;
-}
-
 #ifdef TPF
 /*
  * This is a replacement for select() on the TPF platform.