Annotation of embedaddon/curl/lib/select.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
! 9: *
! 10: * This software is licensed as described in the file COPYING, which
! 11: * you should have received as part of this distribution. The terms
! 12: * are also available at https://curl.haxx.se/docs/copyright.html.
! 13: *
! 14: * You may opt to use, copy, modify, merge, publish, distribute and/or sell
! 15: * copies of the Software, and permit persons to whom the Software is
! 16: * furnished to do so, under the terms of the COPYING file.
! 17: *
! 18: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
! 19: * KIND, either express or implied.
! 20: *
! 21: ***************************************************************************/
! 22:
! 23: #include "curl_setup.h"
! 24:
! 25: #ifdef HAVE_SYS_SELECT_H
! 26: #include <sys/select.h>
! 27: #elif defined(HAVE_UNISTD_H)
! 28: #include <unistd.h>
! 29: #endif
! 30:
! 31: #if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE)
! 32: #error "We can't compile without select() or poll() support."
! 33: #endif
! 34:
! 35: #if defined(__BEOS__) && !defined(__HAIKU__)
! 36: /* BeOS has FD_SET defined in socket.h */
! 37: #include <socket.h>
! 38: #endif
! 39:
! 40: #ifdef MSDOS
! 41: #include <dos.h> /* delay() */
! 42: #endif
! 43:
! 44: #ifdef __VXWORKS__
! 45: #include <strings.h> /* bzero() in FD_SET */
! 46: #endif
! 47:
! 48: #include <curl/curl.h>
! 49:
! 50: #include "urldata.h"
! 51: #include "connect.h"
! 52: #include "select.h"
! 53: #include "warnless.h"
! 54:
! 55: /* Convenience local macros */
! 56: #define ELAPSED_MS() (int)Curl_timediff(Curl_now(), initial_tv)
! 57:
! 58: /*
! 59: * Internal function used for waiting a specific amount of ms
! 60: * in Curl_socket_check() and Curl_poll() when no file descriptor
! 61: * is provided to wait on, just being used to delay execution.
! 62: * WinSock select() and poll() timeout mechanisms need a valid
! 63: * socket descriptor in a not null file descriptor set to work.
! 64: * Waiting indefinitely with this function is not allowed, a
! 65: * zero or negative timeout value will return immediately.
! 66: * Timeout resolution, accuracy, as well as maximum supported
! 67: * value is system dependent, neither factor is a citical issue
! 68: * for the intended use of this function in the library.
! 69: *
! 70: * Return values:
! 71: * -1 = system call error, invalid timeout value, or interrupted
! 72: * 0 = specified timeout has elapsed
! 73: */
! 74: int Curl_wait_ms(int timeout_ms)
! 75: {
! 76: int r = 0;
! 77:
! 78: if(!timeout_ms)
! 79: return 0;
! 80: if(timeout_ms < 0) {
! 81: SET_SOCKERRNO(EINVAL);
! 82: return -1;
! 83: }
! 84: #if defined(MSDOS)
! 85: delay(timeout_ms);
! 86: #elif defined(USE_WINSOCK)
! 87: Sleep(timeout_ms);
! 88: #else
! 89: #if defined(HAVE_POLL_FINE)
! 90: r = poll(NULL, 0, timeout_ms);
! 91: #else
! 92: {
! 93: struct timeval pending_tv;
! 94: pending_tv.tv_sec = timeout_ms / 1000;
! 95: pending_tv.tv_usec = (timeout_ms % 1000) * 1000;
! 96: r = select(0, NULL, NULL, NULL, &pending_tv);
! 97: }
! 98: #endif /* HAVE_POLL_FINE */
! 99: #endif /* USE_WINSOCK */
! 100: if(r)
! 101: r = -1;
! 102: return r;
! 103: }
! 104:
! 105: /*
! 106: * This is a wrapper around select() to aid in Windows compatibility.
! 107: * A negative timeout value makes this function wait indefinitely,
! 108: * unless no valid file descriptor is given, when this happens the
! 109: * negative timeout is ignored and the function times out immediately.
! 110: *
! 111: * Return values:
! 112: * -1 = system call error or fd >= FD_SETSIZE
! 113: * 0 = timeout
! 114: * N = number of signalled file descriptors
! 115: */
! 116: int Curl_select(curl_socket_t maxfd,
! 117: fd_set *fds_read,
! 118: fd_set *fds_write,
! 119: fd_set *fds_err,
! 120: time_t timeout_ms) /* milliseconds to wait */
! 121: {
! 122: struct timeval pending_tv;
! 123: struct timeval *ptimeout;
! 124: int pending_ms;
! 125: int r;
! 126:
! 127: #if SIZEOF_TIME_T != SIZEOF_INT
! 128: /* wrap-around precaution */
! 129: if(timeout_ms >= INT_MAX)
! 130: timeout_ms = INT_MAX;
! 131: #endif
! 132:
! 133: #ifdef USE_WINSOCK
! 134: /* WinSock select() can't handle zero events. See the comment below. */
! 135: if((!fds_read || fds_read->fd_count == 0) &&
! 136: (!fds_write || fds_write->fd_count == 0) &&
! 137: (!fds_err || fds_err->fd_count == 0)) {
! 138: r = Curl_wait_ms((int)timeout_ms);
! 139: return r;
! 140: }
! 141: #endif
! 142:
! 143: ptimeout = &pending_tv;
! 144:
! 145: if(timeout_ms < 0) {
! 146: ptimeout = NULL;
! 147: }
! 148: else if(timeout_ms > 0) {
! 149: pending_ms = (int)timeout_ms;
! 150: pending_tv.tv_sec = pending_ms / 1000;
! 151: pending_tv.tv_usec = (pending_ms % 1000) * 1000;
! 152: }
! 153: else if(!timeout_ms) {
! 154: pending_tv.tv_sec = 0;
! 155: pending_tv.tv_usec = 0;
! 156: }
! 157:
! 158: #ifdef USE_WINSOCK
! 159: /* WinSock select() must not be called with an fd_set that contains zero
! 160: fd flags, or it will return WSAEINVAL. But, it also can't be called
! 161: with no fd_sets at all! From the documentation:
! 162:
! 163: Any two of the parameters, readfds, writefds, or exceptfds, can be
! 164: given as null. At least one must be non-null, and any non-null
! 165: descriptor set must contain at least one handle to a socket.
! 166:
! 167: It is unclear why WinSock doesn't just handle this for us instead of
! 168: calling this an error.
! 169: */
! 170: r = select((int)maxfd + 1,
! 171: fds_read && fds_read->fd_count ? fds_read : NULL,
! 172: fds_write && fds_write->fd_count ? fds_write : NULL,
! 173: fds_err && fds_err->fd_count ? fds_err : NULL, ptimeout);
! 174: #else
! 175: r = select((int)maxfd + 1, fds_read, fds_write, fds_err, ptimeout);
! 176: #endif
! 177:
! 178: return r;
! 179: }
! 180:
! 181: /*
! 182: * Wait for read or write events on a set of file descriptors. It uses poll()
! 183: * when a fine poll() is available, in order to avoid limits with FD_SETSIZE,
! 184: * otherwise select() is used. An error is returned if select() is being used
! 185: * and a file descriptor is too large for FD_SETSIZE.
! 186: *
! 187: * A negative timeout value makes this function wait indefinitely,
! 188: * unless no valid file descriptor is given, when this happens the
! 189: * negative timeout is ignored and the function times out immediately.
! 190: *
! 191: * Return values:
! 192: * -1 = system call error or fd >= FD_SETSIZE
! 193: * 0 = timeout
! 194: * [bitmask] = action as described below
! 195: *
! 196: * CURL_CSELECT_IN - first socket is readable
! 197: * CURL_CSELECT_IN2 - second socket is readable
! 198: * CURL_CSELECT_OUT - write socket is writable
! 199: * CURL_CSELECT_ERR - an error condition occurred
! 200: */
! 201: int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
! 202: curl_socket_t readfd1,
! 203: curl_socket_t writefd, /* socket to write to */
! 204: timediff_t timeout_ms) /* milliseconds to wait */
! 205: {
! 206: #ifdef HAVE_POLL_FINE
! 207: struct pollfd pfd[3];
! 208: int pending_ms;
! 209: int num;
! 210: #else
! 211: fd_set fds_read;
! 212: fd_set fds_write;
! 213: fd_set fds_err;
! 214: curl_socket_t maxfd;
! 215: #endif
! 216: int r;
! 217: int ret;
! 218:
! 219: #if SIZEOF_TIME_T != SIZEOF_INT
! 220: /* wrap-around precaution */
! 221: if(timeout_ms >= INT_MAX)
! 222: timeout_ms = INT_MAX;
! 223: #endif
! 224:
! 225: if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) &&
! 226: (writefd == CURL_SOCKET_BAD)) {
! 227: /* no sockets, just wait */
! 228: r = Curl_wait_ms((int)timeout_ms);
! 229: return r;
! 230: }
! 231:
! 232: /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
! 233: time in this function does not need to be measured. This happens
! 234: when function is called with a zero timeout or a negative timeout
! 235: value indicating a blocking call should be performed. */
! 236:
! 237: #ifdef HAVE_POLL_FINE
! 238:
! 239: num = 0;
! 240: if(readfd0 != CURL_SOCKET_BAD) {
! 241: pfd[num].fd = readfd0;
! 242: pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
! 243: pfd[num].revents = 0;
! 244: num++;
! 245: }
! 246: if(readfd1 != CURL_SOCKET_BAD) {
! 247: pfd[num].fd = readfd1;
! 248: pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
! 249: pfd[num].revents = 0;
! 250: num++;
! 251: }
! 252: if(writefd != CURL_SOCKET_BAD) {
! 253: pfd[num].fd = writefd;
! 254: pfd[num].events = POLLWRNORM|POLLOUT;
! 255: pfd[num].revents = 0;
! 256: num++;
! 257: }
! 258:
! 259: if(timeout_ms > 0)
! 260: pending_ms = (int)timeout_ms;
! 261: else if(timeout_ms < 0)
! 262: pending_ms = -1;
! 263: else
! 264: pending_ms = 0;
! 265: r = poll(pfd, num, pending_ms);
! 266:
! 267: if(r < 0)
! 268: return -1;
! 269: if(r == 0)
! 270: return 0;
! 271:
! 272: ret = 0;
! 273: num = 0;
! 274: if(readfd0 != CURL_SOCKET_BAD) {
! 275: if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
! 276: ret |= CURL_CSELECT_IN;
! 277: if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
! 278: ret |= CURL_CSELECT_ERR;
! 279: num++;
! 280: }
! 281: if(readfd1 != CURL_SOCKET_BAD) {
! 282: if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
! 283: ret |= CURL_CSELECT_IN2;
! 284: if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
! 285: ret |= CURL_CSELECT_ERR;
! 286: num++;
! 287: }
! 288: if(writefd != CURL_SOCKET_BAD) {
! 289: if(pfd[num].revents & (POLLWRNORM|POLLOUT))
! 290: ret |= CURL_CSELECT_OUT;
! 291: if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
! 292: ret |= CURL_CSELECT_ERR;
! 293: }
! 294:
! 295: return ret;
! 296:
! 297: #else /* HAVE_POLL_FINE */
! 298:
! 299: FD_ZERO(&fds_err);
! 300: maxfd = (curl_socket_t)-1;
! 301:
! 302: FD_ZERO(&fds_read);
! 303: if(readfd0 != CURL_SOCKET_BAD) {
! 304: VERIFY_SOCK(readfd0);
! 305: FD_SET(readfd0, &fds_read);
! 306: FD_SET(readfd0, &fds_err);
! 307: maxfd = readfd0;
! 308: }
! 309: if(readfd1 != CURL_SOCKET_BAD) {
! 310: VERIFY_SOCK(readfd1);
! 311: FD_SET(readfd1, &fds_read);
! 312: FD_SET(readfd1, &fds_err);
! 313: if(readfd1 > maxfd)
! 314: maxfd = readfd1;
! 315: }
! 316:
! 317: FD_ZERO(&fds_write);
! 318: if(writefd != CURL_SOCKET_BAD) {
! 319: VERIFY_SOCK(writefd);
! 320: FD_SET(writefd, &fds_write);
! 321: FD_SET(writefd, &fds_err);
! 322: if(writefd > maxfd)
! 323: maxfd = writefd;
! 324: }
! 325:
! 326: /* We know that we have at least one bit set in at least two fd_sets in
! 327: this case, but we may have no bits set in either fds_read or fd_write,
! 328: so check for that and handle it. Luckily, with WinSock, we can _also_
! 329: ask how many bits are set on an fd_set.
! 330:
! 331: Note also that WinSock ignores the first argument, so we don't worry
! 332: about the fact that maxfd is computed incorrectly with WinSock (since
! 333: curl_socket_t is unsigned in such cases and thus -1 is the largest
! 334: value).
! 335: */
! 336: r = Curl_select(maxfd, &fds_read, &fds_write, &fds_err, (time_t)timeout_ms);
! 337:
! 338: if(r < 0)
! 339: return -1;
! 340: if(r == 0)
! 341: return 0;
! 342:
! 343: ret = 0;
! 344: if(readfd0 != CURL_SOCKET_BAD) {
! 345: if(FD_ISSET(readfd0, &fds_read))
! 346: ret |= CURL_CSELECT_IN;
! 347: if(FD_ISSET(readfd0, &fds_err))
! 348: ret |= CURL_CSELECT_ERR;
! 349: }
! 350: if(readfd1 != CURL_SOCKET_BAD) {
! 351: if(FD_ISSET(readfd1, &fds_read))
! 352: ret |= CURL_CSELECT_IN2;
! 353: if(FD_ISSET(readfd1, &fds_err))
! 354: ret |= CURL_CSELECT_ERR;
! 355: }
! 356: if(writefd != CURL_SOCKET_BAD) {
! 357: if(FD_ISSET(writefd, &fds_write))
! 358: ret |= CURL_CSELECT_OUT;
! 359: if(FD_ISSET(writefd, &fds_err))
! 360: ret |= CURL_CSELECT_ERR;
! 361: }
! 362:
! 363: return ret;
! 364:
! 365: #endif /* HAVE_POLL_FINE */
! 366:
! 367: }
! 368:
! 369: /*
! 370: * This is a wrapper around poll(). If poll() does not exist, then
! 371: * select() is used instead. An error is returned if select() is
! 372: * being used and a file descriptor is too large for FD_SETSIZE.
! 373: * A negative timeout value makes this function wait indefinitely,
! 374: * unless no valid file descriptor is given, when this happens the
! 375: * negative timeout is ignored and the function times out immediately.
! 376: *
! 377: * Return values:
! 378: * -1 = system call error or fd >= FD_SETSIZE
! 379: * 0 = timeout
! 380: * N = number of structures with non zero revent fields
! 381: */
! 382: int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
! 383: {
! 384: #ifdef HAVE_POLL_FINE
! 385: int pending_ms;
! 386: #else
! 387: fd_set fds_read;
! 388: fd_set fds_write;
! 389: fd_set fds_err;
! 390: curl_socket_t maxfd;
! 391: #endif
! 392: bool fds_none = TRUE;
! 393: unsigned int i;
! 394: int r;
! 395:
! 396: if(ufds) {
! 397: for(i = 0; i < nfds; i++) {
! 398: if(ufds[i].fd != CURL_SOCKET_BAD) {
! 399: fds_none = FALSE;
! 400: break;
! 401: }
! 402: }
! 403: }
! 404: if(fds_none) {
! 405: r = Curl_wait_ms(timeout_ms);
! 406: return r;
! 407: }
! 408:
! 409: /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
! 410: time in this function does not need to be measured. This happens
! 411: when function is called with a zero timeout or a negative timeout
! 412: value indicating a blocking call should be performed. */
! 413:
! 414: #ifdef HAVE_POLL_FINE
! 415:
! 416: if(timeout_ms > 0)
! 417: pending_ms = timeout_ms;
! 418: else if(timeout_ms < 0)
! 419: pending_ms = -1;
! 420: else
! 421: pending_ms = 0;
! 422: r = poll(ufds, nfds, pending_ms);
! 423:
! 424: if(r < 0)
! 425: return -1;
! 426: if(r == 0)
! 427: return 0;
! 428:
! 429: for(i = 0; i < nfds; i++) {
! 430: if(ufds[i].fd == CURL_SOCKET_BAD)
! 431: continue;
! 432: if(ufds[i].revents & POLLHUP)
! 433: ufds[i].revents |= POLLIN;
! 434: if(ufds[i].revents & POLLERR)
! 435: ufds[i].revents |= (POLLIN|POLLOUT);
! 436: }
! 437:
! 438: #else /* HAVE_POLL_FINE */
! 439:
! 440: FD_ZERO(&fds_read);
! 441: FD_ZERO(&fds_write);
! 442: FD_ZERO(&fds_err);
! 443: maxfd = (curl_socket_t)-1;
! 444:
! 445: for(i = 0; i < nfds; i++) {
! 446: ufds[i].revents = 0;
! 447: if(ufds[i].fd == CURL_SOCKET_BAD)
! 448: continue;
! 449: VERIFY_SOCK(ufds[i].fd);
! 450: if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI|
! 451: POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
! 452: if(ufds[i].fd > maxfd)
! 453: maxfd = ufds[i].fd;
! 454: if(ufds[i].events & (POLLRDNORM|POLLIN))
! 455: FD_SET(ufds[i].fd, &fds_read);
! 456: if(ufds[i].events & (POLLWRNORM|POLLOUT))
! 457: FD_SET(ufds[i].fd, &fds_write);
! 458: if(ufds[i].events & (POLLRDBAND|POLLPRI))
! 459: FD_SET(ufds[i].fd, &fds_err);
! 460: }
! 461: }
! 462:
! 463: r = Curl_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms);
! 464:
! 465: if(r < 0)
! 466: return -1;
! 467: if(r == 0)
! 468: return 0;
! 469:
! 470: r = 0;
! 471: for(i = 0; i < nfds; i++) {
! 472: ufds[i].revents = 0;
! 473: if(ufds[i].fd == CURL_SOCKET_BAD)
! 474: continue;
! 475: if(FD_ISSET(ufds[i].fd, &fds_read))
! 476: ufds[i].revents |= POLLIN;
! 477: if(FD_ISSET(ufds[i].fd, &fds_write))
! 478: ufds[i].revents |= POLLOUT;
! 479: if(FD_ISSET(ufds[i].fd, &fds_err))
! 480: ufds[i].revents |= POLLPRI;
! 481: if(ufds[i].revents != 0)
! 482: r++;
! 483: }
! 484:
! 485: #endif /* HAVE_POLL_FINE */
! 486:
! 487: return r;
! 488: }
! 489:
! 490: #ifdef TPF
! 491: /*
! 492: * This is a replacement for select() on the TPF platform.
! 493: * It is used whenever libcurl calls select().
! 494: * The call below to tpf_process_signals() is required because
! 495: * TPF's select calls are not signal interruptible.
! 496: *
! 497: * Return values are the same as select's.
! 498: */
! 499: int tpf_select_libcurl(int maxfds, fd_set *reads, fd_set *writes,
! 500: fd_set *excepts, struct timeval *tv)
! 501: {
! 502: int rc;
! 503:
! 504: rc = tpf_select_bsd(maxfds, reads, writes, excepts, tv);
! 505: tpf_process_signals();
! 506: return rc;
! 507: }
! 508: #endif /* TPF */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>