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_EVENTFD=auto
|
||||
CFG_CLOEXEC=no
|
||||
CFG_POLL=auto
|
||||
CFG_RPATH=yes
|
||||
CFG_FRAMEWORK=auto
|
||||
CFG_USE_GOLD_LINKER=auto
|
||||
@ -6135,6 +6136,16 @@ if compileTest unix/cloexec "cloexec"; then
|
||||
CFG_CLOEXEC=yes
|
||||
fi
|
||||
|
||||
if compileTest unix/ppoll "ppoll"; then
|
||||
CFG_POLL="ppoll"
|
||||
elif compileTest unix/pollts "pollts"; then
|
||||
CFG_POLL="pollts"
|
||||
elif compileTest unix/poll "poll"; then
|
||||
CFG_POLL="poll"
|
||||
else
|
||||
CFG_POLL="select"
|
||||
fi
|
||||
|
||||
if [ "$XPLATFORM_MAC" = "yes" ] && [ "$CFG_SECURETRANSPORT" != "no" ] && ([ "$CFG_OPENSSL" = "no" ] || [ "$CFG_OPENSSL" = "auto" ]); then
|
||||
CFG_SECURETRANSPORT=yes
|
||||
CFG_OPENSSL=no
|
||||
@ -6444,6 +6455,10 @@ fi
|
||||
if [ "$CFG_CLOEXEC" = "yes" ]; then
|
||||
QT_CONFIG="$QT_CONFIG threadsafe-cloexec"
|
||||
fi
|
||||
if [ "$CFG_POLL" = "select" ]; then
|
||||
QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_NATIVE_POLL"
|
||||
fi
|
||||
QT_CONFIG="$QT_CONFIG poll_$CFG_POLL"
|
||||
if [ "$CFG_LIBJPEG" = "no" ]; then
|
||||
CFG_JPEG="no"
|
||||
elif [ "$CFG_LIBJPEG" = "system" ]; then
|
||||
|
@ -143,6 +143,10 @@ unix|integrity {
|
||||
kernel/qeventdispatcher_unix_p.h \
|
||||
kernel/qtimerinfo_unix_p.h
|
||||
|
||||
contains(QT_CONFIG, poll_poll): DEFINES += QT_HAVE_POLL
|
||||
contains(QT_CONFIG, poll_ppoll): DEFINES += QT_HAVE_POLL QT_HAVE_PPOLL
|
||||
contains(QT_CONFIG, poll_pollts): DEFINES += QT_HAVE_POLL QT_HAVE_POLLTS
|
||||
|
||||
contains(QT_CONFIG, glib) {
|
||||
SOURCES += \
|
||||
kernel/qeventdispatcher_glib.cpp
|
||||
|
@ -52,6 +52,33 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if !defined(QT_HAVE_PPOLL) && defined(QT_HAVE_POLLTS)
|
||||
# define ppoll pollts
|
||||
# define QT_HAVE_PPOLL
|
||||
#endif
|
||||
|
||||
#ifndef _POSIX_POLL
|
||||
# if defined(QT_HAVE_PPOLL) || _POSIX_VERSION >= 200809L || _XOPEN_VERSION >= 700
|
||||
# define _POSIX_POLL 1
|
||||
# elif defined(QT_HAVE_POLL)
|
||||
# define _POSIX_POLL 0
|
||||
# else
|
||||
# define _POSIX_POLL -1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_QNX) || _POSIX_POLL <= 0 || defined(QT_BUILD_INTERNAL)
|
||||
static inline struct timeval timespecToTimeval(const struct timespec &ts)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = ts.tv_sec;
|
||||
tv.tv_usec = ts.tv_nsec / 1000;
|
||||
|
||||
return tv;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool time_update(struct timespec *tv, const struct timespec &start,
|
||||
const struct timespec &timeout)
|
||||
{
|
||||
@ -81,9 +108,7 @@ int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept,
|
||||
#ifndef Q_OS_QNX
|
||||
ret = ::pselect(nfds, fdread, fdwrite, fdexcept, &timeout, 0);
|
||||
#else
|
||||
timeval timeoutVal;
|
||||
timeoutVal.tv_sec = timeout.tv_sec;
|
||||
timeoutVal.tv_usec = timeout.tv_nsec / 1000;
|
||||
timeval timeoutVal = timespecToTimeval(timeout);
|
||||
ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeoutVal);
|
||||
#endif
|
||||
if (ret != -1 || errno != EINTR)
|
||||
@ -98,15 +123,268 @@ int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept,
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct timespec millisecsToTimespec(const unsigned int ms)
|
||||
{
|
||||
struct timespec tv;
|
||||
|
||||
tv.tv_sec = ms / 1000;
|
||||
tv.tv_nsec = (ms % 1000) * 1000 * 1000;
|
||||
|
||||
return tv;
|
||||
}
|
||||
|
||||
int qt_select_msecs(int nfds, fd_set *fdread, fd_set *fdwrite, int timeout)
|
||||
{
|
||||
if (timeout < 0)
|
||||
return qt_safe_select(nfds, fdread, fdwrite, 0, 0);
|
||||
|
||||
struct timespec tv;
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_nsec = (timeout % 1000) * 1000 * 1000;
|
||||
struct timespec tv = millisecsToTimespec(timeout);
|
||||
return qt_safe_select(nfds, fdread, fdwrite, 0, &tv);
|
||||
}
|
||||
|
||||
#if _POSIX_POLL <= 0 || defined(QT_BUILD_INTERNAL)
|
||||
|
||||
#define QT_POLL_READ_MASK (POLLIN | POLLRDNORM)
|
||||
#define QT_POLL_WRITE_MASK (POLLOUT | POLLWRNORM | POLLWRBAND)
|
||||
#define QT_POLL_EXCEPT_MASK (POLLPRI | POLLRDBAND)
|
||||
#define QT_POLL_ERROR_MASK (POLLERR | POLLNVAL)
|
||||
#define QT_POLL_EVENTS_MASK (QT_POLL_READ_MASK | QT_POLL_WRITE_MASK | QT_POLL_EXCEPT_MASK)
|
||||
|
||||
static inline int qt_poll_prepare(struct pollfd *fds, nfds_t nfds,
|
||||
fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
|
||||
{
|
||||
int max_fd = -1;
|
||||
|
||||
FD_ZERO(read_fds);
|
||||
FD_ZERO(write_fds);
|
||||
FD_ZERO(except_fds);
|
||||
|
||||
for (nfds_t i = 0; i < nfds; i++) {
|
||||
if (fds[i].fd >= FD_SETSIZE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((fds[i].fd < 0) || (fds[i].revents & QT_POLL_ERROR_MASK))
|
||||
continue;
|
||||
|
||||
if (fds[i].events & QT_POLL_READ_MASK)
|
||||
FD_SET(fds[i].fd, read_fds);
|
||||
|
||||
if (fds[i].events & QT_POLL_WRITE_MASK)
|
||||
FD_SET(fds[i].fd, write_fds);
|
||||
|
||||
if (fds[i].events & QT_POLL_EXCEPT_MASK)
|
||||
FD_SET(fds[i].fd, except_fds);
|
||||
|
||||
if (fds[i].events & QT_POLL_EVENTS_MASK)
|
||||
max_fd = qMax(max_fd, fds[i].fd);
|
||||
}
|
||||
|
||||
return max_fd + 1;
|
||||
}
|
||||
|
||||
static inline void qt_poll_examine_ready_read(struct pollfd &pfd)
|
||||
{
|
||||
int res;
|
||||
char data;
|
||||
|
||||
EINTR_LOOP(res, ::recv(pfd.fd, &data, sizeof(data), MSG_PEEK));
|
||||
const int error = (res < 0) ? errno : 0;
|
||||
|
||||
if (res == 0) {
|
||||
pfd.revents |= POLLHUP;
|
||||
} else if (res > 0 || error == ENOTSOCK || error == ENOTCONN) {
|
||||
pfd.revents |= QT_POLL_READ_MASK & pfd.events;
|
||||
} else {
|
||||
switch (error) {
|
||||
case ESHUTDOWN:
|
||||
case ECONNRESET:
|
||||
case ECONNABORTED:
|
||||
case ENETRESET:
|
||||
pfd.revents |= POLLHUP;
|
||||
break;
|
||||
default:
|
||||
pfd.revents |= POLLERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int qt_poll_sweep(struct pollfd *fds, nfds_t nfds,
|
||||
fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
for (nfds_t i = 0; i < nfds; i++) {
|
||||
if (fds[i].fd < 0)
|
||||
continue;
|
||||
|
||||
if (FD_ISSET(fds[i].fd, read_fds))
|
||||
qt_poll_examine_ready_read(fds[i]);
|
||||
|
||||
if (FD_ISSET(fds[i].fd, write_fds))
|
||||
fds[i].revents |= QT_POLL_WRITE_MASK & fds[i].events;
|
||||
|
||||
if (FD_ISSET(fds[i].fd, except_fds))
|
||||
fds[i].revents |= QT_POLL_EXCEPT_MASK & fds[i].events;
|
||||
|
||||
if (fds[i].revents != 0)
|
||||
result++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool qt_poll_is_bad_fd(int fd)
|
||||
{
|
||||
int ret;
|
||||
EINTR_LOOP(ret, fcntl(fd, F_GETFD));
|
||||
return (ret == -1 && errno == EBADF);
|
||||
}
|
||||
|
||||
static inline int qt_poll_mark_bad_fds(struct pollfd *fds, const nfds_t nfds)
|
||||
{
|
||||
int n_marked = 0;
|
||||
|
||||
for (nfds_t i = 0; i < nfds; i++) {
|
||||
if (fds[i].fd < 0)
|
||||
continue;
|
||||
|
||||
if (fds[i].revents & QT_POLL_ERROR_MASK)
|
||||
continue;
|
||||
|
||||
if (qt_poll_is_bad_fd(fds[i].fd)) {
|
||||
fds[i].revents |= POLLNVAL;
|
||||
n_marked++;
|
||||
}
|
||||
}
|
||||
|
||||
return n_marked;
|
||||
}
|
||||
|
||||
Q_AUTOTEST_EXPORT int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
|
||||
{
|
||||
if (!fds && nfds) {
|
||||
errno = EFAULT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd_set read_fds, write_fds, except_fds;
|
||||
struct timeval tv, *ptv = 0;
|
||||
|
||||
if (timeout_ts) {
|
||||
tv = timespecToTimeval(*timeout_ts);
|
||||
ptv = &tv;
|
||||
}
|
||||
|
||||
int n_bad_fds = 0;
|
||||
|
||||
for (nfds_t i = 0; i < nfds; i++) {
|
||||
fds[i].revents = 0;
|
||||
|
||||
if (fds[i].fd < 0)
|
||||
continue;
|
||||
|
||||
if (fds[i].events & QT_POLL_EVENTS_MASK)
|
||||
continue;
|
||||
|
||||
if (qt_poll_is_bad_fd(fds[i].fd)) {
|
||||
// Mark bad file descriptors that have no event flags set
|
||||
// here, as we won't be passing them to select below and therefore
|
||||
// need to do the check ourselves
|
||||
fds[i].revents = POLLNVAL;
|
||||
n_bad_fds++;
|
||||
}
|
||||
}
|
||||
|
||||
forever {
|
||||
const int max_fd = qt_poll_prepare(fds, nfds, &read_fds, &write_fds, &except_fds);
|
||||
|
||||
if (max_fd < 0)
|
||||
return max_fd;
|
||||
|
||||
if (n_bad_fds > 0) {
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
ptv = &tv;
|
||||
}
|
||||
|
||||
const int ret = ::select(max_fd, &read_fds, &write_fds, &except_fds, ptv);
|
||||
|
||||
if (ret == 0)
|
||||
return n_bad_fds;
|
||||
|
||||
if (ret > 0)
|
||||
return qt_poll_sweep(fds, nfds, &read_fds, &write_fds, &except_fds);
|
||||
|
||||
if (errno != EBADF)
|
||||
return -1;
|
||||
|
||||
// We have at least one bad file descriptor that we waited on, find out which and try again
|
||||
n_bad_fds += qt_poll_mark_bad_fds(fds, nfds);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _POSIX_POLL <= 0 || defined(QT_BUILD_INTERNAL)
|
||||
|
||||
#if !defined(QT_HAVE_PPOLL) && ((_POSIX_POLL > 0) || defined(_SC_POLL))
|
||||
static inline int timespecToMillisecs(const struct timespec *ts)
|
||||
{
|
||||
return (ts == NULL) ? -1 :
|
||||
(ts->tv_sec * 1000) + (ts->tv_nsec / 1000000);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int qt_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
|
||||
{
|
||||
#if defined(QT_HAVE_PPOLL)
|
||||
return ::ppoll(fds, nfds, timeout_ts, Q_NULLPTR);
|
||||
#elif _POSIX_POLL > 0
|
||||
return ::poll(fds, nfds, timespecToMillisecs(timeout_ts));
|
||||
#else
|
||||
# if defined(_SC_POLL)
|
||||
static const bool have_poll = (sysconf(_SC_POLL) > 0);
|
||||
if (have_poll)
|
||||
return ::poll(fds, nfds, timespecToMillisecs(timeout_ts));
|
||||
# endif
|
||||
return qt_poll(fds, nfds, timeout_ts);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
Behaves as close to POSIX poll(2) as practical but may be implemented
|
||||
using select(2) where necessary. In that case, returns -1 and sets errno
|
||||
to EINVAL if passed any descriptor greater than or equal to FD_SETSIZE.
|
||||
*/
|
||||
int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
|
||||
{
|
||||
if (!timeout_ts) {
|
||||
// no timeout -> block forever
|
||||
int ret;
|
||||
EINTR_LOOP(ret, qt_ppoll(fds, nfds, Q_NULLPTR));
|
||||
return ret;
|
||||
}
|
||||
|
||||
timespec start = qt_gettime();
|
||||
timespec timeout = *timeout_ts;
|
||||
|
||||
// loop and recalculate the timeout as needed
|
||||
forever {
|
||||
const int ret = qt_ppoll(fds, nfds, &timeout);
|
||||
if (ret != -1 || errno != EINTR)
|
||||
return ret;
|
||||
|
||||
// recalculate the timeout
|
||||
if (!time_update(&timeout, start, *timeout_ts)) {
|
||||
// timeout during update
|
||||
// or clock reset, fake timeout error
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -66,6 +66,28 @@
|
||||
# include <ioLib.h>
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_NATIVE_POLL
|
||||
# include <poll.h>
|
||||
#else
|
||||
struct pollfd {
|
||||
int fd;
|
||||
short events, revents;
|
||||
};
|
||||
|
||||
typedef unsigned long int nfds_t;
|
||||
|
||||
# define POLLIN 0x001
|
||||
# define POLLPRI 0x002
|
||||
# define POLLOUT 0x004
|
||||
# define POLLERR 0x008
|
||||
# define POLLHUP 0x010
|
||||
# define POLLNVAL 0x020
|
||||
# define POLLRDNORM 0x040
|
||||
# define POLLRDBAND 0x080
|
||||
# define POLLWRNORM 0x100
|
||||
# define POLLWRBAND 0x200
|
||||
#endif
|
||||
|
||||
struct sockaddr;
|
||||
|
||||
#define EINTR_LOOP(var, cmd) \
|
||||
@ -303,6 +325,8 @@ static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options)
|
||||
timespec qt_gettime() Q_DECL_NOTHROW;
|
||||
void qt_nanosleep(timespec amount);
|
||||
|
||||
Q_CORE_EXPORT int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts);
|
||||
|
||||
Q_CORE_EXPORT int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept,
|
||||
const struct timespec *tv);
|
||||
|
||||
|
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…
Reference in New Issue
Block a user