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. --- lib/select.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 195 insertions(+), 2 deletions(-) (limited to 'lib/select.c') 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. -- cgit v1.2.3