📄 qeventdispatcher_unix.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the QtCore module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.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 GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qplatformdefs.h"#include "qcoreapplication.h"#include "qpair.h"#include "qsocketnotifier.h"#include "qthread.h"#include "qeventdispatcher_unix_p.h"#include <private/qthread_p.h>#include <errno.h>#include <stdio.h>#include <stdlib.h>Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false;// Internal operator functions for timevalsstatic inline bool operator<(const timeval &t1, const timeval &t2){ return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_usec < t2.tv_usec); }static inline bool operator==(const timeval &t1, const timeval &t2){ return t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec; }static inline timeval &operator+=(timeval &t1, const timeval &t2){ t1.tv_sec += t2.tv_sec; if ((t1.tv_usec += t2.tv_usec) >= 1000000l) { ++t1.tv_sec; t1.tv_usec -= 1000000l; } return t1;}static inline timeval operator+(const timeval &t1, const timeval &t2){ timeval tmp; tmp.tv_sec = t1.tv_sec + t2.tv_sec; if ((tmp.tv_usec = t1.tv_usec + t2.tv_usec) >= 1000000l) { ++tmp.tv_sec; tmp.tv_usec -= 1000000l; } return tmp;}static inline timeval operator-(const timeval &t1, const timeval &t2){ timeval tmp; tmp.tv_sec = t1.tv_sec - t2.tv_sec; if ((tmp.tv_usec = t1.tv_usec - t2.tv_usec) < 0l) { --tmp.tv_sec; tmp.tv_usec += 1000000l; } return tmp;}// get time of daystatic inline void getTime(timeval &t){ gettimeofday(&t, 0); // NTP-related fix while (t.tv_usec >= 1000000l) { t.tv_usec -= 1000000l; ++t.tv_sec; } while (t.tv_usec < 0l) { if (t.tv_sec > 0l) { t.tv_usec += 1000000l; --t.tv_sec; } else { t.tv_usec = 0l; break; } }}struct QTimerInfo { int id; // - timer identifier timeval interval; // - timer interval timeval timeout; // - when to sent event QObject *obj; // - object to receive event};/***************************************************************************** UNIX signal handling *****************************************************************************/static sig_atomic_t signal_received;static sig_atomic_t signals_fired[NSIG];static void signalHandler(int sig){ signals_fired[sig] = 1; signal_received = 1;}QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate(){ extern Qt::HANDLE qt_application_thread_id; mainThread = (QThread::currentThreadId() == qt_application_thread_id); // initialize the common parts of the event loop pipe(thread_pipe); fcntl(thread_pipe[0], F_SETFD, FD_CLOEXEC); fcntl(thread_pipe[1], F_SETFD, FD_CLOEXEC); fcntl(thread_pipe[0], F_SETFL, fcntl(thread_pipe[0], F_GETFL) | O_NONBLOCK); fcntl(thread_pipe[1], F_SETFL, fcntl(thread_pipe[1], F_GETFL) | O_NONBLOCK); sn_highest = -1; interrupt = false; getTime(watchtime);}QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate(){ // cleanup the common parts of the event loop close(thread_pipe[0]); close(thread_pipe[1]); // cleanup timers qDeleteAll(timerList);}int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags, timeval *timeout){ Q_Q(QEventDispatcherUNIX); int nsel; do { if (mainThread) { while (signal_received) { signal_received = 0; for (int i = 0; i < NSIG; ++i) { if (signals_fired[i]) { signals_fired[i] = 0; emit QCoreApplication::instance()->unixSignal(i); } } } } // Process timers and socket notifiers - the common UNIX stuff int highest = 0; FD_ZERO(&sn_vec[0].select_fds); FD_ZERO(&sn_vec[1].select_fds); FD_ZERO(&sn_vec[2].select_fds); if (! (flags & QEventLoop::ExcludeSocketNotifiers) && (sn_highest >= 0)) { // return the highest fd we can wait for input on if (!sn_vec[0].list.isEmpty()) sn_vec[0].select_fds = sn_vec[0].enabled_fds; if (!sn_vec[1].list.isEmpty()) sn_vec[1].select_fds = sn_vec[1].enabled_fds; if (!sn_vec[2].list.isEmpty()) sn_vec[2].select_fds = sn_vec[2].enabled_fds; highest = sn_highest; } FD_SET(thread_pipe[0], &sn_vec[0].select_fds); highest = qMax(highest, thread_pipe[0]); 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) { QList<QSockNot *> &list = sn_vec[type].list; if (list.size() == 0) continue; for (int i = 0; i < list.size(); ++i) { QSockNot *sn = list.at(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"); } } // some other thread woke us up... consume the data on the thread pipe so that // select doesn't immediately return next time int nevents = 0; if (nsel > 0 && FD_ISSET(thread_pipe[0], &sn_vec[0].select_fds)) { char c[16]; while (::read(thread_pipe[0], c, sizeof(c)) > 0) ; wakeUps = 0; ++nevents; } // 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++) { QList<QSockNot *> &list = sn_vec[i].list; for (int j = 0; j < list.size(); ++j) { QSockNot *sn = list.at(j); if (FD_ISSET(sn->fd, &sn_vec[i].select_fds)) q->setSocketNotifierPending(sn->obj); } } } return (nevents + q->activateSocketNotifiers());}/* * Internal functions for manipulating timer data structures. The * timerBitVec array is used for keeping track of timer identifiers. *//* insert timer info into list*/ void QEventDispatcherUNIXPrivate::timerInsert(QTimerInfo *ti){ int index = 0;#if defined(QT_DEBUG) int dangerCount = 0;#endif for (; index < timerList.size(); ++index) { register const QTimerInfo * const t = timerList.at(index);#if defined(QT_DEBUG) if (t->obj == ti->obj) ++dangerCount;#endif if (ti->timeout < t->timeout) break; } timerList.insert(index, ti);#if defined(QT_DEBUG) if (dangerCount > 16) qDebug("QObject: %d timers now exist for object %s::%s", dangerCount, ti->obj->metaObject()->className(), ti->obj->objectName().toLocal8Bit().data());#endif}/* repair broken timer*/void QEventDispatcherUNIXPrivate::timerRepair(const timeval &time){ timeval diff = watchtime - time; // repair all timers for (int i = 0; i < timerList.size(); ++i) { register QTimerInfo *t = timerList.at(i); t->timeout = t->timeout - diff; }}/* Returns the time to wait for the next timer, or null if no timers are waiting.*/bool QEventDispatcherUNIXPrivate::timerWait(timeval &tm){ timeval currentTime; getTime(currentTime); if (currentTime < watchtime) // clock was turned back timerRepair(currentTime); watchtime = currentTime; if (timerList.isEmpty()) return false; QTimerInfo *t = timerList.first(); // first waiting timer if (currentTime < t->timeout) { // time to wait tm = t->timeout - currentTime; } else { // no time to wait tm.tv_sec = 0; tm.tv_usec = 0; } return true;}QEventDispatcherUNIX::QEventDispatcherUNIX(QObject *parent) : QAbstractEventDispatcher(*new QEventDispatcherUNIXPrivate, parent){ }QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent) : QAbstractEventDispatcher(dd, parent){ }QEventDispatcherUNIX::~QEventDispatcherUNIX(){ }int QEventDispatcherUNIX::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, timeval *timeout){ Q_D(QEventDispatcherUNIX); if (timeout) { // handle the case where select returns with a timeout, too // soon. timeval tvStart = d->watchtime; timeval tvCurrent = tvStart; timeval originalTimeout = *timeout; int nsel; do { timeval tvRest = originalTimeout + tvStart - tvCurrent; nsel = ::select(nfds, readfds, writefds, exceptfds, &tvRest); getTime(tvCurrent); } while (nsel == 0 && (tvCurrent - tvStart) < originalTimeout); return nsel; } return ::select(nfds, readfds, writefds, exceptfds, timeout);}/*! \internal*/void QEventDispatcherUNIX::registerTimer(int timerId, int interval, QObject *obj){ 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; } QTimerInfo *t = new QTimerInfo; // create timer t->id = timerId; t->interval.tv_sec = interval / 1000; t->interval.tv_usec = (interval % 1000) * 1000; timeval currentTime; getTime(currentTime); t->timeout = currentTime + t->interval; t->obj = obj; Q_D(QEventDispatcherUNIX); d->timerInsert(t); // put timer in list}/*! \internal*/bool QEventDispatcherUNIX::unregisterTimer(int timerId){ 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; } Q_D(QEventDispatcherUNIX); // set timer inactive for (int i = 0; i < d->timerList.size(); ++i) { register QTimerInfo *t = d->timerList.at(i); if (t->id == timerId) { d->timerList.removeAt(i); delete t; return true; } } // id not found return false;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -