diff options
Diffstat (limited to 'src/corelib/kernel/qeventdispatcher_symbian.cpp')
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_symbian.cpp | 1316 |
1 files changed, 0 insertions, 1316 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp deleted file mode 100644 index 1d7ecce9ae..0000000000 --- a/src/corelib/kernel/qeventdispatcher_symbian.cpp +++ /dev/null @@ -1,1316 +0,0 @@ -/**************************************************************************** -** -** 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 "qeventdispatcher_symbian_p.h" -#include <private/qthread_p.h> -#include <qcoreapplication.h> -#include <private/qcoreapplication_p.h> -#include <qsemaphore.h> - -#include <unistd.h> -#include <errno.h> - -QT_BEGIN_NAMESPACE - -#ifdef SYMBIAN_GRAPHICS_WSERV_QT_EFFECTS -// when the system UI is Qt based, priority drop is not needed as CPU starved processes will not be killed. -#undef QT_SYMBIAN_PRIORITY_DROP -#else -#define QT_SYMBIAN_PRIORITY_DROP -#endif - -#define WAKE_UP_PRIORITY CActive::EPriorityStandard -#define TIMER_PRIORITY CActive::EPriorityHigh -#define NULLTIMER_PRIORITY CActive::EPriorityLow -#define COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY CActive::EPriorityIdle - -static inline int qt_pipe_write(int socket, const char *data, qint64 len) -{ - return ::write(socket, data, len); -} -#if defined(write) -# undef write -#endif - -static inline int qt_pipe_close(int socket) -{ - return ::close(socket); -} -#if defined(close) -# undef close -#endif - -static inline int qt_pipe_fcntl(int socket, int command) -{ - return ::fcntl(socket, command); -} -static inline int qt_pipe2_fcntl(int socket, int command, int option) -{ - return ::fcntl(socket, command, option); -} -#if defined(fcntl) -# undef fcntl -#endif - -static inline int qt_socket_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) -{ - return ::select(nfds, readfds, writefds, exceptfds, timeout); -} - -// This simply interrupts the select and locks the mutex until destroyed. -class QSelectMutexGrabber -{ -public: - QSelectMutexGrabber(int writeFd, int readFd, QMutex *mutex) - : m_mutex(mutex) - { - if (m_mutex->tryLock()) - return; - - char dummy = 0; - qt_pipe_write(writeFd, &dummy, 1); - - m_mutex->lock(); - - char buffer; - while (::read(readFd, &buffer, 1) > 0) {} - } - - ~QSelectMutexGrabber() - { - m_mutex->unlock(); - } - -private: - QMutex *m_mutex; -}; - -/* - * This class is designed to aid in implementing event handling in a more round robin fashion. We - * cannot change active objects that we do not own, but the active objects that Qt owns will use - * this as a base class with convenience functions. - * - * Here is how it works: On every RunL, the deriving class should call maybeQueueForLater(). - * This will return whether the active object has been queued, or whether it should run immediately. - * Queued objects will run again after other events have been processed. - * - * The QCompleteDeferredAOs class is a special object that runs after all others, which will - * reactivate the objects that were previously not run. - */ -QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher) - : CActive(priority), - m_dispatcher(dispatcher), - m_hasAlreadyRun(false), - m_hasRunAgain(false), - m_iterationCount(1) -{ -} - -QActiveObject::~QActiveObject() -{ - if (m_hasRunAgain) - m_dispatcher->removeDeferredActiveObject(this); -} - -bool QActiveObject::maybeQueueForLater() -{ - Q_ASSERT(!m_hasRunAgain); - - if (!m_hasAlreadyRun || m_dispatcher->iterationCount() != m_iterationCount) { - // First occurrence of this event in this iteration. - m_hasAlreadyRun = true; - m_iterationCount = m_dispatcher->iterationCount(); - return false; - } else { - // The event has already occurred. - m_dispatcher->addDeferredActiveObject(this); - m_hasRunAgain = true; - return true; - } -} - -bool QActiveObject::maybeDeferSocketEvent() -{ - Q_ASSERT(!m_hasRunAgain); - Q_ASSERT(m_dispatcher); - if (!m_dispatcher->areSocketEventsBlocked()) { - return false; - } - m_hasRunAgain = true; - m_dispatcher->addDeferredSocketActiveObject(this); - return true; -} - -void QActiveObject::reactivateAndComplete() -{ - TInt error = iStatus.Int(); - iStatus = KRequestPending; - SetActive(); - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, error); - - m_hasRunAgain = false; - m_hasAlreadyRun = false; -} - -QWakeUpActiveObject::QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher) - : QActiveObject(WAKE_UP_PRIORITY, dispatcher) -{ - m_hostThreadId = RThread().Id(); - CActiveScheduler::Add(this); - iStatus = KRequestPending; - SetActive(); -} - -QWakeUpActiveObject::~QWakeUpActiveObject() -{ - Cancel(); -} - -void QWakeUpActiveObject::DoCancel() -{ - if (iStatus.Int() == KRequestPending) { - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, KErrNone); - } else if (IsActive() && m_hostThreadId != RThread().Id()) { - // This is being cancelled in the adopted monitor thread, which can happen if an adopted thread with - // an event loop has exited. The event loop creates an event dispatcher with this active object, which may be complete but not run on exit. - // We force a cancellation in this thread, because a) the object cannot be deleted while active and b) without a cancellation - // the thread semaphore will be one count down. - // It is possible for this problem to affect other active objects. They symptom would be that finished signals - // from adopted threads are not sent, or they arrive much later than they should. - TRequestStatus *status = &iStatus; - User::RequestComplete(status, KErrNone); - } -} - -void QWakeUpActiveObject::RunL() -{ - if (maybeQueueForLater()) - return; - - iStatus = KRequestPending; - SetActive(); - QT_TRYCATCH_LEAVING(m_dispatcher->wakeUpWasCalled()); -} - -QTimerActiveObject::QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo) - : QActiveObject((timerInfo->interval) ? TIMER_PRIORITY : NULLTIMER_PRIORITY , dispatcher), - m_timerInfo(timerInfo), m_expectedTimeSinceLastEvent(0) -{ - // start the timeout timer to ensure initialisation - m_timeoutTimer.start(); -} - -QTimerActiveObject::~QTimerActiveObject() -{ - Cancel(); - m_rTimer.Close(); //close of null handle is safe -} - -void QTimerActiveObject::DoCancel() -{ - if (m_timerInfo->interval > 0) { - m_rTimer.Cancel(); - } else { - if (iStatus.Int() == KRequestPending) { - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, KErrNone); - } - } -} - -void QTimerActiveObject::RunL() -{ - int error = KErrNone; - if (iStatus == KErrNone) { - QT_TRYCATCH_ERROR(error, Run()); - } else { - error = iStatus.Int(); - } - // All Symbian error codes are negative. - if (error < 0) { - CActiveScheduler::Current()->Error(error); // stop and report here, as this timer will be deleted on scope exit - } -} - -#define MAX_SYMBIAN_TIMEOUT_MS 2000000 -void QTimerActiveObject::StartTimer() -{ - if (m_timerInfo->msLeft > MAX_SYMBIAN_TIMEOUT_MS) { - //There is loss of accuracy anyway due to needing to restart the timer every 33 minutes, - //so the 1/64s res of After() is acceptable for these very long timers. - m_rTimer.After(iStatus, MAX_SYMBIAN_TIMEOUT_MS * 1000); - m_timerInfo->msLeft -= MAX_SYMBIAN_TIMEOUT_MS; - } else { - // this algorithm implements drift correction for repeating timers - // calculate how late we are for this event - int timeSinceLastEvent = m_timeoutTimer.restart(); - int overshoot = timeSinceLastEvent - m_expectedTimeSinceLastEvent; - if (overshoot > m_timerInfo->msLeft) { - // we skipped a whole timeout, restart from here - overshoot = 0; - } - // calculate when the next event should happen - int waitTime = m_timerInfo->msLeft - overshoot; - m_expectedTimeSinceLastEvent = waitTime; - // limit the actual ms wait time to avoid wild corrections - // this will cause the real event time to slowly drift back to the expected event time - // measurements show that Symbian timers always fire 1 or 2 ms late - const int limit = 4; - waitTime = qMax(m_timerInfo->msLeft - limit, waitTime); - m_rTimer.HighRes(iStatus, waitTime * 1000); - m_timerInfo->msLeft = 0; - } - SetActive(); -} - -void QTimerActiveObject::Run() -{ - //restart timer immediately, if the timeout has been split because it overflows max for platform. - if (m_timerInfo->msLeft > 0) { - StartTimer(); - return; - } - - if (maybeQueueForLater()) - return; - - if (m_timerInfo->interval > 0) { - // Start a new timer immediately so that we don't lose time. - m_timerInfo->msLeft = m_timerInfo->interval; - StartTimer(); - - m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId); - } else { - // However, we only complete zero timers after the event has finished, - // in order to prevent busy looping when doing nested loops. - - // Keep the refpointer around in order to avoid deletion until the end of this function. - SymbianTimerInfoPtr timerInfoPtr(m_timerInfo); - - m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId); - - iStatus = KRequestPending; - SetActive(); - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, KErrNone); - } -} - -void QTimerActiveObject::Start() -{ - CActiveScheduler::Add(this); - m_timerInfo->msLeft = m_timerInfo->interval; - if (m_timerInfo->interval > 0) { - if (!m_rTimer.Handle()) { - qt_symbian_throwIfError(m_rTimer.CreateLocal()); - } - m_timeoutTimer.start(); - m_expectedTimeSinceLastEvent = 0; - StartTimer(); - } else { - iStatus = KRequestPending; - SetActive(); - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, KErrNone); - } -} - -SymbianTimerInfo::SymbianTimerInfo() - : timerAO(0) -{ -} - -SymbianTimerInfo::~SymbianTimerInfo() -{ - delete timerAO; -} - -QCompleteDeferredAOs::QCompleteDeferredAOs(QEventDispatcherSymbian *dispatcher) - : CActive(COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY), - m_dispatcher(dispatcher) -{ - CActiveScheduler::Add(this); - iStatus = KRequestPending; - SetActive(); -} - -QCompleteDeferredAOs::~QCompleteDeferredAOs() -{ - Cancel(); -} - -void QCompleteDeferredAOs::complete() -{ - if (iStatus.Int() == KRequestPending) { - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, KErrNone); - } -} - -void QCompleteDeferredAOs::DoCancel() -{ - if (iStatus.Int() == KRequestPending) { - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, KErrNone); - } -} - -void QCompleteDeferredAOs::RunL() -{ - iStatus = KRequestPending; - SetActive(); - - QT_TRYCATCH_LEAVING(m_dispatcher->reactivateDeferredActiveObjects()); -} - -QSelectThread::QSelectThread() - : m_quit(false) -{ - if (::pipe(m_pipeEnds) != 0) { - qWarning("Select thread was unable to open a pipe, errno: %i", errno); - } else { - int flags0 = qt_pipe_fcntl(m_pipeEnds[0], F_GETFL); - int flags1 = qt_pipe_fcntl(m_pipeEnds[1], F_GETFL); - // We should check the error code here, but Open C has a bug that returns - // failure even though the operation was successful. - qt_pipe2_fcntl(m_pipeEnds[0], F_SETFL, flags0 | O_NONBLOCK); - qt_pipe2_fcntl(m_pipeEnds[1], F_SETFL, flags1 | O_NONBLOCK); - } -} - -QSelectThread::~QSelectThread() -{ - qt_pipe_close(m_pipeEnds[1]); - qt_pipe_close(m_pipeEnds[0]); -} - -void QSelectThread::run() -{ - Q_D(QThread); - - m_mutex.lock(); - - while (!m_quit) { - fd_set readfds; - fd_set writefds; - fd_set exceptionfds; - - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&exceptionfds); - - int maxfd = 0; - maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Read, &readfds)); - maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Write, &writefds)); - maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Exception, &exceptionfds)); - maxfd = qMax(maxfd, m_pipeEnds[0]); - maxfd++; - - FD_SET(m_pipeEnds[0], &readfds); - - int ret; - int savedSelectErrno; - ret = qt_socket_select(maxfd, &readfds, &writefds, &exceptionfds, 0); - savedSelectErrno = errno; - - if(ret == 0) { - // do nothing - } else if (ret < 0) { - switch (savedSelectErrno) { - case EBADF: - case EINVAL: - case ENOMEM: - case EFAULT: - qWarning("::select() returned an error: %i", savedSelectErrno); - break; - case ECONNREFUSED: - case EPIPE: - qWarning("::select() returned an error: %i (go through sockets)", savedSelectErrno); - // prepare to go through all sockets - // mark in fd sets both: - // good ones - // ones that return -1 in select - // after loop update notifiers for all of them - - // as we don't have "exception" notifier type - // we should force monitoring fd_set of this - // type as well - - // clean @ start - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&exceptionfds); - for (QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin(); - i != m_AOStatuses.end(); ++i) { - - fd_set onefds; - FD_ZERO(&onefds); - FD_SET(i.key()->socket(), &onefds); - - fd_set excfds; - FD_ZERO(&excfds); - FD_SET(i.key()->socket(), &excfds); - - maxfd = i.key()->socket() + 1; - - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - ret = 0; - - if(i.key()->type() == QSocketNotifier::Read) { - ret = ::select(maxfd, &onefds, 0, &excfds, &timeout); - if(ret != 0) FD_SET(i.key()->socket(), &readfds); - } else if(i.key()->type() == QSocketNotifier::Write) { - ret = ::select(maxfd, 0, &onefds, &excfds, &timeout); - if(ret != 0) FD_SET(i.key()->socket(), &writefds); - } - - } // end for - - // traversed all, so update - updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds); - updateActivatedNotifiers(QSocketNotifier::Read, &readfds); - updateActivatedNotifiers(QSocketNotifier::Write, &writefds); - - break; - case EINTR: // Should never occur on Symbian, but this is future proof! - default: - qWarning("::select() returned an unknown error: %i", savedSelectErrno); - - break; - } - } else { - updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds); - updateActivatedNotifiers(QSocketNotifier::Read, &readfds); - updateActivatedNotifiers(QSocketNotifier::Write, &writefds); - } - - if (FD_ISSET(m_pipeEnds[0], &readfds)) - m_waitCond.wait(&m_mutex); - } - - m_mutex.unlock(); -} - -void QSelectThread::requestSocketEvents ( QSocketNotifier *notifier, TRequestStatus *status ) -{ - Q_D(QThread); - - if (!isRunning()) { - start(); - } - - Q_ASSERT(QThread::currentThread() == this->thread()); - - QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex); - - Q_ASSERT(!m_AOStatuses.contains(notifier)); - - m_AOStatuses.insert(notifier, status); - - m_waitCond.wakeAll(); -} - -void QSelectThread::cancelSocketEvents ( QSocketNotifier *notifier ) -{ - Q_ASSERT(QThread::currentThread() == this->thread()); - - QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex); - - m_AOStatuses.remove(notifier); - - m_waitCond.wakeAll(); -} - -void QSelectThread::restart() -{ - Q_ASSERT(QThread::currentThread() == this->thread()); - - QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex); - - m_waitCond.wakeAll(); -} - -int QSelectThread::updateSocketSet(QSocketNotifier::Type type, fd_set *fds) -{ - int maxfd = 0; - if(m_AOStatuses.isEmpty()) { - /* - * Wonder if should return -1 - * to signal that no descriptors - * added to fds - */ - return maxfd; - } - for ( QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin(); - i != m_AOStatuses.end(); ++i) { - if (i.key()->type() == type) { - FD_SET(i.key()->socket(), fds); - maxfd = qMax(maxfd, i.key()->socket()); - } else if(type == QSocketNotifier::Exception) { - /* - * We are registering existing sockets - * always to exception set - * - * Doing double FD_SET shouldn't - * matter - */ - FD_SET(i.key()->socket(), fds); - maxfd = qMax(maxfd, i.key()->socket()); - } - } - - return maxfd; -} - -void QSelectThread::updateActivatedNotifiers(QSocketNotifier::Type type, fd_set *fds) -{ - Q_D(QThread); - if(m_AOStatuses.isEmpty()) { - return; - } - QList<QSocketNotifier *> toRemove; - for (QHash<QSocketNotifier *, TRequestStatus *>::const_iterator i = m_AOStatuses.begin(); - i != m_AOStatuses.end(); ++i) { - if (i.key()->type() == type && FD_ISSET(i.key()->socket(), fds)) { - toRemove.append(i.key()); - TRequestStatus *status = i.value(); - // Thread data is still owned by the main thread. - QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone); - } else if(type == QSocketNotifier::Exception && FD_ISSET(i.key()->socket(), fds)) { - /* - * check if socket is in exception set - * then signal RequestComplete for it - */ - qWarning("exception on %d [will close the socket handle - hack]", i.key()->socket()); - // quick fix; there is a bug - // when doing read on socket - // errors not preoperly mapped - // after offline-ing the device - // on some devices we do get exception - ::close(i.key()->socket()); - toRemove.append(i.key()); - TRequestStatus *status = i.value(); - QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone); - } - } - - for (int c = 0; c < toRemove.size(); ++c) { - m_AOStatuses.remove(toRemove[c]); - } -} - -void QSelectThread::stop() -{ - m_quit = true; - restart(); - wait(); -} - -QSocketActiveObject::QSocketActiveObject(QEventDispatcherSymbian *dispatcher, QSocketNotifier *notifier) - : QActiveObject(CActive::EPriorityStandard, dispatcher), - m_notifier(notifier), - m_inSocketEvent(false), - m_deleteLater(false) -{ - CActiveScheduler::Add(this); - iStatus = KRequestPending; - SetActive(); -} - -QSocketActiveObject::~QSocketActiveObject() -{ - Cancel(); -} - -void QSocketActiveObject::DoCancel() -{ - if (iStatus.Int() == KRequestPending) { - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, KErrNone); - } -} - -void QSocketActiveObject::RunL() -{ - if (maybeDeferSocketEvent()) - return; - if (maybeQueueForLater()) - return; - - QT_TRYCATCH_LEAVING(run()); -} - -void QSocketActiveObject::run() -{ - QEvent e(QEvent::SockAct); - m_inSocketEvent = true; - QCoreApplication::sendEvent(m_notifier, &e); - m_inSocketEvent = false; - - if (m_deleteLater) { - delete this; - } else { - iStatus = KRequestPending; - SetActive(); - m_dispatcher->reactivateSocketNotifier(m_notifier); - } -} - -void QSocketActiveObject::deleteLater() -{ - if (m_inSocketEvent) { - m_deleteLater = true; - } else { - delete this; - } -} - -#ifdef QT_SYMBIAN_PRIORITY_DROP -class QIdleDetectorThread -{ -public: - QIdleDetectorThread() - : m_state(STATE_RUN), m_stop(false), m_running(false) - { - start(); - } - - ~QIdleDetectorThread() - { - stop(); - } - - void start() - { - QMutexLocker lock(&m_mutex); - if (m_running) - return; - m_stop = false; - m_state = STATE_RUN; - TInt err = m_idleDetectorThread.Create(KNullDesC(), &idleDetectorThreadFunc, 1024, &User::Allocator(), this); - if (err != KErrNone) - return; // Fail silently on error. Next kick will try again. Exception might stop the event being processed - m_idleDetectorThread.SetPriority(EPriorityAbsoluteBackgroundNormal); - m_idleDetectorThread.Resume(); - m_running = true; - // get a callback from QCoreApplication destruction to stop this thread - qAddPostRoutine(StopIdleDetectorThread); - } - - void stop() - { - QMutexLocker lock(&m_mutex); - if (!m_running) - return; - // close down the idle thread because if corelib is loaded temporarily, this would leak threads into the host process - m_stop = true; - m_kick.release(); - m_idleDetectorThread.SetPriority(EPriorityNormal); - TRequestStatus s; - m_idleDetectorThread.Logon(s); - User::WaitForRequest(s); - m_idleDetectorThread.Close(); - m_running = false; - } - - void kick() - { - start(); - m_state = STATE_KICKED; - m_kick.release(); - } - - bool hasRun() - { - return m_state == STATE_RUN; - } - -private: - static TInt idleDetectorThreadFunc(TAny* self) - { - User::RenameThread(_L("IdleDetectorThread")); - static_cast<QIdleDetectorThread*>(self)->IdleLoop(); - return KErrNone; - } - - void IdleLoop() - { - while (!m_stop) { - m_kick.acquire(); - m_state = STATE_RUN; - } - } - - static void StopIdleDetectorThread(); - -private: - enum IdleStates {STATE_KICKED, STATE_RUN} m_state; - bool m_stop; - bool m_running; - RThread m_idleDetectorThread; - QSemaphore m_kick; - QMutex m_mutex; -}; - -Q_GLOBAL_STATIC(QIdleDetectorThread, idleDetectorThread); - -void QIdleDetectorThread::StopIdleDetectorThread() -{ - idleDetectorThread()->stop(); -} - -const int maxBusyTime = 2000; // maximum time we allow idle detector to be blocked before worrying, in milliseconds -const int baseDelay = 1000; // minimum delay time used when backing off to allow idling, in microseconds -#endif - -QEventDispatcherSymbian::QEventDispatcherSymbian(QObject *parent) - : QAbstractEventDispatcher(parent), - m_selectThread(0), - m_activeScheduler(0), - m_wakeUpAO(0), - m_completeDeferredAOs(0), - m_interrupt(false), - m_wakeUpDone(0), - m_iterationCount(0), - m_insideTimerEvent(false), - m_noSocketEvents(false) -{ -#ifdef QT_SYMBIAN_PRIORITY_DROP - m_delay = baseDelay; - m_avgEventTime = 0; - idleDetectorThread(); -#endif -} - -QEventDispatcherSymbian::~QEventDispatcherSymbian() -{ -} - -void QEventDispatcherSymbian::startingUp() -{ - if( !CActiveScheduler::Current() ) { - m_activeScheduler = q_check_ptr(new CQtActiveScheduler()); // CBase derived class needs to be checked on new - CActiveScheduler::Install(m_activeScheduler); - } - m_wakeUpAO = q_check_ptr(new QWakeUpActiveObject(this)); - m_completeDeferredAOs = q_check_ptr(new QCompleteDeferredAOs(this)); - // We already might have posted events, wakeup once to process them - wakeUp(); -} - -QSelectThread& QEventDispatcherSymbian::selectThread() { - if (!m_selectThread) - m_selectThread = new QSelectThread; - return *m_selectThread; -} - -void QEventDispatcherSymbian::closingDown() -{ - if (m_selectThread && m_selectThread->isRunning()) { - m_selectThread->stop(); - } - delete m_selectThread; - m_selectThread = 0; - - delete m_completeDeferredAOs; - delete m_wakeUpAO; - if (m_activeScheduler) { - delete m_activeScheduler; - } -} - -bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags flags ) -{ - bool handledAnyEvent = false; - bool oldNoSocketEventsValue = m_noSocketEvents; - bool oldInsideTimerEventValue = m_insideTimerEvent; - - m_insideTimerEvent = false; - - QT_TRY { - Q_D(QAbstractEventDispatcher); - - // It is safe if this counter overflows. The main importance is that each - // iteration count is different from the last. - m_iterationCount++; - - RThread &thread = d->threadData->symbian_thread_handle; - - bool block; - if (flags & QEventLoop::WaitForMoreEvents) { - block = true; - emit aboutToBlock(); - } else { - block = false; - } - - if (flags & QEventLoop::ExcludeSocketNotifiers) { - m_noSocketEvents = true; - } else { - m_noSocketEvents = false; - handledAnyEvent = sendDeferredSocketEvents(); - } - - bool handledSymbianEvent = false; - m_interrupt = false; - -#ifdef QT_SYMBIAN_PRIORITY_DROP - QElapsedTimer eventTimer; -#endif - - while (1) { - if (block) { - // This is where Qt will spend most of its time. - CActiveScheduler::Current()->WaitForAnyRequest(); - } else { - if (thread.RequestCount() == 0) { -#ifdef QT_SYMBIAN_PRIORITY_DROP - if (idleDetectorThread()->hasRun()) { - m_lastIdleRequestTimer.start(); - idleDetectorThread()->kick(); - } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) { - User::AfterHighRes(m_delay); - } -#endif - break; - } - // This one should return without delay. - CActiveScheduler::Current()->WaitForAnyRequest(); - } - -#ifdef QT_SYMBIAN_PRIORITY_DROP - if (idleDetectorThread()->hasRun()) { - if (m_delay > baseDelay) - m_delay -= baseDelay; - m_lastIdleRequestTimer.start(); - idleDetectorThread()->kick(); - } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) { - User::AfterHighRes(m_delay); - // allow delay to be up to 1/4 of execution time - if (!idleDetectorThread()->hasRun() && m_delay*3 < m_avgEventTime) - m_delay += baseDelay; - } - eventTimer.start(); -#endif - - TInt error; - handledSymbianEvent = CActiveScheduler::RunIfReady(error, KMinTInt); - if (error) { - qWarning("CActiveScheduler::RunIfReady() returned error: %i\n", error); - CActiveScheduler::Current()->Error(error); - } - -#ifdef QT_SYMBIAN_PRIORITY_DROP - int eventDur = eventTimer.elapsed()*1000; - // average is calcualted as a 5% decaying exponential average - m_avgEventTime = (m_avgEventTime * 95 + eventDur * 5) / 100; -#endif - - if (!handledSymbianEvent) { - qFatal("QEventDispatcherSymbian::processEvents(): Caught Symbian stray signal"); - } - handledAnyEvent = true; - if (m_interrupt) { - break; - } - block = false; - }; - - emit awake(); - } QT_CATCH (const std::exception& ex) { -#ifndef QT_NO_EXCEPTIONS - CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex)); -#endif - } - - m_noSocketEvents = oldNoSocketEventsValue; - m_insideTimerEvent = oldInsideTimerEventValue; - - return handledAnyEvent; -} - -void QEventDispatcherSymbian::timerFired(int timerId) -{ - QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.find(timerId); - if (i == m_timerList.end()) { - // The timer has been deleted. Ignore this event. - return; - } - - SymbianTimerInfoPtr timerInfo = *i; - - // Prevent infinite timer recursion. - if (timerInfo->inTimerEvent) { - return; - } - - timerInfo->inTimerEvent = true; - bool oldInsideTimerEventValue = m_insideTimerEvent; - m_insideTimerEvent = true; - - QTimerEvent event(timerInfo->timerId); - QCoreApplication::sendEvent(timerInfo->receiver, &event); - - m_insideTimerEvent = oldInsideTimerEventValue; - timerInfo->inTimerEvent = false; - - return; -} - -void QEventDispatcherSymbian::wakeUpWasCalled() -{ - // The reactivation should happen in RunL, right before the call to this function. - // This is because m_wakeUpDone is the "signal" that the object can be completed - // once more. - // Also, by dispatching the posted events after resetting m_wakeUpDone, we guarantee - // that no posted event notification will be lost. If we did it the other way - // around, it would be possible for another thread to post an event right after - // the sendPostedEvents was done, but before the object was ready to be completed - // again. This could deadlock the application if there are no other posted events. - m_wakeUpDone.fetchAndStoreOrdered(0); - sendPostedEvents(); -} - -void QEventDispatcherSymbian::interrupt() -{ - m_interrupt = true; - wakeUp(); -} - -void QEventDispatcherSymbian::wakeUp() -{ - Q_D(QAbstractEventDispatcher); - - if (m_wakeUpAO && m_wakeUpDone.testAndSetAcquire(0, 1)) { - TRequestStatus *status = &m_wakeUpAO->iStatus; - QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone); - } -} - -bool QEventDispatcherSymbian::sendPostedEvents() -{ - Q_D(QAbstractEventDispatcher); - - // moveToThread calls this and canWait == true -> Events will never get processed - // if we check for d->threadData->canWait - // - // QCoreApplication::postEvent sets canWait = false, but after the object and events - // are moved to a new thread, the canWait in new thread is true i.e. not changed to reflect - // the flag on old thread. That's why events in a new thread will not get processed. - // This migth be actually bug in moveToThread functionality, but because other platforms - // do not check canWait in wakeUp (where we essentially are now) - decided to remove it from - // here as well. - - //if (!d->threadData->canWait) { - QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); - return true; - //} - //return false; -} - -inline void QEventDispatcherSymbian::addDeferredActiveObject(QActiveObject *object) -{ - queueDeferredActiveObjectsCompletion(); - m_deferredActiveObjects.append(object); -} - -inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object) -{ - m_deferredActiveObjects.removeAll(object); - m_deferredSocketEvents.removeAll(object); -} - -inline void QEventDispatcherSymbian::addDeferredSocketActiveObject(QActiveObject *object) -{ - m_deferredSocketEvents.append(object); -} - -void QEventDispatcherSymbian::queueDeferredActiveObjectsCompletion() -{ - m_completeDeferredAOs->complete(); -} - -void QEventDispatcherSymbian::reactivateDeferredActiveObjects() -{ - while (!m_deferredActiveObjects.isEmpty()) { - QActiveObject *object = m_deferredActiveObjects.takeFirst(); - object->reactivateAndComplete(); - } - - // We do this because we want to return from processEvents. This is because - // each invocation of processEvents should only run each active object once. - // The active scheduler should run them continously, however. - m_interrupt = true; -} - -bool QEventDispatcherSymbian::sendDeferredSocketEvents() -{ - bool sentAnyEvents = false; - while (!m_deferredSocketEvents.isEmpty()) { - sentAnyEvents = true; - QActiveObject *object = m_deferredSocketEvents.takeFirst(); - object->reactivateAndComplete(); - } - - return sentAnyEvents; -} - -void QEventDispatcherSymbian::flush() -{ -} - -bool QEventDispatcherSymbian::hasPendingEvents() -{ - Q_D(QAbstractEventDispatcher); - return (d->threadData->symbian_thread_handle.RequestCount() != 0 - || !d->threadData->canWait || !m_deferredSocketEvents.isEmpty()); -} - -void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifier ) -{ - //check socket descriptor is usable - if (notifier->socket() >= FD_SETSIZE || notifier->socket() < 0) { - //same warning message as the unix event dispatcher for easy testing - qWarning("QSocketNotifier: Internal error"); - return; - } - //note - this is only for "open C" file descriptors - //for native sockets, an active object in the symbian socket engine handles this - QSocketActiveObject *socketAO = new QSocketActiveObject(this, notifier); - Q_CHECK_PTR(socketAO); - m_notifiers.insert(notifier, socketAO); - selectThread().requestSocketEvents(notifier, &socketAO->iStatus); -} - -void QEventDispatcherSymbian::unregisterSocketNotifier ( QSocketNotifier * notifier ) -{ - //note - this is only for "open C" file descriptors - //for native sockets, an active object in the symbian socket engine handles this - if (m_selectThread) - m_selectThread->cancelSocketEvents(notifier); - if (m_notifiers.contains(notifier)) { - QSocketActiveObject *sockObj = *m_notifiers.find(notifier); - m_deferredSocketEvents.removeAll(sockObj); - sockObj->deleteLater(); - m_notifiers.remove(notifier); - } -} - -void QEventDispatcherSymbian::reactivateSocketNotifier(QSocketNotifier *notifier) -{ - selectThread().requestSocketEvents(notifier, &m_notifiers[notifier]->iStatus); -} - -void QEventDispatcherSymbian::registerTimer ( int timerId, int interval, QObject * object ) -{ - if (interval < 0) { - qWarning("Timer interval < 0"); - interval = 0; - } - - SymbianTimerInfoPtr timer(new SymbianTimerInfo); - timer->timerId = timerId; - timer->interval = interval; - timer->inTimerEvent = false; - timer->receiver = object; - timer->dispatcher = this; - timer->timerAO = q_check_ptr(new QTimerActiveObject(this, timer.data())); - m_timerList.insert(timerId, timer); - - timer->timerAO->Start(); - - if (m_insideTimerEvent) - // If we are inside a timer event, we need to prevent event starvation - // by preventing newly created timers from running in the same event processing - // iteration. Do this by calling the maybeQueueForLater() function to "fake" that we have - // already run once. This will cause the next run to be added to the deferred - // queue instead. - timer->timerAO->maybeQueueForLater(); -} - -bool QEventDispatcherSymbian::unregisterTimer ( int timerId ) -{ - if (!m_timerList.contains(timerId)) { - return false; - } - - SymbianTimerInfoPtr timerInfo = m_timerList.take(timerId); - - if (!QObjectPrivate::get(timerInfo->receiver)->inThreadChangeEvent) - QAbstractEventDispatcherPrivate::releaseTimerId(timerId); - - return true; -} - -bool QEventDispatcherSymbian::unregisterTimers ( QObject * object ) -{ - if (m_timerList.isEmpty()) - return false; - - bool unregistered = false; - for (QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.begin(); i != m_timerList.end(); ) { - if ((*i)->receiver == object) { - i = m_timerList.erase(i); - unregistered = true; - } else { - ++i; - } - } - - return unregistered; -} - -QList<QEventDispatcherSymbian::TimerInfo> QEventDispatcherSymbian::registeredTimers ( QObject * object ) const -{ - QList<TimerInfo> list; - for (QHash<int, SymbianTimerInfoPtr>::const_iterator i = m_timerList.begin(); i != m_timerList.end(); ++i) { - if ((*i)->receiver == object) { - list.push_back(TimerInfo((*i)->timerId, (*i)->interval)); - } - } - - return list; -} - -/* - * This active scheduler class implements a simple report and continue policy, for Symbian OS leaves - * or exceptions from Qt that fall back to the scheduler. - * It will be used in cases where there is no existing active scheduler installed. - * Apps which link to qts60main.lib will have the UI active scheduler installed in the main thread - * instead of this one. But this would be used in other threads in the UI. - * An app could replace this behaviour by installing an alternative active scheduler. - */ -void CQtActiveScheduler::Error(TInt aError) const -{ - QT_TRY { - qWarning("Error from active scheduler %d", aError); - } - QT_CATCH (const std::bad_alloc&) {} // ignore alloc fails, nothing more can be done -} - -bool QActiveObject::wait(CActive* ao, int ms) -{ - if (!ao->IsActive()) - return true; //request already complete - bool timedout = false; - if (ms > 0) { - TRequestStatus tstat; - RTimer t; - if (KErrNone != t.CreateLocal()) - return false; - t.HighRes(tstat, ms*1000); - User::WaitForRequest(tstat, ao->iStatus); - if (tstat != KRequestPending) { - timedout = true; - } else { - t.Cancel(); - //balance thread semaphore - User::WaitForRequest(tstat); - } - t.Close(); - } else { - User::WaitForRequest(ao->iStatus); - } - if (timedout) - return false; - - //evil cast to allow calling of protected virtual - ((QActiveObject*)ao)->RunL(); - - //clear active & pending flags - ao->iStatus = TRequestStatus(); - - return true; -} - -bool QActiveObject::wait(QList<CActive*> aos, int ms) -{ - QVector<TRequestStatus*> stati; - stati.reserve(aos.count() + 1); - foreach (CActive* ao, aos) { - if (!ao->IsActive()) - return true; //request already complete - stati.append(&(ao->iStatus)); - } - bool timedout = false; - TRequestStatus tstat; - RTimer t; - if (ms > 0) { - if (KErrNone != t.CreateLocal()) - return false; - t.HighRes(tstat, ms*1000); - stati.append(&tstat); - } - User::WaitForNRequest(stati.data(), stati.count()); - if (ms > 0) { - if (tstat != KRequestPending) { - timedout = true; - } else { - t.Cancel(); - //balance thread semaphore - User::WaitForRequest(tstat); - } - t.Close(); - } - if (timedout) - return false; - - foreach (CActive* ao, aos) { - if (ao->iStatus != KRequestPending) { - //evil cast to allow calling of protected virtual - ((QActiveObject*)ao)->RunL(); - - //clear active & pending flags - ao->iStatus = TRequestStatus(); - break; //only call one - } - } - return true; -} - -QT_END_NAMESPACE - -#include "moc_qeventdispatcher_symbian_p.cpp" |