* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * 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
#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);
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;
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;
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;
* 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
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 */
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++;
}
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.