/**************************************************************************** | |
** | |
** 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$ | |
** GNU Lesser General Public License Usage | |
** 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. | |
** | |
** 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. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $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_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"); | |
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_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) | |
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(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) | |
{ | |
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 = ¤tTimerInfo; | |
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 |