From eed47311f8afb47b5a68b512c58c7031b91ff180 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 27 Mar 2007 18:15:26 +0000 Subject: New Internal wrapper function Curl_select() around select (2), it uses poll() when a fine poll() is available, so now libcurl can be built without select() support at all if a fine poll() is available. --- CHANGES | 4 ++ RELEASE-NOTES | 1 + lib/easy.c | 7 +- lib/hostares.c | 7 +- lib/select.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- lib/select.h | 16 +++++ 6 files changed, 222 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 0e3d52d1e..d4d9f5837 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,10 @@ Yang Tse (27 March 2007) - Internal function Curl_select() renamed to Curl_socket_ready() + New Internal wrapper function Curl_select() around select (2), it + uses poll() when a fine poll() is available, so now libcurl can be + built without select() support at all if a fine poll() is available. + Daniel S (25 March 2007) - Daniel Johnson fixed multi code to traverse the easy handle list properly. A left-over bug from the February 21 fix. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 759e49b18..0661b8d98 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -24,6 +24,7 @@ This release includes the following changes: o added experimental CURL_ACKNOWLEDGE_EINTR symbol definition check o --key and new --pubkey options for SSH public key file logins o --pass now works for a SSH public key file, too + o select (2) support no longer needed to build the library if poll() used This release includes the following bugfixes: diff --git a/lib/easy.c b/lib/easy.c index c16654d65..dc18c7488 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -61,10 +61,6 @@ #include #endif -#ifdef HAVE_SYS_SELECT_H -#include -#endif - #endif /* WIN32 ... */ #include "urldata.h" @@ -79,6 +75,7 @@ #include "memory.h" #include "progress.h" #include "easyif.h" +#include "select.h" #include "sendf.h" /* for failf function prototype */ #include @@ -417,7 +414,7 @@ CURLcode curl_easy_perform(CURL *easy) /* get file descriptors from the transfers */ curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd); - rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); + rc = Curl_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); if(rc == -1) /* select error */ diff --git a/lib/hostares.c b/lib/hostares.c index c9fbcc6de..dae1ca3b7 100644 --- a/lib/hostares.c +++ b/lib/hostares.c @@ -74,6 +74,7 @@ #include "url.h" #include "multiif.h" #include "connect.h" +#include "select.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -144,8 +145,8 @@ CURLcode Curl_is_resolved(struct connectdata *conn, nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds); - (void)select(nfds, &read_fds, &write_fds, NULL, - (struct timeval *)&tv); + (void)Curl_select(nfds, &read_fds, &write_fds, NULL, + (struct timeval *)&tv); /* Call ares_process() unconditonally here, even if we simply timed out above, as otherwise the ares name resolve won't timeout! */ @@ -210,7 +211,7 @@ CURLcode Curl_wait_for_resolv(struct connectdata *conn, /* no file descriptors means we're done waiting */ break; tvp = ares_timeout(data->state.areschannel, &store, &tv); - count = select(nfds, &read_fds, &write_fds, NULL, tvp); + count = Curl_select(nfds, &read_fds, &write_fds, NULL, tvp); if ((count < 0) && (SOCKERRNO != EINVAL)) break; diff --git a/lib/select.c b/lib/select.c index 161a62161..2e25adede 100644 --- a/lib/select.c +++ b/lib/select.c @@ -32,8 +32,8 @@ #include #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 #ifdef __BEOS__ @@ -64,6 +64,7 @@ #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 { \ @@ -72,6 +73,13 @@ 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 /* Convenience local macros */ @@ -84,6 +92,8 @@ #define error_not_EINTR (1) #endif +#define SMALL_POLLNFDS 0X20 + /* * Internal function used for waiting a specific amount of ms * in Curl_socket_ready() and Curl_poll() when no file descriptor @@ -424,6 +434,189 @@ 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. diff --git a/lib/select.h b/lib/select.h index 8c59f155e..5a62a6fd8 100644 --- a/lib/select.h +++ b/lib/select.h @@ -64,6 +64,18 @@ struct pollfd #endif +#ifndef POLLRDNORM +#define POLLRDNORM POLLIN +#endif + +#ifndef POLLWRNORM +#define POLLWRNORM POLLOUT +#endif + +#ifndef POLLRDBAND +#define POLLRDBAND POLLPRI +#endif + #define CSELECT_IN 0x01 #define CSELECT_OUT 0x02 #define CSELECT_ERR 0x04 @@ -72,6 +84,10 @@ int Curl_socket_ready(curl_socket_t readfd, curl_socket_t writefd, int timeout_m int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms); +int Curl_select(int nfds, + fd_set *fds_read, fd_set *fds_write, fd_set *fds_excep, + struct timeval *timeout); + #ifdef TPF int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes, fd_set* excepts, struct timeval* tv); -- cgit v1.2.3