summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.tests/unix/poll/poll.cpp45
-rw-r--r--config.tests/unix/poll/poll.pro2
-rw-r--r--config.tests/unix/pollts/pollts.cpp51
-rw-r--r--config.tests/unix/pollts/pollts.pro2
-rw-r--r--config.tests/unix/ppoll/ppoll.cpp50
-rw-r--r--config.tests/unix/ppoll/ppoll.pro2
-rwxr-xr-xconfigure15
-rw-r--r--src/corelib/kernel/kernel.pri4
-rw-r--r--src/corelib/kernel/qcore_unix.cpp290
-rw-r--r--src/corelib/kernel/qcore_unix_p.h24
-rw-r--r--tests/manual/qt_poll/qt_poll.pro4
-rw-r--r--tests/manual/qt_poll/tst_qt_poll.cpp158
12 files changed, 641 insertions, 6 deletions
diff --git a/config.tests/unix/poll/poll.cpp b/config.tests/unix/poll/poll.cpp
new file mode 100644
index 0000000000..06ae0386b5
--- /dev/null
+++ b/config.tests/unix/poll/poll.cpp
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the config.tests of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <poll.h>
+
+int main()
+{
+ struct pollfd pfd;
+
+ pfd.fd = -1;
+ pfd.events = 0;
+ pfd.revents = 0;
+
+ return ::poll(&pfd, 1, 0);
+}
diff --git a/config.tests/unix/poll/poll.pro b/config.tests/unix/poll/poll.pro
new file mode 100644
index 0000000000..70121b4586
--- /dev/null
+++ b/config.tests/unix/poll/poll.pro
@@ -0,0 +1,2 @@
+SOURCES = poll.cpp
+CONFIG -= qt
diff --git a/config.tests/unix/pollts/pollts.cpp b/config.tests/unix/pollts/pollts.cpp
new file mode 100644
index 0000000000..c2d1940ee0
--- /dev/null
+++ b/config.tests/unix/pollts/pollts.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the config.tests of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <poll.h>
+#include <signal.h>
+#include <time.h>
+
+int main()
+{
+ struct pollfd pfd;
+ struct timespec ts;
+
+ pfd.fd = -1;
+ pfd.events = 0;
+ pfd.revents = 0;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ return ::pollts(&pfd, 1, &ts, nullptr);
+}
diff --git a/config.tests/unix/pollts/pollts.pro b/config.tests/unix/pollts/pollts.pro
new file mode 100644
index 0000000000..5109dc33fd
--- /dev/null
+++ b/config.tests/unix/pollts/pollts.pro
@@ -0,0 +1,2 @@
+SOURCES = pollts.cpp
+CONFIG -= qt
diff --git a/config.tests/unix/ppoll/ppoll.cpp b/config.tests/unix/ppoll/ppoll.cpp
new file mode 100644
index 0000000000..19d680edcf
--- /dev/null
+++ b/config.tests/unix/ppoll/ppoll.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the config.tests of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <signal.h>
+#include <poll.h>
+
+int main()
+{
+ struct pollfd pfd;
+ struct timespec ts;
+
+ pfd.fd = -1;
+ pfd.events = 0;
+ pfd.revents = 0;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ return ::ppoll(&pfd, 1, &ts, nullptr);
+}
diff --git a/config.tests/unix/ppoll/ppoll.pro b/config.tests/unix/ppoll/ppoll.pro
new file mode 100644
index 0000000000..d08a8a0679
--- /dev/null
+++ b/config.tests/unix/ppoll/ppoll.pro
@@ -0,0 +1,2 @@
+SOURCES = ppoll.cpp
+CONFIG -= qt
diff --git a/configure b/configure
index b65519cce9..0412a42e12 100755
--- a/configure
+++ b/configure
@@ -745,6 +745,7 @@ CFG_GETIFADDRS=auto
CFG_INOTIFY=auto
CFG_EVENTFD=auto
CFG_CLOEXEC=no
+CFG_POLL=auto
CFG_RPATH=yes
CFG_FRAMEWORK=auto
CFG_USE_GOLD_LINKER=auto
@@ -6135,6 +6136,16 @@ if compileTest unix/cloexec "cloexec"; then
CFG_CLOEXEC=yes
fi
+if compileTest unix/ppoll "ppoll"; then
+ CFG_POLL="ppoll"
+elif compileTest unix/pollts "pollts"; then
+ CFG_POLL="pollts"
+elif compileTest unix/poll "poll"; then
+ CFG_POLL="poll"
+else
+ CFG_POLL="select"
+fi
+
if [ "$XPLATFORM_MAC" = "yes" ] && [ "$CFG_SECURETRANSPORT" != "no" ] && ([ "$CFG_OPENSSL" = "no" ] || [ "$CFG_OPENSSL" = "auto" ]); then
CFG_SECURETRANSPORT=yes
CFG_OPENSSL=no
@@ -6444,6 +6455,10 @@ fi
if [ "$CFG_CLOEXEC" = "yes" ]; then
QT_CONFIG="$QT_CONFIG threadsafe-cloexec"
fi
+if [ "$CFG_POLL" = "select" ]; then
+ QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_NATIVE_POLL"
+fi
+QT_CONFIG="$QT_CONFIG poll_$CFG_POLL"
if [ "$CFG_LIBJPEG" = "no" ]; then
CFG_JPEG="no"
elif [ "$CFG_LIBJPEG" = "system" ]; then
diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri
index 26a5ee425c..aabe076062 100644
--- a/src/corelib/kernel/kernel.pri
+++ b/src/corelib/kernel/kernel.pri
@@ -143,6 +143,10 @@ unix|integrity {
kernel/qeventdispatcher_unix_p.h \
kernel/qtimerinfo_unix_p.h
+ contains(QT_CONFIG, poll_poll): DEFINES += QT_HAVE_POLL
+ contains(QT_CONFIG, poll_ppoll): DEFINES += QT_HAVE_POLL QT_HAVE_PPOLL
+ contains(QT_CONFIG, poll_pollts): DEFINES += QT_HAVE_POLL QT_HAVE_POLLTS
+
contains(QT_CONFIG, glib) {
SOURCES += \
kernel/qeventdispatcher_glib.cpp
diff --git a/src/corelib/kernel/qcore_unix.cpp b/src/corelib/kernel/qcore_unix.cpp
index bef191c0ad..bb75823bff 100644
--- a/src/corelib/kernel/qcore_unix.cpp
+++ b/src/corelib/kernel/qcore_unix.cpp
@@ -52,6 +52,33 @@
QT_BEGIN_NAMESPACE
+#if !defined(QT_HAVE_PPOLL) && defined(QT_HAVE_POLLTS)
+# define ppoll pollts
+# define QT_HAVE_PPOLL
+#endif
+
+#ifndef _POSIX_POLL
+# if defined(QT_HAVE_PPOLL) || _POSIX_VERSION >= 200809L || _XOPEN_VERSION >= 700
+# define _POSIX_POLL 1
+# elif defined(QT_HAVE_POLL)
+# define _POSIX_POLL 0
+# else
+# define _POSIX_POLL -1
+# endif
+#endif
+
+#if defined(Q_OS_QNX) || _POSIX_POLL <= 0 || defined(QT_BUILD_INTERNAL)
+static inline struct timeval timespecToTimeval(const struct timespec &ts)
+{
+ struct timeval tv;
+
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = ts.tv_nsec / 1000;
+
+ return tv;
+}
+#endif
+
static inline bool time_update(struct timespec *tv, const struct timespec &start,
const struct timespec &timeout)
{
@@ -81,9 +108,7 @@ int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept,
#ifndef Q_OS_QNX
ret = ::pselect(nfds, fdread, fdwrite, fdexcept, &timeout, 0);
#else
- timeval timeoutVal;
- timeoutVal.tv_sec = timeout.tv_sec;
- timeoutVal.tv_usec = timeout.tv_nsec / 1000;
+ timeval timeoutVal = timespecToTimeval(timeout);
ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeoutVal);
#endif
if (ret != -1 || errno != EINTR)
@@ -98,15 +123,268 @@ int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept,
}
}
+static inline struct timespec millisecsToTimespec(const unsigned int ms)
+{
+ struct timespec tv;
+
+ tv.tv_sec = ms / 1000;
+ tv.tv_nsec = (ms % 1000) * 1000 * 1000;
+
+ return tv;
+}
+
int qt_select_msecs(int nfds, fd_set *fdread, fd_set *fdwrite, int timeout)
{
if (timeout < 0)
return qt_safe_select(nfds, fdread, fdwrite, 0, 0);
- struct timespec tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_nsec = (timeout % 1000) * 1000 * 1000;
+ struct timespec tv = millisecsToTimespec(timeout);
return qt_safe_select(nfds, fdread, fdwrite, 0, &tv);
}
+#if _POSIX_POLL <= 0 || defined(QT_BUILD_INTERNAL)
+
+#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)
+{
+ 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;
+}
+
+Q_AUTOTEST_EXPORT 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);
+ }
+}
+
+#endif // _POSIX_POLL <= 0 || defined(QT_BUILD_INTERNAL)
+
+#if !defined(QT_HAVE_PPOLL) && ((_POSIX_POLL > 0) || defined(_SC_POLL))
+static inline int timespecToMillisecs(const struct timespec *ts)
+{
+ return (ts == NULL) ? -1 :
+ (ts->tv_sec * 1000) + (ts->tv_nsec / 1000000);
+}
+#endif
+
+static inline int qt_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
+{
+#if defined(QT_HAVE_PPOLL)
+ return ::ppoll(fds, nfds, timeout_ts, Q_NULLPTR);
+#elif _POSIX_POLL > 0
+ return ::poll(fds, nfds, timespecToMillisecs(timeout_ts));
+#else
+# if defined(_SC_POLL)
+ static const bool have_poll = (sysconf(_SC_POLL) > 0);
+ if (have_poll)
+ return ::poll(fds, nfds, timespecToMillisecs(timeout_ts));
+# endif
+ return qt_poll(fds, nfds, timeout_ts);
+#endif
+}
+
+
+/*!
+ \internal
+
+ Behaves as close to POSIX poll(2) as practical but may be implemented
+ using select(2) where necessary. In that case, returns -1 and sets errno
+ to EINVAL if passed any descriptor greater than or equal to FD_SETSIZE.
+*/
+int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
+{
+ if (!timeout_ts) {
+ // no timeout -> block forever
+ int ret;
+ EINTR_LOOP(ret, qt_ppoll(fds, nfds, Q_NULLPTR));
+ return ret;
+ }
+
+ timespec start = qt_gettime();
+ timespec timeout = *timeout_ts;
+
+ // loop and recalculate the timeout as needed
+ forever {
+ const int ret = qt_ppoll(fds, nfds, &timeout);
+ if (ret != -1 || errno != EINTR)
+ return ret;
+
+ // recalculate the timeout
+ if (!time_update(&timeout, start, *timeout_ts)) {
+ // timeout during update
+ // or clock reset, fake timeout error
+ return 0;
+ }
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h
index d82b2651e7..d5434888d0 100644
--- a/src/corelib/kernel/qcore_unix_p.h
+++ b/src/corelib/kernel/qcore_unix_p.h
@@ -66,6 +66,28 @@
# include <ioLib.h>
#endif
+#ifndef QT_NO_NATIVE_POLL
+# include <poll.h>
+#else
+struct pollfd {
+ int fd;
+ short events, revents;
+};
+
+typedef unsigned long int nfds_t;
+
+# define POLLIN 0x001
+# define POLLPRI 0x002
+# define POLLOUT 0x004
+# define POLLERR 0x008
+# define POLLHUP 0x010
+# define POLLNVAL 0x020
+# define POLLRDNORM 0x040
+# define POLLRDBAND 0x080
+# define POLLWRNORM 0x100
+# define POLLWRBAND 0x200
+#endif
+
struct sockaddr;
#define EINTR_LOOP(var, cmd) \
@@ -303,6 +325,8 @@ static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options)
timespec qt_gettime() Q_DECL_NOTHROW;
void qt_nanosleep(timespec amount);
+Q_CORE_EXPORT int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts);
+
Q_CORE_EXPORT int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept,
const struct timespec *tv);
diff --git a/tests/manual/qt_poll/qt_poll.pro b/tests/manual/qt_poll/qt_poll.pro
new file mode 100644
index 0000000000..e8104c764b
--- /dev/null
+++ b/tests/manual/qt_poll/qt_poll.pro
@@ -0,0 +1,4 @@
+CONFIG += testcase
+TARGET = tst_qt_poll
+QT = core-private network testlib
+SOURCES = tst_qt_poll.cpp
diff --git a/tests/manual/qt_poll/tst_qt_poll.cpp b/tests/manual/qt_poll/tst_qt_poll.cpp
new file mode 100644
index 0000000000..1edc08244e
--- /dev/null
+++ b/tests/manual/qt_poll/tst_qt_poll.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtNetwork>
+
+#include <private/qcore_unix_p.h>
+
+#ifdef QT_BUILD_INTERNAL
+QT_BEGIN_NAMESPACE
+Q_AUTOTEST_EXPORT int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts);
+QT_END_NAMESPACE
+#endif // QT_BUILD_INTERNAL
+
+QT_USE_NAMESPACE
+
+class tst_qt_poll : public QObject
+{
+ Q_OBJECT
+
+#ifdef QT_BUILD_INTERNAL
+private slots:
+ void pollout();
+ void pollin();
+ void pollnval();
+ void pollprihup();
+#endif // QT_BUILD_INTERNAL
+};
+
+#ifdef QT_BUILD_INTERNAL
+void tst_qt_poll::pollout()
+{
+ int fds[2];
+ QCOMPARE(pipe(fds), 0);
+
+ struct pollfd pfd = { fds[1], POLLOUT, 0 };
+ const int nready = qt_poll(&pfd, 1, NULL);
+
+ QCOMPARE(nready, 1);
+ QCOMPARE(pfd.revents, short(POLLOUT));
+
+ qt_safe_close(fds[0]);
+ qt_safe_close(fds[1]);
+}
+
+void tst_qt_poll::pollin()
+{
+ int fds[2];
+ QCOMPARE(pipe(fds), 0);
+
+ const char data = 'Q';
+ QCOMPARE(qt_safe_write(fds[1], &data, 1), 1);
+
+ struct pollfd pfd = { fds[0], POLLIN, 0 };
+ const int nready = qt_poll(&pfd, 1, NULL);
+
+ QCOMPARE(nready, 1);
+ QCOMPARE(pfd.revents, short(POLLIN));
+
+ qt_safe_close(fds[0]);
+ qt_safe_close(fds[1]);
+}
+
+void tst_qt_poll::pollnval()
+{
+ struct pollfd pfd = { 42, POLLOUT, 0 };
+
+ int nready = qt_poll(&pfd, 1, NULL);
+ QCOMPARE(nready, 1);
+ QCOMPARE(pfd.revents, short(POLLNVAL));
+
+ pfd.events = 0;
+ pfd.revents = 0;
+
+ nready = qt_poll(&pfd, 1, NULL);
+ QCOMPARE(nready, 1);
+ QCOMPARE(pfd.revents, short(POLLNVAL));
+}
+
+void tst_qt_poll::pollprihup()
+{
+ QTcpServer server;
+ QTcpSocket client_socket;
+
+ QVERIFY(server.listen(QHostAddress::LocalHost));
+
+ const quint16 server_port = server.serverPort();
+ client_socket.connectToHost(server.serverAddress(), server_port);
+
+ QVERIFY(client_socket.waitForConnected());
+ QVERIFY(server.waitForNewConnection());
+
+ QTcpSocket *server_socket = server.nextPendingConnection();
+ server.close();
+
+ // TCP supports only a single byte of urgent data
+ static const char oob_out = 'Q';
+ QCOMPARE(::send(server_socket->socketDescriptor(), &oob_out, 1, MSG_OOB),
+ ssize_t(1));
+
+ struct pollfd pfd = {
+ int(client_socket.socketDescriptor()),
+ POLLPRI | POLLIN,
+ 0
+ };
+ int res = qt_poll(&pfd, 1, NULL);
+
+ QCOMPARE(res, 1);
+ QCOMPARE(pfd.revents, short(POLLPRI | POLLIN));
+
+ char oob_in = 0;
+ // We do not specify MSG_OOB here as SO_OOBINLINE is turned on by default
+ // in the native socket engine
+ QCOMPARE(::recv(client_socket.socketDescriptor(), &oob_in, 1, 0),
+ ssize_t(1));
+ QCOMPARE(oob_in, oob_out);
+
+ server_socket->close();
+ pfd.events = POLLIN;
+ res = qt_poll(&pfd, 1, NULL);
+
+ QCOMPARE(res, 1);
+ QCOMPARE(pfd.revents, short(POLLHUP));
+}
+#endif // QT_BUILD_INTERNAL
+
+QTEST_APPLESS_MAIN(tst_qt_poll)
+#include "tst_qt_poll.moc"