diff --git a/config.tests/unix/poll/poll.cpp b/config.tests/unix/poll/poll.cpp new file mode 100644 index 0000000..06ae038 --- /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 + +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 0000000..70121b4 --- /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/poll/poll.cpp b/config.tests/unix/pollts/poll/poll.cpp new file mode 100644 index 0000000..06ae038 --- /dev/null +++ b/config.tests/unix/pollts/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 + +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/pollts/poll/poll.pro b/config.tests/unix/pollts/poll/poll.pro new file mode 100644 index 0000000..70121b4 --- /dev/null +++ b/config.tests/unix/pollts/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 0000000..c2d1940 --- /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 +#include +#include + +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/poll/poll.cpp b/config.tests/unix/pollts/pollts/poll/poll.cpp new file mode 100644 index 0000000..06ae038 --- /dev/null +++ b/config.tests/unix/pollts/pollts/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 + +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/pollts/pollts/poll/poll.pro b/config.tests/unix/pollts/pollts/poll/poll.pro new file mode 100644 index 0000000..70121b4 --- /dev/null +++ b/config.tests/unix/pollts/pollts/poll/poll.pro @@ -0,0 +1,2 @@ +SOURCES = poll.cpp +CONFIG -= qt diff --git a/config.tests/unix/pollts/pollts/pollts.cpp b/config.tests/unix/pollts/pollts/pollts.cpp new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/config.tests/unix/pollts/pollts/pollts.cpp @@ -0,0 +1 @@ + diff --git a/config.tests/unix/pollts/pollts/pollts.pro b/config.tests/unix/pollts/pollts/pollts.pro new file mode 100644 index 0000000..5109dc3 --- /dev/null +++ b/config.tests/unix/pollts/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 0000000..5b0cc4d --- /dev/null +++ b/config.tests/unix/ppoll/ppoll.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 +#include + +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 0000000..d08a8a0 --- /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 7651e29..9be4179 100755 --- a/configure +++ b/configure @@ -729,6 +729,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 @@ -6043,6 +6044,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 @@ -6350,6 +6361,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/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 8eb5ac9..74a0c41 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -115,11 +115,52 @@ QT_BEGIN_NAMESPACE // so we will use 512 static const int errorBufferMax = 512; -static inline void add_fd(int &nfds, int fd, fd_set *fdset) +namespace { +struct QProcessPoller { - FD_SET(fd, fdset); - if ((fd) > nfds) - nfds = fd; + QProcessPoller(const QProcessPrivate &proc); + + int poll(int timeout); + + pollfd &stdinPipe() { return pfds[0]; } + pollfd &stdoutPipe() { return pfds[1]; } + pollfd &stderrPipe() { return pfds[2]; } + pollfd &forkfd() { return pfds[3]; } + pollfd &childStartedPipe() { return pfds[4]; } + + enum { n_pfds = 5 }; + pollfd pfds[n_pfds]; +}; + +QProcessPoller::QProcessPoller(const QProcessPrivate &proc) +{ + for (int i = 0; i < n_pfds; i++) + pfds[i] = qt_make_pollfd(-1, POLLIN); + + stdoutPipe().fd = proc.stdoutChannel.pipe[0]; + stderrPipe().fd = proc.stderrChannel.pipe[0]; + + if (!proc.stdinChannel.buffer.isEmpty()) { + stdinPipe().fd = proc.stdinChannel.pipe[1]; + stdinPipe().events = POLLOUT; + } + + forkfd().fd = proc.forkfd; + + if (proc.processState == QProcess::Starting) + childStartedPipe().fd = proc.childStartedPipe[0]; +} + +int QProcessPoller::poll(int timeout) +{ + const nfds_t nfds = (childStartedPipe().fd == -1) ? 4 : 5; + return qt_poll_msecs(pfds, nfds, timeout); +} +} // anonymous namespace + +static bool qt_pollfd_check(const pollfd &pfd, short revents) +{ + return pfd.fd >= 0 && (pfd.revents & (revents | POLLHUP | POLLERR | POLLNVAL)) != 0; } static int qt_create_pipe(int *pipe) @@ -812,10 +853,9 @@ bool QProcessPrivate::waitForStarted(int msecs) childStartedPipe[0]); #endif - fd_set fds; - FD_ZERO(&fds); - FD_SET(childStartedPipe[0], &fds); - if (qt_select_msecs(childStartedPipe[0] + 1, &fds, 0, msecs) == 0) { + pollfd pfd = qt_make_pollfd(childStartedPipe[0], POLLIN); + + if (qt_poll_msecs(&pfd, 1, msecs) == 0) { setError(QProcess::Timedout); #if defined (QPROCESS_DEBUG) qDebug("QProcessPrivate::waitForStarted(%d) == false (timed out)", msecs); @@ -855,31 +895,13 @@ bool QProcessPrivate::waitForReadyRead(int msecs) #endif forever { - fd_set fdread; - fd_set fdwrite; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - - int nfds = forkfd; - FD_SET(forkfd, &fdread); - - if (processState == QProcess::Starting) - add_fd(nfds, childStartedPipe[0], &fdread); - - if (stdoutChannel.pipe[0] != -1) - add_fd(nfds, stdoutChannel.pipe[0], &fdread); - if (stderrChannel.pipe[0] != -1) - add_fd(nfds, stderrChannel.pipe[0], &fdread); - - if (!stdinChannel.buffer.isEmpty() && stdinChannel.pipe[1] != -1) - add_fd(nfds, stdinChannel.pipe[1], &fdwrite); + QProcessPoller poller(*this); int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); #ifdef Q_OS_BLACKBERRY int ret = bb_select(notifiers, nfds + 1, &fdread, &fdwrite, timeout); #else - int ret = qt_select_msecs(nfds + 1, &fdread, &fdwrite, timeout); + int ret = poller.poll(timeout); #endif if (ret < 0) { break; @@ -889,18 +911,18 @@ bool QProcessPrivate::waitForReadyRead(int msecs) return false; } - if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { + if (qt_pollfd_check(poller.childStartedPipe(), POLLIN)) { if (!_q_startupNotification()) return false; } bool readyReadEmitted = false; - if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) { + if (qt_pollfd_check(poller.stdoutPipe(), POLLIN)) { bool canRead = _q_canReadStandardOutput(); if (processChannel == QProcess::StandardOutput && canRead) readyReadEmitted = true; } - if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) { + if (qt_pollfd_check(poller.stderrPipe(), POLLIN)) { bool canRead = _q_canReadStandardError(); if (processChannel == QProcess::StandardError && canRead) readyReadEmitted = true; @@ -908,10 +930,10 @@ bool QProcessPrivate::waitForReadyRead(int msecs) if (readyReadEmitted) return true; - if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) + if (qt_pollfd_check(poller.stdinPipe(), POLLOUT)) _q_canWrite(); - if (forkfd == -1 || FD_ISSET(forkfd, &fdread)) { + if (qt_pollfd_check(poller.forkfd(), POLLIN)) { if (_q_processDied()) return false; } @@ -933,32 +955,13 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) #endif while (!stdinChannel.buffer.isEmpty()) { - fd_set fdread; - fd_set fdwrite; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - - int nfds = forkfd; - FD_SET(forkfd, &fdread); - - if (processState == QProcess::Starting) - add_fd(nfds, childStartedPipe[0], &fdread); - - if (stdoutChannel.pipe[0] != -1) - add_fd(nfds, stdoutChannel.pipe[0], &fdread); - if (stderrChannel.pipe[0] != -1) - add_fd(nfds, stderrChannel.pipe[0], &fdread); - - - if (!stdinChannel.buffer.isEmpty() && stdinChannel.pipe[1] != -1) - add_fd(nfds, stdinChannel.pipe[1], &fdwrite); + QProcessPoller poller(*this); int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); #ifdef Q_OS_BLACKBERRY int ret = bb_select(notifiers, nfds + 1, &fdread, &fdwrite, timeout); #else - int ret = qt_select_msecs(nfds + 1, &fdread, &fdwrite, timeout); + int ret = poller.poll(timeout); #endif if (ret < 0) { break; @@ -969,21 +972,21 @@ bool QProcessPrivate::waitForBytesWritten(int msecs) return false; } - if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { + if (qt_pollfd_check(poller.childStartedPipe(), POLLIN)) { if (!_q_startupNotification()) return false; } - if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) + if (qt_pollfd_check(poller.stdinPipe(), POLLOUT)) return _q_canWrite(); - if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) + if (qt_pollfd_check(poller.stdoutPipe(), POLLIN)) _q_canReadStandardOutput(); - if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) + if (qt_pollfd_check(poller.stderrPipe(), POLLIN)) _q_canReadStandardError(); - if (forkfd == -1 || FD_ISSET(forkfd, &fdread)) { + if (qt_pollfd_check(poller.forkfd(), POLLIN)) { if (_q_processDied()) return false; } @@ -1006,32 +1009,13 @@ bool QProcessPrivate::waitForFinished(int msecs) #endif forever { - fd_set fdread; - fd_set fdwrite; - int nfds = -1; - - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - - if (processState == QProcess::Starting) - add_fd(nfds, childStartedPipe[0], &fdread); - - if (stdoutChannel.pipe[0] != -1) - add_fd(nfds, stdoutChannel.pipe[0], &fdread); - if (stderrChannel.pipe[0] != -1) - add_fd(nfds, stderrChannel.pipe[0], &fdread); - - if (processState == QProcess::Running && forkfd != -1) - add_fd(nfds, forkfd, &fdread); - - if (!stdinChannel.buffer.isEmpty() && stdinChannel.pipe[1] != -1) - add_fd(nfds, stdinChannel.pipe[1], &fdwrite); + QProcessPoller poller(*this); int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed()); #ifdef Q_OS_BLACKBERRY int ret = bb_select(notifiers, nfds + 1, &fdread, &fdwrite, timeout); #else - int ret = qt_select_msecs(nfds + 1, &fdread, &fdwrite, timeout); + int ret = poller.poll(timeout); #endif if (ret < 0) { break; @@ -1041,20 +1025,20 @@ bool QProcessPrivate::waitForFinished(int msecs) return false; } - if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { + if (qt_pollfd_check(poller.childStartedPipe(), POLLIN)) { if (!_q_startupNotification()) return false; } - if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) + if (qt_pollfd_check(poller.stdinPipe(), POLLOUT)) _q_canWrite(); - if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) + if (qt_pollfd_check(poller.stdoutPipe(), POLLIN)) _q_canReadStandardOutput(); - if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) + if (qt_pollfd_check(poller.stderrPipe(), POLLIN)) _q_canReadStandardError(); - if (forkfd == -1 || FD_ISSET(forkfd, &fdread)) { + if (qt_pollfd_check(poller.forkfd(), POLLIN)) { if (_q_processDied()) return true; } @@ -1064,10 +1048,8 @@ bool QProcessPrivate::waitForFinished(int msecs) bool QProcessPrivate::waitForWrite(int msecs) { - fd_set fdwrite; - FD_ZERO(&fdwrite); - FD_SET(stdinChannel.pipe[1], &fdwrite); - return qt_select_msecs(stdinChannel.pipe[1] + 1, 0, &fdwrite, msecs < 0 ? 0 : msecs) == 1; + pollfd pfd = qt_make_pollfd(stdinChannel.pipe[1], POLLOUT); + return qt_poll_msecs(&pfd, 1, msecs < 0 ? 0 : msecs) == 1; } void QProcessPrivate::findExitCode() diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index bc93791..ff64b4e 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -143,8 +143,14 @@ unix|integrity { kernel/qcore_unix_p.h \ kernel/qcrashhandler_p.h \ kernel/qeventdispatcher_unix_p.h \ + kernel/qpoll_p.h \ kernel/qtimerinfo_unix_p.h + contains(QT_CONFIG, poll_select): SOURCES += kernel/qpoll.cpp + 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 5695cb3..2ffc746 100644 --- a/src/corelib/kernel/qcore_unix.cpp +++ b/src/corelib/kernel/qcore_unix.cpp @@ -56,6 +56,11 @@ QT_BEGIN_NAMESPACE +#if !defined(QT_HAVE_PPOLL) && defined(QT_HAVE_POLLTS) +# define ppoll pollts +# define QT_HAVE_PPOLL +#endif + static inline bool time_update(struct timespec *tv, const struct timespec &start, const struct timespec &timeout) { @@ -85,9 +90,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) @@ -102,17 +105,82 @@ 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 !defined(QT_HAVE_PPOLL) && defined(QT_HAVE_POLL) +static inline int timespecToMillisecs(const struct timespec *ts) +{ + return (ts == NULL) ? -1 : + (ts->tv_sec * 1000) + (ts->tv_nsec / 1000000); +} +#endif + +// defined in qpoll.cpp +int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts); + +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 defined(QT_HAVE_POLL) + return ::poll(fds, nfds, timespecToMillisecs(timeout_ts)); +#else + 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; + } + } +} + #ifdef Q_OS_BLACKBERRY // The BlackBerry event dispatcher uses bps_get_event. Unfortunately, already registered // socket notifiers are disabled by a call to select. This is to rearm the standard streams. diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index f80dcb5..fc3f63b 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -66,6 +66,12 @@ # include #endif +#ifdef QT_NO_NATIVE_POLL +# include "qpoll_p.h" +#else +# include +#endif + struct sockaddr; #define EINTR_LOOP(var, cmd) \ @@ -122,6 +128,14 @@ inline timespec operator*(const timespec &t1, int mul) return normalizedTimespec(tmp); } +inline timeval timespecToTimeval(const timespec &ts) +{ + timeval tv; + tv.tv_sec = ts.tv_sec; + tv.tv_usec = ts.tv_nsec / 1000; + return tv; +} + inline void qt_ignore_sigpipe() { // Set to ignore SIGPIPE once only. @@ -303,6 +317,27 @@ 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); + +static inline int qt_poll_msecs(struct pollfd *fds, nfds_t nfds, int timeout) +{ + timespec ts, *pts = Q_NULLPTR; + + if (timeout >= 0) { + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000 * 1000; + pts = &ts; + } + + return qt_safe_poll(fds, nfds, pts); +} + +static inline struct pollfd qt_make_pollfd(int fd, short events) +{ + struct pollfd pfd = { fd, events, 0 }; + return pfd; +} + 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/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp index 155f7b7..7d23283 100644 --- a/src/corelib/kernel/qeventdispatcher_unix.cpp +++ b/src/corelib/kernel/qeventdispatcher_unix.cpp @@ -59,7 +59,7 @@ # define _POSIX_MONOTONIC_CLOCK 1 # endif # include -# include +# include #endif #if (_POSIX_MONOTONIC_CLOCK-0 <= 0) || defined(QT_BOOTSTRAPPED) @@ -68,6 +68,20 @@ QT_BEGIN_NAMESPACE +static const char *socketType(QSocketNotifier::Type type) +{ + switch (type) { + case QSocketNotifier::Read: + return "Read"; + case QSocketNotifier::Write: + return "Write"; + case QSocketNotifier::Exception: + return "Exception"; + } + + Q_UNREACHABLE(); +} + #if defined(Q_OS_INTEGRITY) || defined(Q_OS_VXWORKS) static void initThreadPipeFD(int fd) { @@ -137,8 +151,6 @@ QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate() if (pipefail) qFatal("QEventDispatcherUNIXPrivate(): Can not continue without a thread pipe"); - - sn_highest = -1; } QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate() @@ -163,116 +175,11 @@ QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate() qDeleteAll(timerList); } -int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags, timespec *timeout) -{ - Q_Q(QEventDispatcherUNIX); - - // needed in QEventDispatcherUNIX::select() - timerList.updateCurrentTime(); - - int nsel; - do { - // Process timers and socket notifiers - the common UNIX stuff - int highest = 0; - if (! (flags & QEventLoop::ExcludeSocketNotifiers) && (sn_highest >= 0)) { - // return the highest fd we can wait for input on - sn_vec[0].select_fds = sn_vec[0].enabled_fds; - sn_vec[1].select_fds = sn_vec[1].enabled_fds; - sn_vec[2].select_fds = sn_vec[2].enabled_fds; - highest = sn_highest; - } else { - FD_ZERO(&sn_vec[0].select_fds); - FD_ZERO(&sn_vec[1].select_fds); - FD_ZERO(&sn_vec[2].select_fds); - } - - int wakeUpFd = initThreadWakeUp(); - highest = qMax(highest, wakeUpFd); - - nsel = q->select(highest + 1, - &sn_vec[0].select_fds, - &sn_vec[1].select_fds, - &sn_vec[2].select_fds, - timeout); - } while (nsel == -1 && (errno == EINTR || errno == EAGAIN)); - - if (nsel == -1) { - if (errno == EBADF) { - // it seems a socket notifier has a bad fd... find out - // which one it is and disable it - fd_set fdset; - timeval tm; - tm.tv_sec = tm.tv_usec = 0l; - - for (int type = 0; type < 3; ++type) { - QSockNotType::List &list = sn_vec[type].list; - if (list.size() == 0) - continue; - - for (int i = 0; i < list.size(); ++i) { - QSockNot *sn = list[i]; - - FD_ZERO(&fdset); - FD_SET(sn->fd, &fdset); - - int ret = -1; - do { - switch (type) { - case 0: // read - ret = select(sn->fd + 1, &fdset, 0, 0, &tm); - break; - case 1: // write - ret = select(sn->fd + 1, 0, &fdset, 0, &tm); - break; - case 2: // except - ret = select(sn->fd + 1, 0, 0, &fdset, &tm); - break; - } - } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); - - if (ret == -1 && errno == EBADF) { - // disable the invalid socket notifier - static const char *t[] = { "Read", "Write", "Exception" }; - qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...", - sn->fd, t[type]); - sn->obj->setEnabled(false); - } - } - } - } else { - // EINVAL... shouldn't happen, so let's complain to stderr - // and hope someone sends us a bug report - perror("select"); - } - } - - int nevents = processThreadWakeUp(nsel); - - // activate socket notifiers - if (! (flags & QEventLoop::ExcludeSocketNotifiers) && nsel > 0 && sn_highest >= 0) { - // if select says data is ready on any socket, then set the socket notifier - // to pending - for (int i=0; i<3; i++) { - QSockNotType::List &list = sn_vec[i].list; - for (int j = 0; j < list.size(); ++j) { - QSockNot *sn = list[j]; - if (FD_ISSET(sn->fd, &sn_vec[i].select_fds)) - q->setSocketNotifierPending(sn->obj); - } - } - } - return (nevents + q->activateSocketNotifiers()); -} - -int QEventDispatcherUNIXPrivate::initThreadWakeUp() +int QEventDispatcherUNIXPrivate::processThreadWakeUp(const pollfd &pfd) { - FD_SET(thread_pipe[0], &sn_vec[0].select_fds); - return thread_pipe[0]; -} + Q_ASSERT(pfd.fd == thread_pipe[0]); -int QEventDispatcherUNIXPrivate::processThreadWakeUp(int nsel) -{ - if (nsel > 0 && FD_ISSET(thread_pipe[0], &sn_vec[0].select_fds)) { + if (pfd.revents & POLLIN) { // some other thread woke us up... consume the data on the thread pipe so that // select doesn't immediately return next time #if defined(Q_OS_VXWORKS) @@ -302,6 +209,80 @@ int QEventDispatcherUNIXPrivate::processThreadWakeUp(int nsel) return 0; } +void QEventDispatcherUNIXPrivate::setSocketNotifierPending(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + + if (pendingNotifiers.contains(notifier)) + return; + + pendingNotifiers << notifier; +} + +int QEventDispatcherUNIXPrivate::activateTimers() +{ + return timerList.activateTimers(); +} + +void QEventDispatcherUNIXPrivate::markPendingSocketNotifiers() +{ + foreach (const pollfd &pfd, pollfds) { + if (pfd.fd < 0 || pfd.revents == 0) + continue; + + auto it = socketNotifiers.find(pfd.fd); + Q_ASSERT(it != socketNotifiers.end()); + + const QSocketNotifierSetUNIX &sn_set = it.value(); + + static const struct { + QSocketNotifier::Type type; + short flags; + } notifiers[] = { + { QSocketNotifier::Read, POLLIN | POLLHUP | POLLERR }, + { QSocketNotifier::Write, POLLOUT | POLLHUP | POLLERR }, + { QSocketNotifier::Exception, POLLPRI | POLLHUP | POLLERR } + }; + + for (const auto &n : notifiers) { + QSocketNotifier *notifier = sn_set.notifiers[n.type]; + + if (!notifier) + continue; + + if (pfd.revents & POLLNVAL) { + qWarning("QSocketNotifier: Invalid socket %d with type %s, disabling...", + it.key(), socketType(n.type)); + notifier->setEnabled(false); + } + + if (pfd.revents & n.flags) + setSocketNotifierPending(notifier); + } + } + + pollfds.resize(0); +} + +int QEventDispatcherUNIXPrivate::activateSocketNotifiers() +{ + markPendingSocketNotifiers(); + + if (pendingNotifiers.isEmpty()) + return 0; + + int n_activated = 0; + QEvent event(QEvent::SockAct); + + while (!pendingNotifiers.isEmpty()) { + QSocketNotifier *notifier = pendingNotifiers.takeFirst(); + QCoreApplication::sendEvent(notifier, &event); + ++n_activated; + } + + return n_activated; +} + QEventDispatcherUNIX::QEventDispatcherUNIX(QObject *parent) : QAbstractEventDispatcher(*new QEventDispatcherUNIXPrivate, parent) { } @@ -311,14 +292,7 @@ QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObj { } QEventDispatcherUNIX::~QEventDispatcherUNIX() -{ -} - -int QEventDispatcherUNIX::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - timespec *timeout) -{ - return qt_safe_select(nfds, readfds, writefds, exceptfds, timeout); -} +{ } /*! \internal @@ -390,22 +364,6 @@ QEventDispatcherUNIX::registeredTimers(QObject *object) const } /***************************************************************************** - Socket notifier type - *****************************************************************************/ -QSockNotType::QSockNotType() -{ - FD_ZERO(&select_fds); - FD_ZERO(&enabled_fds); - FD_ZERO(&pending_fds); -} - -QSockNotType::~QSockNotType() -{ - for (int i = 0; i < list.size(); ++i) - delete list[i]; -} - -/***************************************************************************** QEventDispatcher implementations for UNIX *****************************************************************************/ @@ -413,160 +371,57 @@ void QEventDispatcherUNIX::registerSocketNotifier(QSocketNotifier *notifier) { Q_ASSERT(notifier); int sockfd = notifier->socket(); - int type = notifier->type(); + QSocketNotifier::Type type = notifier->type(); #ifndef QT_NO_DEBUG - if (sockfd < 0 - || unsigned(sockfd) >= FD_SETSIZE) { - qWarning("QSocketNotifier: Internal error"); - return; - } else if (notifier->thread() != thread() - || thread() != QThread::currentThread()) { + if (notifier->thread() != thread() || thread() != QThread::currentThread()) { qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); return; } #endif Q_D(QEventDispatcherUNIX); - QSockNotType::List &list = d->sn_vec[type].list; - fd_set *fds = &d->sn_vec[type].enabled_fds; - QSockNot *sn; - - sn = new QSockNot; - sn->obj = notifier; - sn->fd = sockfd; - sn->queue = &d->sn_vec[type].pending_fds; - - int i; - for (i = 0; i < list.size(); ++i) { - QSockNot *p = list[i]; - if (p->fd < sockfd) - break; - if (p->fd == sockfd) { - static const char *t[] = { "Read", "Write", "Exception" }; - qWarning("QSocketNotifier: Multiple socket notifiers for " - "same socket %d and type %s", sockfd, t[type]); - } - } - list.insert(i, sn); + QSocketNotifierSetUNIX &sn_set = d->socketNotifiers[sockfd]; + + if (sn_set.notifiers[type] && sn_set.notifiers[type] != notifier) + qWarning("%s: Multiple socket notifiers for same socket %d and type %s", + Q_FUNC_INFO, sockfd, socketType(type)); - FD_SET(sockfd, fds); - d->sn_highest = qMax(d->sn_highest, sockfd); + sn_set.notifiers[type] = notifier; } void QEventDispatcherUNIX::unregisterSocketNotifier(QSocketNotifier *notifier) { Q_ASSERT(notifier); int sockfd = notifier->socket(); - int type = notifier->type(); + QSocketNotifier::Type type = notifier->type(); #ifndef QT_NO_DEBUG - if (sockfd < 0 - || unsigned(sockfd) >= FD_SETSIZE) { - qWarning("QSocketNotifier: Internal error"); - return; - } else if (notifier->thread() != thread() - || thread() != QThread::currentThread()) { + if (notifier->thread() != thread() || thread() != QThread::currentThread()) { qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); return; } #endif Q_D(QEventDispatcherUNIX); - QSockNotType::List &list = d->sn_vec[type].list; - fd_set *fds = &d->sn_vec[type].enabled_fds; - QSockNot *sn = 0; - int i; - for (i = 0; i < list.size(); ++i) { - sn = list[i]; - if(sn->obj == notifier && sn->fd == sockfd) - break; - } - if (i == list.size()) // not found - return; + d->pendingNotifiers.removeOne(notifier); - FD_CLR(sockfd, fds); // clear fd bit - FD_CLR(sockfd, sn->queue); - d->sn_pending_list.removeAll(sn); // remove from activation list - list.removeAt(i); // remove notifier found above - delete sn; - - if (d->sn_highest == sockfd) { // find highest fd - d->sn_highest = -1; - for (int i=0; i<3; i++) { - if (!d->sn_vec[i].list.isEmpty()) - d->sn_highest = qMax(d->sn_highest, // list is fd-sorted - d->sn_vec[i].list[0]->fd); - } - } -} - -void QEventDispatcherUNIX::setSocketNotifierPending(QSocketNotifier *notifier) -{ - Q_ASSERT(notifier); - int sockfd = notifier->socket(); - int type = notifier->type(); -#ifndef QT_NO_DEBUG - if (sockfd < 0 - || unsigned(sockfd) >= FD_SETSIZE) { - qWarning("QSocketNotifier: Internal error"); + auto i = d->socketNotifiers.find(sockfd); + if (i == d->socketNotifiers.end()) return; - } - Q_ASSERT(notifier->thread() == thread() && thread() == QThread::currentThread()); -#endif + QSocketNotifierSetUNIX &sn_set = i.value(); - Q_D(QEventDispatcherUNIX); - QSockNotType::List &list = d->sn_vec[type].list; - QSockNot *sn = 0; - int i; - for (i = 0; i < list.size(); ++i) { - sn = list[i]; - if(sn->obj == notifier && sn->fd == sockfd) - break; - } - if (i == list.size()) // not found + if (sn_set.notifiers[type] == nullptr) return; - // We choose a random activation order to be more fair under high load. - // If a constant order is used and a peer early in the list can - // saturate the IO, it might grab our attention completely. - // Also, if we're using a straight list, the callback routines may - // delete other entries from the list before those other entries are - // processed. - if (! FD_ISSET(sn->fd, sn->queue)) { - if (d->sn_pending_list.isEmpty()) { - d->sn_pending_list.append(sn); - } else { - d->sn_pending_list.insert((qrand() & 0xff) % - (d->sn_pending_list.size()+1), sn); - } - FD_SET(sn->fd, sn->queue); + if (sn_set.notifiers[type] != notifier) { + qWarning("%s: Multiple socket notifiers for same socket %d and type %s", + Q_FUNC_INFO, sockfd, socketType(type)); + return; } -} - -int QEventDispatcherUNIX::activateTimers() -{ - Q_ASSERT(thread() == QThread::currentThread()); - Q_D(QEventDispatcherUNIX); - return d->timerList.activateTimers(); -} -int QEventDispatcherUNIX::activateSocketNotifiers() -{ - Q_D(QEventDispatcherUNIX); - if (d->sn_pending_list.isEmpty()) - return 0; + sn_set.notifiers[type] = nullptr; - // activate entries - int n_act = 0; - QEvent event(QEvent::SockAct); - while (!d->sn_pending_list.isEmpty()) { - QSockNot *sn = d->sn_pending_list.takeFirst(); - if (FD_ISSET(sn->fd, sn->queue)) { - FD_CLR(sn->fd, sn->queue); - QCoreApplication::sendEvent(sn->obj, &event); - ++n_act; - } - } - return n_act; + if (sn_set.isEmpty()) + d->socketNotifiers.erase(i); } bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags) @@ -578,39 +433,54 @@ bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags) emit awake(); QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); - int nevents = 0; + const bool include_timers = (flags & QEventLoop::X11ExcludeTimers) == 0; + const bool include_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers) == 0; + const bool wait_for_events = flags & QEventLoop::WaitForMoreEvents; + const bool canWait = (d->threadData->canWaitLocked() && !d->interrupt.load() - && (flags & QEventLoop::WaitForMoreEvents)); + && wait_for_events); if (canWait) emit aboutToBlock(); - if (!d->interrupt.load()) { - // return the maximum time we can wait for an event. - timespec *tm = 0; - timespec wait_tm = { 0l, 0l }; - if (!(flags & QEventLoop::X11ExcludeTimers)) { - if (d->timerList.timerWait(wait_tm)) - tm = &wait_tm; - } + if (d->interrupt.load()) + return false; - if (!canWait) { - if (!tm) - tm = &wait_tm; + timespec *tm = nullptr; + timespec wait_tm = { 0, 0 }; - // no time to wait - tm->tv_sec = 0l; - tm->tv_nsec = 0l; - } + if (!canWait || (include_timers && d->timerList.timerWait(wait_tm))) + tm = &wait_tm; - nevents = d->doSelect(flags, tm); + d->pollfds.reserve(1 + (include_notifiers ? d->socketNotifiers.size() : 0)); + d->pollfds.resize(0); - // activate timers - if (! (flags & QEventLoop::X11ExcludeTimers)) { - nevents += activateTimers(); - } + if (include_notifiers) + for (auto it = d->socketNotifiers.cbegin(); it != d->socketNotifiers.cend(); ++it) + d->pollfds.append(qt_make_pollfd(it.key(), it.value().events())); + + // This must be last, as it's popped off the end below + d->pollfds.append(qt_make_pollfd(d->thread_pipe[0], POLLIN)); + + int nevents = 0; + + switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)) { + case -1: + perror("qt_safe_poll"); + break; + case 0: + break; + default: + nevents += d->processThreadWakeUp(d->pollfds.takeLast()); + if (include_notifiers) + nevents += d->activateSocketNotifiers(); + break; } + + if (include_timers) + nevents += d->activateTimers(); + // return true if we handled events, false otherwise return (nevents > 0); } diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h index df08080..b76bb8e 100644 --- a/src/corelib/kernel/qeventdispatcher_unix_p.h +++ b/src/corelib/kernel/qeventdispatcher_unix_p.h @@ -53,38 +53,21 @@ #include "QtCore/qvarlengtharray.h" #include "private/qtimerinfo_unix_p.h" -#if !defined(Q_OS_VXWORKS) -# include -# if (!defined(Q_OS_HPUX) || defined(__ia64)) && !defined(Q_OS_NACL) -# include -# endif -#endif - QT_BEGIN_NAMESPACE -struct QSockNot -{ - QSocketNotifier *obj; - int fd; - fd_set *queue; -}; +class QEventDispatcherUNIXPrivate; -class QSockNotType +struct Q_CORE_EXPORT QSocketNotifierSetUNIX Q_DECL_FINAL { -public: - QSockNotType(); - ~QSockNotType(); + inline QSocketNotifierSetUNIX() Q_DECL_NOTHROW; - typedef QPodList List; - - List list; - fd_set select_fds; - fd_set enabled_fds; - fd_set pending_fds; + inline bool isEmpty() const Q_DECL_NOTHROW; + inline short events() const Q_DECL_NOTHROW; + QSocketNotifier *notifiers[3]; }; -class QEventDispatcherUNIXPrivate; +Q_DECLARE_TYPEINFO(QSocketNotifierSetUNIX, Q_PRIMITIVE_TYPE); #ifdef Q_OS_QNX # define FINAL_EXCEPT_BLACKBERRY @@ -120,15 +103,6 @@ public: protected: QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent = 0); - - void setSocketNotifierPending(QSocketNotifier *notifier); - - int activateTimers(); - int activateSocketNotifiers(); - - virtual int select(int nfds, - fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - timespec *timeout); }; class Q_CORE_EXPORT QEventDispatcherUNIXPrivate : public QAbstractEventDispatcherPrivate @@ -139,9 +113,13 @@ public: QEventDispatcherUNIXPrivate(); ~QEventDispatcherUNIXPrivate(); - int doSelect(QEventLoop::ProcessEventsFlags flags, timespec *timeout); - virtual int initThreadWakeUp() FINAL_EXCEPT_BLACKBERRY; - virtual int processThreadWakeUp(int nsel) FINAL_EXCEPT_BLACKBERRY; + int processThreadWakeUp(const pollfd &pfd); + + int activateTimers(); + + void markPendingSocketNotifiers(); + int activateSocketNotifiers(); + void setSocketNotifierPending(QSocketNotifier *notifier); bool mainThread; @@ -149,15 +127,12 @@ public: // if thread_pipe[1] is -1, then eventfd(7) is in use and is stored in thread_pipe[0] int thread_pipe[2]; - // highest fd for all socket notifiers - int sn_highest; - // 3 socket notifier types - read, write and exception - QSockNotType sn_vec[3]; + QVector pollfds; - QTimerInfoList timerList; + QHash socketNotifiers; + QVector pendingNotifiers; - // pending socket notifiers list - QSockNotType::List sn_pending_list; + QTimerInfoList timerList; QAtomicInt wakeUps; QAtomicInt interrupt; // bool @@ -165,6 +140,34 @@ public: #undef FINAL_EXCEPT_BLACKBERRY +inline QSocketNotifierSetUNIX::QSocketNotifierSetUNIX() Q_DECL_NOTHROW +{ + notifiers[0] = 0; + notifiers[1] = 0; + notifiers[2] = 0; +} + +inline bool QSocketNotifierSetUNIX::isEmpty() const Q_DECL_NOTHROW +{ + return !notifiers[0] && !notifiers[1] && !notifiers[2]; +} + +inline short QSocketNotifierSetUNIX::events() const Q_DECL_NOTHROW +{ + short result = 0; + + if (notifiers[0]) + result |= POLLIN; + + if (notifiers[1]) + result |= POLLOUT; + + if (notifiers[2]) + result |= POLLPRI; + + return result; +} + QT_END_NAMESPACE #endif // QEVENTDISPATCHER_UNIX_P_H diff --git a/src/corelib/kernel/qpoll.cpp b/src/corelib/kernel/qpoll.cpp new file mode 100644 index 0000000..b152518 --- /dev/null +++ b/src/corelib/kernel/qpoll.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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 "qcore_unix_p.h" + +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) +{ + 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 diff --git a/src/corelib/kernel/qpoll_p.h b/src/corelib/kernel/qpoll_p.h new file mode 100644 index 0000000..497058a --- /dev/null +++ b/src/corelib/kernel/qpoll_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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$ +** +****************************************************************************/ + +#ifndef QPOLL_P_H +#define QPOLL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt code on Unix. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +#ifdef QT_NO_NATIVE_POLL + +#include +#include + +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 // QT_NO_NATIVE_POLL + +QT_END_NAMESPACE + +#endif // QPOLL_P_H diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp index a356e21..79b9b9c 100644 --- a/src/network/socket/qlocalserver_unix.cpp +++ b/src/network/socket/qlocalserver_unix.cpp @@ -277,24 +277,27 @@ void QLocalServerPrivate::_q_onNewConnection() void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut) { - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(listenSocket, &readfds); + pollfd pfd = qt_make_pollfd(listenSocket, POLLIN); - struct timespec timeout; - timeout.tv_sec = msec / 1000; - timeout.tv_nsec = (msec % 1000) * 1000 * 1000; + switch (qt_poll_msecs(&pfd, 1, msec)) { + case 0: + if (timedOut) + *timedOut = true; + return; + break; + default: + if ((pfd.revents & POLLNVAL) == 0) { + _q_onNewConnection(); + return; + } - int result = -1; - result = qt_safe_select(listenSocket + 1, &readfds, 0, 0, (msec == -1) ? 0 : &timeout); - if (-1 == result) { + errno = EBADF; + // FALLTHROUGH + case -1: setError(QLatin1String("QLocalServer::waitForNewConnection")); closeServer(); + break; } - if (result > 0) - _q_onNewConnection(); - if (timedOut) - *timedOut = (result == 0); } void QLocalServerPrivate::setError(const QString &function) diff --git a/src/network/socket/qlocalsocket_unix.cpp b/src/network/socket/qlocalsocket_unix.cpp index bb0f11f..3e85217 100644 --- a/src/network/socket/qlocalsocket_unix.cpp +++ b/src/network/socket/qlocalsocket_unix.cpp @@ -507,36 +507,25 @@ void QLocalSocket::setReadBufferSize(qint64 size) bool QLocalSocket::waitForConnected(int msec) { Q_D(QLocalSocket); + if (state() != ConnectingState) return (state() == ConnectedState); - fd_set fds; - FD_ZERO(&fds); - FD_SET(d->connectingSocket, &fds); + QElapsedTimer timer; + timer.start(); - timeval timeout; - timeout.tv_sec = msec / 1000; - timeout.tv_usec = (msec % 1000) * 1000; + pollfd pfd = qt_make_pollfd(d->connectingSocket, POLLIN); - // timeout can not be 0 or else select will return an error. - if (0 == msec) - timeout.tv_usec = 1000; + do { + const int timeout = (msec > 0) ? qMax(msec - timer.elapsed(), Q_INT64_C(0)) : msec; + const int result = qt_poll_msecs(&pfd, 1, timeout); - int result = -1; - // on Linux timeout will be updated by select, but _not_ on other systems. - QElapsedTimer timer; - timer.start(); - while (state() == ConnectingState - && (-1 == msec || timer.elapsed() < msec)) { - result = ::select(d->connectingSocket + 1, &fds, 0, 0, &timeout); - if (-1 == result && errno != EINTR) { - d->errorOccurred( QLocalSocket::UnknownSocketError, - QLatin1String("QLocalSocket::waitForConnected")); - break; - } - if (result > 0) + if (result == -1) + d->errorOccurred(QLocalSocket::UnknownSocketError, + QLatin1String("QLocalSocket::waitForConnected")); + else if (result > 0) d->_q_connectToSocket(); - } + } while (state() == ConnectingState && !timer.hasExpired(msec)); return (state() == ConnectedState); } diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 6a740f4..de86f27 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -1256,47 +1256,36 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const { - fd_set fds; - FD_ZERO(&fds); - FD_SET(socketDescriptor, &fds); - - struct timespec tv; - tv.tv_sec = timeout / 1000; - tv.tv_nsec = (timeout % 1000) * 1000 * 1000; - - int retval; - if (selectForRead) - retval = qt_safe_select(socketDescriptor + 1, &fds, 0, 0, timeout < 0 ? 0 : &tv); - else - retval = qt_safe_select(socketDescriptor + 1, 0, &fds, 0, timeout < 0 ? 0 : &tv); - - return retval; + bool dummy; + return nativeSelect(timeout, selectForRead, !selectForRead, &dummy, &dummy); } int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite, bool *selectForRead, bool *selectForWrite) const { - fd_set fdread; - FD_ZERO(&fdread); + pollfd pfd = qt_make_pollfd(socketDescriptor, 0); + if (checkRead) - FD_SET(socketDescriptor, &fdread); + pfd.events |= POLLIN; - fd_set fdwrite; - FD_ZERO(&fdwrite); if (checkWrite) - FD_SET(socketDescriptor, &fdwrite); + pfd.events |= POLLOUT; - struct timespec tv; - tv.tv_sec = timeout / 1000; - tv.tv_nsec = (timeout % 1000) * 1000 * 1000; - - int ret; - ret = qt_safe_select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); + const int ret = qt_poll_msecs(&pfd, 1, timeout); if (ret <= 0) return ret; - *selectForRead = FD_ISSET(socketDescriptor, &fdread); - *selectForWrite = FD_ISSET(socketDescriptor, &fdwrite); + + if (pfd.revents & POLLNVAL) { + errno = EBADF; + return -1; + } + + static const short read_flags = POLLIN | POLLHUP | POLLERR; + static const short write_flags = POLLOUT | POLLERR; + + *selectForRead = ((pfd.revents & read_flags) != 0); + *selectForWrite = ((pfd.revents & write_flags) != 0); return ret; } diff --git a/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp b/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp index 8ffe4d8..c528891 100644 --- a/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp +++ b/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp @@ -48,6 +48,7 @@ private slots: void memberFunctions(); void implicitConvertibleTypes(); void runWaitLoop(); + void pollForIsFinished(); void recursive(); #ifndef QT_NO_EXCEPTIONS void exceptions(); @@ -353,6 +354,33 @@ void tst_QtConcurrentRun::runWaitLoop() run(fn).waitForFinished(); } +static bool allFinished(const QList > &futures) +{ + auto hasNotFinished = [](const QFuture &future) { return !future.isFinished(); }; + return std::find_if(futures.cbegin(), futures.cend(), hasNotFinished) + == futures.constEnd(); +} + +static void runFunction() +{ + QEventLoop loop; + QTimer::singleShot(20, &loop, &QEventLoop::quit); + loop.exec(); +} + +void tst_QtConcurrentRun::pollForIsFinished() +{ + const int numThreads = std::max(4, 2 * QThread::idealThreadCount()); + QThreadPool::globalInstance()->setMaxThreadCount(numThreads); + + QFutureSynchronizer synchronizer; + for (int i = 0; i < numThreads; ++i) + synchronizer.addFuture(QtConcurrent::run(&runFunction)); + + // same as synchronizer.waitForFinished() but with a timeout + QTRY_VERIFY(allFinished(synchronizer.futures())); +} + QAtomicInt count; void recursiveRun(int level) diff --git a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp index c8bb4cd..9b2bada 100644 --- a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp +++ b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp @@ -430,11 +430,8 @@ public slots: dataSent = serverSocket->waitForBytesWritten(-1); if (dataSent) { - fd_set fdread; - int fd = socket->socketDescriptor(); - FD_ZERO(&fdread); - FD_SET(fd, &fdread); - dataReadable = (1 == qt_safe_select(fd + 1, &fdread, 0, 0, 0)); + pollfd pfd = qt_make_pollfd(socket->socketDescriptor(), POLLIN); + dataReadable = (1 == qt_safe_poll(&pfd, 1, nullptr)); } if (!dataReadable) { diff --git a/tests/manual/qt_poll/qt_poll.pro b/tests/manual/qt_poll/qt_poll.pro new file mode 100644 index 0000000..beea4d1 --- /dev/null +++ b/tests/manual/qt_poll/qt_poll.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qt_poll +QT = core-private network testlib +INCLUDEPATH += ../../../src/corelib/kernel +SOURCES += \ + tst_qt_poll.cpp \ + ../../../src/corelib/kernel/qpoll.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 0000000..56e41e4 --- /dev/null +++ b/tests/manual/qt_poll/tst_qt_poll.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QT_NO_NATIVE_POLL +#define QT_NO_NATIVE_POLL +#endif + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +// defined in qpoll.cpp +int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts); +QT_END_NAMESPACE + +class tst_qt_poll : public QObject +{ + Q_OBJECT + +private slots: + void pollout(); + void pollin(); + void pollnval(); + void pollprihup(); +}; + +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)); +} + +QTEST_APPLESS_MAIN(tst_qt_poll) +#include "tst_qt_poll.moc" diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 555ccbf..67a581a 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -1733,6 +1733,7 @@ void Configure::applySpecSpecifics() dictionary[ "QT_EVDEV" ] = "no"; dictionary[ "QT_MTDEV" ] = "no"; dictionary[ "FONT_CONFIG" ] = "auto"; + dictionary[ "POLL" ] = "poll"; dictionary[ "ANGLE" ] = "no"; dictionary["DECORATIONS"] = "default windows styled"; @@ -1758,6 +1759,7 @@ void Configure::applySpecSpecifics() dictionary[ "QT_XKBCOMMON" ] = "no"; dictionary["ANDROID_STYLE_ASSETS"] = "yes"; dictionary[ "STYLE_ANDROID" ] = "yes"; + dictionary[ "POLL" ] = "poll"; } } @@ -3049,6 +3051,9 @@ void Configure::generateOutputVars() if (dictionary["REDUCE_EXPORTS"] == "yes") qtConfig += "reduce_exports"; + if (!dictionary["POLL"].isEmpty()) + qtConfig += "poll_" + dictionary["POLL"]; + // We currently have no switch for QtConcurrent, so add it unconditionally. qtConfig += "concurrent";