📄 qeventdispatcher_unix.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 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://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** 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 <private/qcoreapplication_p.h>#include <errno.h>#include <stdio.h>#include <stdlib.h>#if (_POSIX_MONOTONIC_CLOCK-0 <= 0)# include <sys/times.h>#endifQ_CORE_EXPORT bool qt_disable_lowpriority_timers=false;/***************************************************************************** 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;}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");}QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate(){ extern Qt::HANDLE qt_application_thread_id; mainThread = (QThread::currentThreadId() == qt_application_thread_id); // initialize the common parts of the event loop#ifdef Q_OS_INTEGRITY // INTEGRITY doesn't like a "select" on pipes, so use socketpair instead if (socketpair(AF_INET, SOCK_STREAM, PF_INET, thread_pipe) == -1) perror("QEventDispatcherUNIXPrivate(): Unable to create socket pair");#else if (pipe(thread_pipe) == -1) perror("QEventDispatcherUNIXPrivate(): Unable to create thread pipe");#endif initThreadPipeFD(thread_pipe[0]); initThreadPipeFD(thread_pipe[1]); sn_highest = -1; interrupt = false;}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); // needed in QEventDispatcherUNIX::select() timerList.updateCurrentTime(); 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; 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); } 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) { QSockNotType::List &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) ; if (!wakeUps.testAndSetRelease(1, 0)) { // hopefully, this is dead code qWarning("QEventDispatcherUNIX: internal error, wakeUps.testAndSetRelease(1, 0) failed!"); } ++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++) { QSockNotType::List &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. */QTimerInfoList::QTimerInfoList(){#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) useMonotonicTimers = false;# if (_POSIX_MONOTONIC_CLOCK == 0) // detect if the system support monotonic timers long x = sysconf(_SC_MONOTONIC_CLOCK); useMonotonicTimers = x >= 200112L;# endif getTime(currentTime); if (!useMonotonicTimers) { // not using monotonic timers, initialize the timeChanged() machinery previousTime = currentTime; tms unused; previousTicks = times(&unused); ticksPerSecond = sysconf(_SC_CLK_TCK); msPerTick = 1000/ticksPerSecond; } else { // detected monotonic timers previousTime.tv_sec = previousTime.tv_usec = 0; previousTicks = 0; ticksPerSecond = 0; msPerTick = 0; }#else // using monotonic timers unconditionally getTime(currentTime);#endif firstTimerInfo = currentTimerInfo = 0;}timeval QTimerInfoList::updateCurrentTime(){ getTime(currentTime); return currentTime;}#if (_POSIX_MONOTONIC_CLOCK-0 > 0)void QTimerInfoList::getTime(timeval &t){ timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); t.tv_sec = ts.tv_sec; t.tv_usec = ts.tv_nsec / 1000;}void QTimerInfoList::repairTimersIfNeeded(){}#else/* Returns true if the real time clock has changed by more than 10% relative to the processor time since the last time this function was called. This presumably means that the system time has been changed. If /a delta is nonzero, delta is set to our best guess at how much the system clock was changed.*/bool QTimerInfoList::timeChanged(timeval *delta){ tms unused; clock_t currentTicks = times(&unused); int elapsedTicks = currentTicks - previousTicks; timeval elapsedTime = currentTime - previousTime; int elapsedMsecTicks = (elapsedTicks * 1000) / ticksPerSecond; int deltaMsecs = (elapsedTime.tv_sec * 1000 + elapsedTime.tv_usec / 1000) - elapsedMsecTicks; if (delta) { delta->tv_sec = deltaMsecs / 1000; delta->tv_usec = (deltaMsecs % 1000) * 1000; } previousTicks = currentTicks; previousTime = currentTime; // If tick drift is more than 10% off compared to realtime, we assume that the clock has // been set. Of course, we have to allow for the tick granularity as well. return (qAbs(deltaMsecs) - msPerTick) * 10 > elapsedMsecTicks;}void QTimerInfoList::getTime(timeval &t){#if !defined(QT_NO_CLOCK_MONOTONIC) if (useMonotonicTimers) { timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); t.tv_sec = ts.tv_sec; t.tv_usec = ts.tv_nsec / 1000; return; }#endif 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; } }}void QTimerInfoList::repairTimersIfNeeded(){ if (useMonotonicTimers) return; timeval delta; if (timeChanged(&delta)) timerRepair(delta);}#endif/* insert timer info into list*/void QTimerInfoList::timerInsert(QTimerInfo *ti){ int index = size(); while (index--) { register const QTimerInfo * const t = at(index); if (!(ti->timeout < t->timeout)) break; } insert(index+1, ti);}/* repair broken timer*/void QTimerInfoList::timerRepair(const timeval &diff){ // repair all timers for (int i = 0; i < size(); ++i) { register QTimerInfo *t = at(i); t->timeout = t->timeout - diff; }}/* Returns the time to wait for the next timer, or null if no timers are waiting.*/bool QTimerInfoList::timerWait(timeval &tm){ timeval currentTime = updateCurrentTime(); repairTimersIfNeeded(); if (isEmpty()) return false; QTimerInfo *t = 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;}void QTimerInfoList::registerTimer(int timerId, int interval, QObject *object){ QTimerInfo *t = new QTimerInfo; t->id = timerId; t->interval.tv_sec = interval / 1000; t->interval.tv_usec = (interval % 1000) * 1000; t->timeout = updateCurrentTime() + t->interval; t->obj = object; t->inTimerEvent = false; timerInsert(t);}bool QTimerInfoList::unregisterTimer(int timerId){ // set timer inactive for (int i = 0; i < count(); ++i) { register QTimerInfo *t = at(i); if (t->id == timerId) { removeAt(i); if (t == firstTimerInfo) firstTimerInfo = 0; if (t == currentTimerInfo) currentTimerInfo = 0; delete t; return true; } } // id not found return false;}bool QTimerInfoList::unregisterTimers(QObject *object){ if (isEmpty()) return false; for (int i = 0; i < count(); ++i) { register QTimerInfo *t = at(i);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -