/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qplatformdefs.h" #include "qcoreapplication.h" #include "qpair.h" #include "qsocketnotifier.h" #include "qthread.h" #include "qelapsedtimer.h" #include "qeventdispatcher_unix_p.h" #include #include #include #include #include #include #ifndef QT_NO_EVENTFD # include #endif // VxWorks doesn't correctly set the _POSIX_... options #if defined(Q_OS_VXWORKS) # if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK <= 0) # undef _POSIX_MONOTONIC_CLOCK # define _POSIX_MONOTONIC_CLOCK 1 # endif # include # include #endif #if (_POSIX_MONOTONIC_CLOCK-0 <= 0) || defined(QT_BOOTSTRAPPED) # include #endif QT_BEGIN_NAMESPACE #if defined(Q_OS_INTEGRITY) || defined(Q_OS_VXWORKS) static void initThreadPipeFD(int fd) { int ret = fcntl(fd, F_SETFD, FD_CLOEXEC); if (ret == -1) perror("QEventDispatcherUNIXPrivate: Unable to init thread pipe"); int flags = fcntl(fd, F_GETFL); if (flags == -1) perror("QEventDispatcherUNIXPrivate: Unable to get flags on thread pipe"); ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); if (ret == -1) perror("QEventDispatcherUNIXPrivate: Unable to set flags on thread pipe"); } #endif QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate() { extern Qt::HANDLE qt_application_thread_id; mainThread = (QThread::currentThreadId() == qt_application_thread_id); bool pipefail = false; // initialize the common parts of the event loop #if defined(Q_OS_NACL) || defined (Q_OS_BLACKBERRY) // do nothing. #elif defined(Q_OS_INTEGRITY) // INTEGRITY doesn't like a "select" on pipes, so use socketpair instead if (socketpair(AF_INET, SOCK_STREAM, 0, thread_pipe) == -1) { perror("QEventDispatcherUNIXPrivate(): Unable to create socket pair"); pipefail = true; } else { initThreadPipeFD(thread_pipe[0]); initThreadPipeFD(thread_pipe[1]); } #elif defined(Q_OS_VXWORKS) char name[20]; qsnprintf(name, sizeof(name), "/pipe/qt_%08x", int(taskIdSelf())); // make sure there is no pipe with this name pipeDevDelete(name, true); // create the pipe if (pipeDevCreate(name, 128 /*maxMsg*/, 1 /*maxLength*/) != OK) { perror("QEventDispatcherUNIXPrivate(): Unable to create thread pipe device"); pipefail = true; } else { if ((thread_pipe[0] = open(name, O_RDWR, 0)) < 0) { perror("QEventDispatcherUNIXPrivate(): Unable to create thread pipe"); pipefail = true; } else { initThreadPipeFD(thread_pipe[0]); thread_pipe[1] = thread_pipe[0]; } } #else # ifndef QT_NO_EVENTFD thread_pipe[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); if (thread_pipe[0] != -1) thread_pipe[1] = -1; else // fall through the next "if" # endif if (qt_safe_pipe(thread_pipe, O_NONBLOCK) == -1) { perror("QEventDispatcherUNIXPrivate(): Unable to create thread pipe"); pipefail = true; } #endif if (pipefail) qFatal("QEventDispatcherUNIXPrivate(): Can not continue without a thread pipe"); sn_highest = -1; interrupt = false; } QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate() { #if defined(Q_OS_NACL) || defined (Q_OS_BLACKBERRY) // do nothing. #elif defined(Q_OS_VXWORKS) close(thread_pipe[0]); char name[20]; qsnprintf(name, sizeof(name), "/pipe/qt_%08x", int(taskIdSelf())); pipeDevDelete(name, true); #else // cleanup the common parts of the event loop close(thread_pipe[0]); if (thread_pipe[1] != -1) close(thread_pipe[1]); #endif // cleanup timers 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() { FD_SET(thread_pipe[0], &sn_vec[0].select_fds); return thread_pipe[0]; } int QEventDispatcherUNIXPrivate::processThreadWakeUp(int nsel) { if (nsel > 0 && FD_ISSET(thread_pipe[0], &sn_vec[0].select_fds)) { // 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) char c[16]; ::read(thread_pipe[0], c, sizeof(c)); ::ioctl(thread_pipe[0], FIOFLUSH, 0); #else # ifndef QT_NO_EVENTFD if (thread_pipe[1] == -1) { // eventfd eventfd_t value; eventfd_read(thread_pipe[0], &value); } else # endif { char c[16]; while (::read(thread_pipe[0], c, sizeof(c)) > 0) { } } #endif if (!wakeUps.testAndSetRelease(1, 0)) { // hopefully, this is dead code qWarning("QEventDispatcherUNIX: internal error, wakeUps.testAndSetRelease(1, 0) failed!"); } return 1; } return 0; } QEventDispatcherUNIX::QEventDispatcherUNIX(QObject *parent) : QAbstractEventDispatcher(*new QEventDispatcherUNIXPrivate, parent) { } QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent) : QAbstractEventDispatcher(dd, parent) { } QEventDispatcherUNIX::~QEventDispatcherUNIX() { Q_D(QEventDispatcherUNIX); d->threadData->eventDispatcher = 0; } 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 */ void QEventDispatcherUNIX::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *obj) { #ifndef QT_NO_DEBUG if (timerId < 1 || interval < 0 || !obj) { qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments"); return; } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { qWarning("QObject::startTimer: timers cannot be started from another thread"); return; } #endif Q_D(QEventDispatcherUNIX); d->timerList.registerTimer(timerId, interval, timerType, obj); } /*! \internal */ bool QEventDispatcherUNIX::unregisterTimer(int timerId) { #ifndef QT_NO_DEBUG if (timerId < 1) { qWarning("QEventDispatcherUNIX::unregisterTimer: invalid argument"); return false; } else if (thread() != QThread::currentThread()) { qWarning("QObject::killTimer: timers cannot be stopped from another thread"); return false; } #endif Q_D(QEventDispatcherUNIX); return d->timerList.unregisterTimer(timerId); } /*! \internal */ bool QEventDispatcherUNIX::unregisterTimers(QObject *object) { #ifndef QT_NO_DEBUG if (!object) { qWarning("QEventDispatcherUNIX::unregisterTimers: invalid argument"); return false; } else if (object->thread() != thread() || thread() != QThread::currentThread()) { qWarning("QObject::killTimers: timers cannot be stopped from another thread"); return false; } #endif Q_D(QEventDispatcherUNIX); return d->timerList.unregisterTimers(object); } QList QEventDispatcherUNIX::registeredTimers(QObject *object) const { if (!object) { qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument"); return QList(); } Q_D(const QEventDispatcherUNIX); return d->timerList.registeredTimers(object); } /***************************************************************************** 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 *****************************************************************************/ void QEventDispatcherUNIX::registerSocketNotifier(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"); return; } else 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); FD_SET(sockfd, fds); d->sn_highest = qMax(d->sn_highest, sockfd); } void QEventDispatcherUNIX::unregisterSocketNotifier(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"); return; } else 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; 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"); return; } Q_ASSERT(notifier->thread() == thread() && thread() == QThread::currentThread()); #endif 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 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); } } 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; // 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; } bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherUNIX); d->interrupt = false; // we are awake, broadcast it emit awake(); QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); int nevents = 0; const bool canWait = (d->threadData->canWait && !d->interrupt && (flags & QEventLoop::WaitForMoreEvents)); if (canWait) emit aboutToBlock(); if (!d->interrupt) { // 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 (!canWait) { if (!tm) tm = &wait_tm; // no time to wait tm->tv_sec = 0l; tm->tv_nsec = 0l; } nevents = d->doSelect(flags, tm); // activate timers if (! (flags & QEventLoop::X11ExcludeTimers)) { nevents += activateTimers(); } } // return true if we handled events, false otherwise return (nevents > 0); } bool QEventDispatcherUNIX::hasPendingEvents() { extern uint qGlobalPostedEventsCount(); // from qapplication.cpp return qGlobalPostedEventsCount(); } int QEventDispatcherUNIX::remainingTime(int timerId) { #ifndef QT_NO_DEBUG if (timerId < 1) { qWarning("QEventDispatcherUNIX::remainingTime: invalid argument"); return -1; } #endif Q_D(QEventDispatcherUNIX); return d->timerList.timerRemainingTime(timerId); } void QEventDispatcherUNIX::wakeUp() { Q_D(QEventDispatcherUNIX); if (d->wakeUps.testAndSetAcquire(0, 1)) { #ifndef QT_NO_EVENTFD if (d->thread_pipe[1] == -1) { // eventfd eventfd_t value = 1; int ret; EINTR_LOOP(ret, eventfd_write(d->thread_pipe[0], value)); return; } #endif char c = 0; qt_safe_write( d->thread_pipe[1], &c, 1 ); } } void QEventDispatcherUNIX::interrupt() { Q_D(QEventDispatcherUNIX); d->interrupt = true; wakeUp(); } void QEventDispatcherUNIX::flush() { } QT_END_NAMESPACE