/* poll.cc. Implements poll(2) via usage of select(2) call. Copyright 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. This file is part of Cygwin. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #define __INSIDE_CYGWIN_NET__ #define FD_SETSIZE 16384 // lots of fds #include "winsup.h" #include #include #include #include #define USE_SYS_TYPES_FD_SET #include #include "cygerrno.h" #include "security.h" #include "path.h" #include "fhandler.h" #include "dtable.h" #include "cygheap.h" #include "sigproc.h" extern "C" int poll (struct pollfd *fds, unsigned int nfds, int timeout) { int max_fd = 0; fd_set *read_fds, *write_fds, *except_fds; struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 }; for (unsigned int i = 0; i < nfds; ++i) if (fds[i].fd > max_fd) max_fd = fds[i].fd; size_t fds_size = howmany (max_fd + 1, NFDBITS) * sizeof (fd_mask); read_fds = (fd_set *) alloca (fds_size); write_fds = (fd_set *) alloca (fds_size); except_fds = (fd_set *) alloca (fds_size); if (!read_fds || !write_fds || !except_fds) { set_errno (EINVAL); /* According to SUSv3. */ return -1; } memset (read_fds, 0, fds_size); memset (write_fds, 0, fds_size); memset (except_fds, 0, fds_size); int invalid_fds = 0; for (unsigned int i = 0; i < nfds; ++i) { fds[i].revents = 0; if (!cygheap->fdtab.not_open (fds[i].fd)) { if (fds[i].events & POLLIN) FD_SET(fds[i].fd, read_fds); if (fds[i].events & POLLOUT) FD_SET(fds[i].fd, write_fds); /* On sockets, except_fds is needed to catch failed connects. */ if ((fds[i].events & POLLPRI) || cygheap->fdtab[fds[i].fd]->is_socket ()) FD_SET(fds[i].fd, except_fds); } else if (fds[i].fd >= 0) { ++invalid_fds; fds[i].revents = POLLNVAL; } } if (invalid_fds) return invalid_fds; int ret = cygwin_select (max_fd + 1, read_fds, write_fds, except_fds, timeout < 0 ? NULL : &tv); if (ret > 0) for (unsigned int i = 0; i < nfds; ++i) { if (fds[i].fd >= 0) { if (cygheap->fdtab.not_open (fds[i].fd)) fds[i].revents = POLLHUP; else { fhandler_socket *sock; if (FD_ISSET(fds[i].fd, read_fds)) { char peek[1]; sock = cygheap->fdtab[fds[i].fd]->is_socket (); if (!sock) fds[i].revents |= POLLIN; else if (sock->listener ()) { fds[i].revents |= POLLIN; } else { /* The following action can change errno. We have to reset it to it's old value. */ int old_errno = get_errno (); switch (sock->recvfrom (peek, sizeof (peek), MSG_PEEK, NULL, NULL)) { case -1: fds[i].revents |= POLLERR; break; case 0: /* Closed on the read side... */ /* ...or shutdown(SHUT_WR) on the write side. We set revents to POLLHUP until 1.5.18, but this is semantically borderline. */ fds[i].revents |= POLLIN; break; default: fds[i].revents |= POLLIN; break; } set_errno (old_errno); } } /* Handle failed connect. */ if (FD_ISSET(fds[i].fd, write_fds) && FD_ISSET(fds[i].fd, except_fds) && (sock = cygheap->fdtab[fds[i].fd]->is_socket ()) && sock->connect_state () == connect_failed) fds[i].revents |= (POLLIN | POLLERR); else { if (FD_ISSET(fds[i].fd, write_fds)) fds[i].revents |= POLLOUT; if (FD_ISSET(fds[i].fd, except_fds)) fds[i].revents |= POLLPRI; } } } } return ret; }