Add qt_safe_poll
This function is introduced to safely provide poll(2)-like semantics for socket multiplexing on Unix-like platforms. For platforms where no poll system call is available, an implementation based on select(2) is provided. Change-Id: I320e97dae5924316675a74d1897c48cae292ac6d Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
f9ba58a13b
commit
105fc117b7
45
config.tests/unix/poll/poll.cpp
Normal file
45
config.tests/unix/poll/poll.cpp
Normal file
@ -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);
|
||||||
|
}
|
2
config.tests/unix/poll/poll.pro
Normal file
2
config.tests/unix/poll/poll.pro
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
SOURCES = poll.cpp
|
||||||
|
CONFIG -= qt
|
51
config.tests/unix/pollts/pollts.cpp
Normal file
51
config.tests/unix/pollts/pollts.cpp
Normal file
@ -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);
|
||||||
|
}
|
2
config.tests/unix/pollts/pollts.pro
Normal file
2
config.tests/unix/pollts/pollts.pro
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
SOURCES = pollts.cpp
|
||||||
|
CONFIG -= qt
|
50
config.tests/unix/ppoll/ppoll.cpp
Normal file
50
config.tests/unix/ppoll/ppoll.cpp
Normal file
@ -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);
|
||||||
|
}
|
2
config.tests/unix/ppoll/ppoll.pro
Normal file
2
config.tests/unix/ppoll/ppoll.pro
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
SOURCES = ppoll.cpp
|
||||||
|
CONFIG -= qt
|
15
configure
vendored
15
configure
vendored
@ -745,6 +745,7 @@ CFG_GETIFADDRS=auto
|
|||||||
CFG_INOTIFY=auto
|
CFG_INOTIFY=auto
|
||||||
CFG_EVENTFD=auto
|
CFG_EVENTFD=auto
|
||||||
CFG_CLOEXEC=no
|
CFG_CLOEXEC=no
|
||||||
|
CFG_POLL=auto
|
||||||
CFG_RPATH=yes
|
CFG_RPATH=yes
|
||||||
CFG_FRAMEWORK=auto
|
CFG_FRAMEWORK=auto
|
||||||
CFG_USE_GOLD_LINKER=auto
|
CFG_USE_GOLD_LINKER=auto
|
||||||
@ -6135,6 +6136,16 @@ if compileTest unix/cloexec "cloexec"; then
|
|||||||
CFG_CLOEXEC=yes
|
CFG_CLOEXEC=yes
|
||||||
fi
|
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
|
if [ "$XPLATFORM_MAC" = "yes" ] && [ "$CFG_SECURETRANSPORT" != "no" ] && ([ "$CFG_OPENSSL" = "no" ] || [ "$CFG_OPENSSL" = "auto" ]); then
|
||||||
CFG_SECURETRANSPORT=yes
|
CFG_SECURETRANSPORT=yes
|
||||||
CFG_OPENSSL=no
|
CFG_OPENSSL=no
|
||||||
@ -6444,6 +6455,10 @@ fi
|
|||||||
if [ "$CFG_CLOEXEC" = "yes" ]; then
|
if [ "$CFG_CLOEXEC" = "yes" ]; then
|
||||||
QT_CONFIG="$QT_CONFIG threadsafe-cloexec"
|
QT_CONFIG="$QT_CONFIG threadsafe-cloexec"
|
||||||
fi
|
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
|
if [ "$CFG_LIBJPEG" = "no" ]; then
|
||||||
CFG_JPEG="no"
|
CFG_JPEG="no"
|
||||||
elif [ "$CFG_LIBJPEG" = "system" ]; then
|
elif [ "$CFG_LIBJPEG" = "system" ]; then
|
||||||
|
@ -143,6 +143,10 @@ unix|integrity {
|
|||||||
kernel/qeventdispatcher_unix_p.h \
|
kernel/qeventdispatcher_unix_p.h \
|
||||||
kernel/qtimerinfo_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) {
|
contains(QT_CONFIG, glib) {
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
kernel/qeventdispatcher_glib.cpp
|
kernel/qeventdispatcher_glib.cpp
|
||||||
|
@ -52,6 +52,33 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
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,
|
static inline bool time_update(struct timespec *tv, const struct timespec &start,
|
||||||
const struct timespec &timeout)
|
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
|
#ifndef Q_OS_QNX
|
||||||
ret = ::pselect(nfds, fdread, fdwrite, fdexcept, &timeout, 0);
|
ret = ::pselect(nfds, fdread, fdwrite, fdexcept, &timeout, 0);
|
||||||
#else
|
#else
|
||||||
timeval timeoutVal;
|
timeval timeoutVal = timespecToTimeval(timeout);
|
||||||
timeoutVal.tv_sec = timeout.tv_sec;
|
|
||||||
timeoutVal.tv_usec = timeout.tv_nsec / 1000;
|
|
||||||
ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeoutVal);
|
ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeoutVal);
|
||||||
#endif
|
#endif
|
||||||
if (ret != -1 || errno != EINTR)
|
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)
|
int qt_select_msecs(int nfds, fd_set *fdread, fd_set *fdwrite, int timeout)
|
||||||
{
|
{
|
||||||
if (timeout < 0)
|
if (timeout < 0)
|
||||||
return qt_safe_select(nfds, fdread, fdwrite, 0, 0);
|
return qt_safe_select(nfds, fdread, fdwrite, 0, 0);
|
||||||
|
|
||||||
struct timespec tv;
|
struct timespec tv = millisecsToTimespec(timeout);
|
||||||
tv.tv_sec = timeout / 1000;
|
|
||||||
tv.tv_nsec = (timeout % 1000) * 1000 * 1000;
|
|
||||||
return qt_safe_select(nfds, fdread, fdwrite, 0, &tv);
|
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
|
QT_END_NAMESPACE
|
||||||
|
@ -66,6 +66,28 @@
|
|||||||
# include <ioLib.h>
|
# include <ioLib.h>
|
||||||
#endif
|
#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;
|
struct sockaddr;
|
||||||
|
|
||||||
#define EINTR_LOOP(var, cmd) \
|
#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;
|
timespec qt_gettime() Q_DECL_NOTHROW;
|
||||||
void qt_nanosleep(timespec amount);
|
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,
|
Q_CORE_EXPORT int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept,
|
||||||
const struct timespec *tv);
|
const struct timespec *tv);
|
||||||
|
|
||||||
|
4
tests/manual/qt_poll/qt_poll.pro
Normal file
4
tests/manual/qt_poll/qt_poll.pro
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
CONFIG += testcase
|
||||||
|
TARGET = tst_qt_poll
|
||||||
|
QT = core-private network testlib
|
||||||
|
SOURCES = tst_qt_poll.cpp
|
158
tests/manual/qt_poll/tst_qt_poll.cpp
Normal file
158
tests/manual/qt_poll/tst_qt_poll.cpp
Normal file
@ -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"
|
Loading…
x
Reference in New Issue
Block a user