diff options
author | Brad King <brad.king@kitware.com> | 2014-10-08 20:30:41 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2014-10-29 20:43:21 (GMT) |
commit | 80c881f7938969dab169f4fc4fbf8bc3d4355fc2 (patch) | |
tree | 77abe643bf8fcb388056480350b99b666f543965 /Utilities/cmcurl/lib/select.c | |
parent | ff67d2b57946a29e18d1947efe71f26ece9e1747 (diff) | |
parent | 3fe5d9bff98b4716e219516c30d71462495324f4 (diff) | |
download | CMake-80c881f7938969dab169f4fc4fbf8bc3d4355fc2.zip CMake-80c881f7938969dab169f4fc4fbf8bc3d4355fc2.tar.gz CMake-80c881f7938969dab169f4fc4fbf8bc3d4355fc2.tar.bz2 |
Merge branch 'curl-upstream' into update-curl
Resolve all conflicts in favor of the upstream side.
We can re-apply specific changes later.
Diffstat (limited to 'Utilities/cmcurl/lib/select.c')
-rw-r--r-- | Utilities/cmcurl/lib/select.c | 567 |
1 files changed, 413 insertions, 154 deletions
diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index 82f9dc2..bb9b8b0 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c @@ -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 @@ -18,33 +18,24 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * - * $Id$ ***************************************************************************/ -#include "setup.h" +#include "curl_setup.h" -#include <errno.h> - -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif -#ifndef HAVE_SELECT -#error "We can't compile without select() support!" +#if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE) +#error "We can't compile without select() or poll() support." #endif -#if defined(__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 @@ -53,244 +44,512 @@ #include "urldata.h" #include "connect.h" #include "select.h" +#include "warnless.h" -#if defined(USE_WINSOCK) || defined(TPF) -#define VERIFY_SOCK(x) /* sockets are not in range [0..FD_SETSIZE] */ -#else -#define VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE)) -#define VERIFY_SOCK(x) do { \ - if(!VALID_SOCK(x)) { \ - errno = EINVAL; \ - return -1; \ - } \ -} while(0) +/* Convenience local macros */ + +#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv) + +int Curl_ack_eintr = 0; +#define error_not_EINTR (Curl_ack_eintr || error != EINTR) + +/* + * Internal function used for waiting a specific amount of ms + * in Curl_socket_ready() and Curl_poll() when no file descriptor + * is provided to wait on, just being used to delay execution. + * WinSock select() and poll() timeout mechanisms need a valid + * socket descriptor in a not null file descriptor set to work. + * 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 dependent, neither factor is a citical issue + * for the intended use of this function in the library. + * + * Return values: + * -1 = system call error, invalid timeout value, or interrupted + * 0 = specified timeout has elapsed + */ +int Curl_wait_ms(int timeout_ms) +{ +#if !defined(MSDOS) && !defined(USE_WINSOCK) +#ifndef HAVE_POLL_FINE + struct timeval pending_tv; +#endif + struct timeval initial_tv; + int pending_ms; + int error; #endif + int r = 0; + + if(!timeout_ms) + return 0; + if(timeout_ms < 0) { + SET_SOCKERRNO(EINVAL); + return -1; + } +#if defined(MSDOS) + delay(timeout_ms); +#elif defined(USE_WINSOCK) + Sleep(timeout_ms); +#else + pending_ms = timeout_ms; + initial_tv = curlx_tvnow(); + do { +#if defined(HAVE_POLL_FINE) + r = poll(NULL, 0, pending_ms); +#else + pending_tv.tv_sec = pending_ms / 1000; + pending_tv.tv_usec = (pending_ms % 1000) * 1000; + r = select(0, NULL, NULL, NULL, &pending_tv); +#endif /* HAVE_POLL_FINE */ + 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) + r = -1; + return r; +} /* - * This is an internal function used for waiting for read or write - * events on single file descriptors. It attempts to replace select() - * in order to avoid limits with 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. * * Return values: - * -1 = system call error + * -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_select(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 */ { -#if (defined(HAVE_POLL) && !defined(_POLL_EMUL_H_)) || defined(CURL_HAVE_WSAPOLL) - struct pollfd pfd[2]; +#ifdef HAVE_POLL_FINE + struct pollfd pfd[3]; int num; +#else + struct timeval pending_tv; + struct timeval *ptimeout; + fd_set fds_read; + fd_set fds_write; + fd_set fds_err; + curl_socket_t maxfd; +#endif + struct timeval initial_tv = {0,0}; + int pending_ms = 0; + int error; int r; int ret; + 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; + } + + /* 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++; } -#if defined(HAVE_POLL) && !defined(_POLL_EMUL_H_) do { - r = poll(pfd, num, timeout_ms); - } while((r == -1) && (errno == EINTR)); -#else - r = WSAPoll(pfd, num, timeout_ms); -#endif + if(timeout_ms < 0) + pending_ms = -1; + else if(!timeout_ms) + pending_ms = 0; + r = poll(pfd, num, pending_ms); + 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(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 & POLLOUT) - ret |= CSELECT_OUT; - if (pfd[num].revents & (POLLERR|POLLHUP)) - ret |= CSELECT_ERR; + 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; -#else - struct timeval timeout; - fd_set fds_read; - fd_set fds_write; - fd_set fds_err; - curl_socket_t maxfd; - int r; - int ret; - timeout.tv_sec = timeout_ms / 1000; - timeout.tv_usec = (timeout_ms % 1000) * 1000; - - if((readfd == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) { - /* According to POSIX we should pass in NULL pointers if we don't want to - wait for anything in particular but just use the timeout function. - Windows however returns immediately if done so. I copied the MSDOS - delay() use from src/main.c that already had this work-around. */ -#ifdef WIN32 - Sleep(timeout_ms); -#elif defined(__MSDOS__) - delay(timeout_ms); -#else - select(0, NULL, NULL, NULL, &timeout); -#endif - return 0; - } +#else /* HAVE_POLL_FINE */ FD_ZERO(&fds_err); 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 { - r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout); - } while((r == -1) && (Curl_sockerrno() == EINTR)); + if(timeout_ms > 0) { + pending_tv.tv_sec = pending_ms / 1000; + pending_tv.tv_usec = (pending_ms % 1000) * 1000; + } + 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; -#endif + +#endif /* HAVE_POLL_FINE */ + } /* * This is a wrapper around poll(). If poll() does not exist, then * select() is used instead. An error is returned if select() is - * being used and a file descriptor too large for FD_SETSIZE. + * 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. * * Return values: * -1 = system call error or fd >= FD_SETSIZE * 0 = timeout - * 1 = number of structures with non zero revent fields + * N = number of structures with non zero revent fields */ int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms) { - int r; -#if defined(HAVE_POLL) && !defined(_POLL_EMUL_H_) - do { - r = poll(ufds, nfds, timeout_ms); - } while((r == -1) && (errno == EINTR)); -#elif defined(CURL_HAVE_WSAPOLL) - r = WSAPoll(ufds, nfds, timeout_ms); -#else - struct timeval timeout; +#ifndef HAVE_POLL_FINE + struct timeval pending_tv; struct timeval *ptimeout; fd_set fds_read; fd_set fds_write; fd_set fds_err; curl_socket_t maxfd; +#endif + struct timeval initial_tv = {0,0}; + bool fds_none = TRUE; unsigned int i; + int pending_ms = 0; + int error; + int r; + + if(ufds) { + for(i = 0; i < nfds; i++) { + if(ufds[i].fd != CURL_SOCKET_BAD) { + fds_none = FALSE; + break; + } + } + } + if(fds_none) { + r = Curl_wait_ms(timeout_ms); + return r; + } + + /* 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) + pending_ms = -1; + else if(!timeout_ms) + pending_ms = 0; + r = poll(ufds, nfds, pending_ms); + 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_read); FD_ZERO(&fds_write); FD_ZERO(&fds_err); maxfd = (curl_socket_t)-1; - for (i = 0; i < nfds; i++) { - if (ufds[i].fd == CURL_SOCKET_BAD) + for(i = 0; i < nfds; i++) { + ufds[i].revents = 0; + if(ufds[i].fd == CURL_SOCKET_BAD) continue; -#ifndef USE_WINSOCK /* winsock sockets are not in range [0..FD_SETSIZE] */ - if (ufds[i].fd >= FD_SETSIZE) { - errno = EINVAL; - return -1; + VERIFY_SOCK(ufds[i].fd); + if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI| + POLLRDNORM|POLLWRNORM|POLLRDBAND)) { + if(ufds[i].fd > maxfd) + maxfd = ufds[i].fd; + if(ufds[i].events & (POLLRDNORM|POLLIN)) + FD_SET(ufds[i].fd, &fds_read); + if(ufds[i].events & (POLLWRNORM|POLLOUT)) + FD_SET(ufds[i].fd, &fds_write); + if(ufds[i].events & (POLLRDBAND|POLLPRI)) + FD_SET(ufds[i].fd, &fds_err); } -#endif - if (ufds[i].fd > maxfd) - maxfd = ufds[i].fd; - if (ufds[i].events & POLLIN) - FD_SET(ufds[i].fd, &fds_read); - if (ufds[i].events & POLLOUT) - FD_SET(ufds[i].fd, &fds_write); - if (ufds[i].events & POLLERR) - FD_SET(ufds[i].fd, &fds_err); } - if (timeout_ms < 0) { - ptimeout = NULL; /* wait forever */ - } else { - timeout.tv_sec = timeout_ms / 1000; - timeout.tv_usec = (timeout_ms % 1000) * 1000; - ptimeout = &timeout; +#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 { - r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout); - } while((r == -1) && (Curl_sockerrno() == EINTR)); + if(timeout_ms > 0) { + pending_tv.tv_sec = pending_ms / 1000; + pending_tv.tv_usec = (pending_ms % 1000) * 1000; + } + 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++; } -#endif + +#endif /* HAVE_POLL_FINE */ + return r; } |