| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtCore module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qcore_unix_p.h" |
| |
| #ifdef Q_OS_RTEMS |
| #include <rtems/rtems_bsdnet_internal.h> |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| #define QT_POLL_READ_MASK (POLLIN | POLLRDNORM) |
| #define QT_POLL_WRITE_MASK (POLLOUT | POLLWRNORM | POLLWRBAND) |
| #define QT_POLL_EXCEPT_MASK (POLLPRI | POLLRDBAND) |
| #define QT_POLL_ERROR_MASK (POLLERR | POLLNVAL) |
| #define QT_POLL_EVENTS_MASK (QT_POLL_READ_MASK | QT_POLL_WRITE_MASK | QT_POLL_EXCEPT_MASK) |
| |
| static inline int qt_poll_prepare(struct pollfd *fds, nfds_t nfds, |
| fd_set *read_fds, fd_set *write_fds, fd_set *except_fds) |
| { |
| int max_fd = -1; |
| |
| FD_ZERO(read_fds); |
| FD_ZERO(write_fds); |
| FD_ZERO(except_fds); |
| |
| for (nfds_t i = 0; i < nfds; i++) { |
| if (fds[i].fd >= FD_SETSIZE) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| if ((fds[i].fd < 0) || (fds[i].revents & QT_POLL_ERROR_MASK)) |
| continue; |
| |
| if (fds[i].events & QT_POLL_READ_MASK) |
| FD_SET(fds[i].fd, read_fds); |
| |
| if (fds[i].events & QT_POLL_WRITE_MASK) |
| FD_SET(fds[i].fd, write_fds); |
| |
| if (fds[i].events & QT_POLL_EXCEPT_MASK) |
| FD_SET(fds[i].fd, except_fds); |
| |
| if (fds[i].events & QT_POLL_EVENTS_MASK) |
| max_fd = qMax(max_fd, fds[i].fd); |
| } |
| |
| return max_fd + 1; |
| } |
| |
| static inline void qt_poll_examine_ready_read(struct pollfd &pfd) |
| { |
| int res; |
| char data; |
| |
| EINTR_LOOP(res, ::recv(pfd.fd, &data, sizeof(data), MSG_PEEK)); |
| const int error = (res < 0) ? errno : 0; |
| |
| if (res == 0) { |
| pfd.revents |= POLLHUP; |
| } else if (res > 0 || error == ENOTSOCK || error == ENOTCONN) { |
| pfd.revents |= QT_POLL_READ_MASK & pfd.events; |
| } else { |
| switch (error) { |
| case ESHUTDOWN: |
| case ECONNRESET: |
| case ECONNABORTED: |
| case ENETRESET: |
| pfd.revents |= POLLHUP; |
| break; |
| default: |
| pfd.revents |= POLLERR; |
| break; |
| } |
| } |
| } |
| |
| static inline int qt_poll_sweep(struct pollfd *fds, nfds_t nfds, |
| fd_set *read_fds, fd_set *write_fds, fd_set *except_fds) |
| { |
| int result = 0; |
| |
| for (nfds_t i = 0; i < nfds; i++) { |
| if (fds[i].fd < 0) |
| continue; |
| |
| if (FD_ISSET(fds[i].fd, read_fds)) |
| qt_poll_examine_ready_read(fds[i]); |
| |
| if (FD_ISSET(fds[i].fd, write_fds)) |
| fds[i].revents |= QT_POLL_WRITE_MASK & fds[i].events; |
| |
| if (FD_ISSET(fds[i].fd, except_fds)) |
| fds[i].revents |= QT_POLL_EXCEPT_MASK & fds[i].events; |
| |
| if (fds[i].revents != 0) |
| result++; |
| } |
| |
| return result; |
| } |
| |
| static inline bool qt_poll_is_bad_fd(int fd) |
| { |
| #ifdef Q_OS_RTEMS |
| if (!rtems_bsdnet_fdToSocket(fd)) |
| return true; |
| #endif |
| |
| int ret; |
| EINTR_LOOP(ret, fcntl(fd, F_GETFD)); |
| return (ret == -1 && errno == EBADF); |
| } |
| |
| static inline int qt_poll_mark_bad_fds(struct pollfd *fds, const nfds_t nfds) |
| { |
| int n_marked = 0; |
| |
| for (nfds_t i = 0; i < nfds; i++) { |
| if (fds[i].fd < 0) |
| continue; |
| |
| if (fds[i].revents & QT_POLL_ERROR_MASK) |
| continue; |
| |
| if (qt_poll_is_bad_fd(fds[i].fd)) { |
| fds[i].revents |= POLLNVAL; |
| n_marked++; |
| } |
| } |
| |
| return n_marked; |
| } |
| |
| int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts) |
| { |
| if (!fds && nfds) { |
| errno = EFAULT; |
| return -1; |
| } |
| |
| fd_set read_fds, write_fds, except_fds; |
| struct timeval tv, *ptv = 0; |
| |
| if (timeout_ts) { |
| tv = timespecToTimeval(*timeout_ts); |
| ptv = &tv; |
| } |
| |
| int n_bad_fds = 0; |
| |
| for (nfds_t i = 0; i < nfds; i++) { |
| fds[i].revents = 0; |
| |
| if (fds[i].fd < 0) |
| continue; |
| |
| if (fds[i].events & QT_POLL_EVENTS_MASK) |
| continue; |
| |
| if (qt_poll_is_bad_fd(fds[i].fd)) { |
| // Mark bad file descriptors that have no event flags set |
| // here, as we won't be passing them to select below and therefore |
| // need to do the check ourselves |
| fds[i].revents = POLLNVAL; |
| n_bad_fds++; |
| } |
| } |
| |
| forever { |
| const int max_fd = qt_poll_prepare(fds, nfds, &read_fds, &write_fds, &except_fds); |
| |
| if (max_fd < 0) |
| return max_fd; |
| |
| if (n_bad_fds > 0) { |
| tv.tv_sec = 0; |
| tv.tv_usec = 0; |
| ptv = &tv; |
| } |
| |
| const int ret = ::select(max_fd, &read_fds, &write_fds, &except_fds, ptv); |
| |
| if (ret == 0) |
| return n_bad_fds; |
| |
| if (ret > 0) |
| return qt_poll_sweep(fds, nfds, &read_fds, &write_fds, &except_fds); |
| |
| if (errno != EBADF) |
| return -1; |
| |
| // We have at least one bad file descriptor that we waited on, find out which and try again |
| n_bad_fds += qt_poll_mark_bad_fds(fds, nfds); |
| } |
| } |
| |
| QT_END_NAMESPACE |