summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qeventdispatcher_unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qeventdispatcher_unix.cpp')
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix.cpp979
1 files changed, 979 insertions, 0 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp
new file mode 100644
index 0000000000..dceb51d181
--- /dev/null
+++ b/src/corelib/kernel/qeventdispatcher_unix.cpp
@@ -0,0 +1,979 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 <private/qthread_p.h>
+#include <private/qcoreapplication_p.h>
+#include <private/qcore_unix_p.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// 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 <pipeDrv.h>
+# include <selectLib.h>
+#endif
+
+#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) || defined(QT_BOOTSTRAPPED)
+# include <sys/times.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+Q_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;
+}
+
+
+#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)
+ // 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(taskIdCurrent));
+
+ // 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
+ 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)
+ // do nothing.
+#elif defined(Q_OS_VXWORKS)
+ close(thread_pipe[0]);
+
+ char name[20];
+ qsnprintf(name, sizeof(name), "/pipe/qt_%08x", int(taskIdCurrent));
+
+ pipeDevDelete(name, true);
+#else
+ // cleanup the common parts of the event loop
+ close(thread_pipe[0]);
+ close(thread_pipe[1]);
+#endif
+
+ // 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[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)) {
+#if defined(Q_OS_VXWORKS)
+ char c[16];
+ ::read(thread_pipe[0], c, sizeof(c));
+ ::ioctl(thread_pipe[0], FIOFLUSH, 0);
+#else
+ 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!");
+ }
+ ++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[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) && !defined(Q_OS_MAC) && !defined(Q_OS_NACL)
+ if (!QElapsedTimer::isMonotonic()) {
+ // not using monotonic timers, initialize the timeChanged() machinery
+ previousTime = qt_gettime();
+
+ 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;
+ }
+#endif
+
+ firstTimerInfo = 0;
+}
+
+timeval QTimerInfoList::updateCurrentTime()
+{
+ return (currentTime = qt_gettime());
+}
+
+#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_INTEGRITY)) || defined(QT_BOOTSTRAPPED)
+
+template <>
+timeval qAbs(const timeval &t)
+{
+ timeval tmp = t;
+ if (tmp.tv_sec < 0) {
+ tmp.tv_sec = -tmp.tv_sec - 1;
+ tmp.tv_usec -= 1000000;
+ }
+ if (tmp.tv_sec == 0 && tmp.tv_usec < 0) {
+ tmp.tv_usec = -tmp.tv_usec;
+ }
+ return normalizedTimeval(tmp);
+}
+
+/*
+ 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)
+{
+#ifdef Q_OS_NACL
+ Q_UNUSED(delta)
+ return false; // Calling "times" crashes.
+#endif
+ struct tms unused;
+ clock_t currentTicks = times(&unused);
+
+ clock_t elapsedTicks = currentTicks - previousTicks;
+ timeval elapsedTime = currentTime - previousTime;
+
+ timeval elapsedTimeTicks;
+ elapsedTimeTicks.tv_sec = elapsedTicks / ticksPerSecond;
+ elapsedTimeTicks.tv_usec = (((elapsedTicks * 1000) / ticksPerSecond) % 1000) * 1000;
+
+ timeval dummy;
+ if (!delta)
+ delta = &dummy;
+ *delta = elapsedTime - elapsedTimeTicks;
+
+ 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.
+ timeval tickGranularity;
+ tickGranularity.tv_sec = 0;
+ tickGranularity.tv_usec = msPerTick * 1000;
+ return elapsedTimeTicks < ((qAbs(*delta) - tickGranularity) * 10);
+}
+
+void QTimerInfoList::repairTimersIfNeeded()
+{
+ if (QElapsedTimer::isMonotonic())
+ return;
+ timeval delta;
+ if (timeChanged(&delta))
+ timerRepair(delta);
+}
+
+#else // !(_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(QT_BOOTSTRAPPED)
+
+void QTimerInfoList::repairTimersIfNeeded()
+{
+}
+
+#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();
+
+ // Find first waiting timer not already active
+ QTimerInfo *t = 0;
+ for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
+ if (!(*it)->activateRef) {
+ t = *it;
+ break;
+ }
+ }
+
+ if (!t)
+ return false;
+
+ 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->activateRef = 0;
+
+ 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) {
+ // found it
+ removeAt(i);
+ if (t == firstTimerInfo)
+ firstTimerInfo = 0;
+ if (t->activateRef)
+ *(t->activateRef) = 0;
+
+ // release the timer id
+ if (!QObjectPrivate::get(t->obj)->inThreadChangeEvent)
+ QAbstractEventDispatcherPrivate::releaseTimerId(timerId);
+
+ 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);
+ if (t->obj == object) {
+ // object found
+ removeAt(i);
+ if (t == firstTimerInfo)
+ firstTimerInfo = 0;
+ if (t->activateRef)
+ *(t->activateRef) = 0;
+
+ // release the timer id
+ if (!QObjectPrivate::get(t->obj)->inThreadChangeEvent)
+ QAbstractEventDispatcherPrivate::releaseTimerId(t->id);
+
+ delete t;
+ // move back one so that we don't skip the new current item
+ --i;
+ }
+ }
+ return true;
+}
+
+QList<QPair<int, int> > QTimerInfoList::registeredTimers(QObject *object) const
+{
+ QList<QPair<int, int> > list;
+ for (int i = 0; i < count(); ++i) {
+ register const QTimerInfo * const t = at(i);
+ if (t->obj == object)
+ list << QPair<int, int>(t->id, t->interval.tv_sec * 1000 + t->interval.tv_usec / 1000);
+ }
+ return list;
+}
+
+/*
+ Activate pending timers, returning how many where activated.
+*/
+int QTimerInfoList::activateTimers()
+{
+ if (qt_disable_lowpriority_timers || isEmpty())
+ return 0; // nothing to do
+
+ int n_act = 0, maxCount = 0;
+ firstTimerInfo = 0;
+
+ timeval currentTime = updateCurrentTime();
+ repairTimersIfNeeded();
+
+
+ // Find out how many timer have expired
+ for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
+ if (currentTime < (*it)->timeout)
+ break;
+ maxCount++;
+ }
+
+ //fire the timers.
+ while (maxCount--) {
+ if (isEmpty())
+ break;
+
+ QTimerInfo *currentTimerInfo = first();
+ if (currentTime < currentTimerInfo->timeout)
+ break; // no timer has expired
+
+ if (!firstTimerInfo) {
+ firstTimerInfo = currentTimerInfo;
+ } else if (firstTimerInfo == currentTimerInfo) {
+ // avoid sending the same timer multiple times
+ break;
+ } else if (currentTimerInfo->interval < firstTimerInfo->interval
+ || currentTimerInfo->interval == firstTimerInfo->interval) {
+ firstTimerInfo = currentTimerInfo;
+ }
+
+ // remove from list
+ removeFirst();
+
+ // determine next timeout time
+ currentTimerInfo->timeout += currentTimerInfo->interval;
+ if (currentTimerInfo->timeout < currentTime)
+ currentTimerInfo->timeout = currentTime + currentTimerInfo->interval;
+
+ // reinsert timer
+ timerInsert(currentTimerInfo);
+ if (currentTimerInfo->interval.tv_usec > 0 || currentTimerInfo->interval.tv_sec > 0)
+ n_act++;
+
+ if (!currentTimerInfo->activateRef) {
+ // send event, but don't allow it to recurse
+ currentTimerInfo->activateRef = &currentTimerInfo;
+
+ QTimerEvent e(currentTimerInfo->id);
+ QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
+
+ if (currentTimerInfo)
+ currentTimerInfo->activateRef = 0;
+ }
+ }
+
+ firstTimerInfo = 0;
+ return n_act;
+}
+
+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,
+ timeval *timeout)
+{
+ return qt_safe_select(nfds, readfds, writefds, exceptfds, timeout);
+}
+
+/*!
+ \internal
+*/
+void QEventDispatcherUNIX::registerTimer(int timerId, int interval, 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, 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::TimerInfo>
+QEventDispatcherUNIX::registeredTimers(QObject *object) const
+{
+ if (!object) {
+ qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
+ return QList<TimerInfo>();
+ }
+
+ 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.
+ timeval *tm = 0;
+ timeval 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_usec = 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();
+}
+
+void QEventDispatcherUNIX::wakeUp()
+{
+ Q_D(QEventDispatcherUNIX);
+ if (d->wakeUps.testAndSetAcquire(0, 1)) {
+ 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()
+{ }
+
+
+
+
+void QCoreApplication::watchUnixSignal(int sig, bool watch)
+{
+ if (sig < NSIG) {
+ struct sigaction sa;
+ sigemptyset(&(sa.sa_mask));
+ sa.sa_flags = 0;
+ if (watch)
+ sa.sa_handler = signalHandler;
+ else
+ sa.sa_handler = SIG_DFL;
+ sigaction(sig, &sa, 0);
+ }
+}
+
+QT_END_NAMESPACE