src/corelib/kernel/qeventdispatcher_symbian.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1023 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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 "qeventdispatcher_symbian_p.h"
+#include <private/qthread_p.h>
+#include <qcoreapplication.h>
+#include <private/qcoreapplication_p.h>
+#include <qdatetime.h>
+
+#include <unistd.h>
+#include <errno.h>
+
+QT_BEGIN_NAMESPACE
+
+#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 fd, QMutex *mutex)
+        : m_mutex(mutex)
+    {
+        if (m_mutex->tryLock())
+            return;
+
+        char dummy = 0;
+        qt_pipe_write(fd, &dummy, 1);
+
+        m_mutex->lock();
+    }
+
+    ~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 okToRun(). This will allow
+ * exactly one run of the active object, and mark it as such. If it is called again, it will return
+ * false, and add the object to a queue so it can be run later.
+ *
+ * The QCompleteDeferredAOs class is a special object that runs after all others, which will
+ * reactivate the objects that were previously not run.
+ */
+inline 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::okToRun()
+{
+    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 true;
+    } else {
+        // The event has already occurred.
+        m_dispatcher->addDeferredActiveObject(this);
+        m_hasRunAgain = true;
+        return false;
+    }
+}
+
+void QActiveObject::reactivateAndComplete()
+{
+    iStatus = KRequestPending;
+    SetActive();
+    TRequestStatus *status = &iStatus;
+    QEventDispatcherSymbian::RequestComplete(status, KErrNone);
+
+    m_hasRunAgain = false;
+    m_hasAlreadyRun = false;
+}
+
+QWakeUpActiveObject::QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher)
+    : CActive(WAKE_UP_PRIORITY),
+      m_dispatcher(dispatcher)
+{
+    CActiveScheduler::Add(this);
+    iStatus = KRequestPending;
+    SetActive();
+}
+
+QWakeUpActiveObject::~QWakeUpActiveObject()
+{
+    Cancel();
+}
+
+void QWakeUpActiveObject::DoCancel()
+{
+    if (iStatus.Int() == KRequestPending) {
+        TRequestStatus *status = &iStatus;
+        QEventDispatcherSymbian::RequestComplete(status, KErrNone);
+    }
+}
+
+void QWakeUpActiveObject::RunL()
+{
+    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)
+{
+}
+
+QTimerActiveObject::~QTimerActiveObject()
+{
+    Cancel();
+}
+
+void QTimerActiveObject::DoCancel()
+{
+    if (m_timerInfo->interval > 0) {
+        m_rTimer.Cancel();
+        m_rTimer.Close();
+    } else {
+        if (iStatus.Int() == KRequestPending) {
+            TRequestStatus *status = &iStatus;
+            QEventDispatcherSymbian::RequestComplete(status, KErrNone);
+        }
+    }
+}
+
+void QTimerActiveObject::RunL()
+{
+    int error;
+    QT_TRYCATCH_ERROR(error, Run());
+    // 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 {
+        //HighRes gives the 1ms accuracy expected by Qt, the +1 is to ensure that
+        //"Timers will never time out earlier than the specified timeout value"
+        //condition is always met.
+        m_rTimer.HighRes(iStatus, (m_timerInfo->msLeft + 1) * 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 (!okToRun())
+        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) {
+        m_rTimer.CreateLocal();
+        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;
+
+        char buffer;
+
+        while (::read(m_pipeEnds[0], &buffer, 1) > 0) {}
+
+        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 dont 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::Read, &readfds);
+                updateActivatedNotifiers(QSocketNotifier::Write, &writefds);
+                updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds);
+
+                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::Read, &readfds);
+            updateActivatedNotifiers(QSocketNotifier::Write, &writefds);
+            updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds);
+        }
+
+        m_waitCond.wait(&m_mutex);
+    }
+
+    m_mutex.unlock();
+}
+
+void QSelectThread::requestSocketEvents ( QSocketNotifier *notifier, TRequestStatus *status )
+{
+    Q_D(QThread);
+
+    if (!isRunning()) {
+        start();
+    }
+
+    QSelectMutexGrabber lock(m_pipeEnds[1], &m_mutex);
+
+    Q_ASSERT(!m_AOStatuses.contains(notifier));
+
+    m_AOStatuses.insert(notifier, status);
+
+    m_waitCond.wakeAll();
+}
+
+void QSelectThread::cancelSocketEvents ( QSocketNotifier *notifier )
+{
+    QSelectMutexGrabber lock(m_pipeEnds[1], &m_mutex);
+
+    m_AOStatuses.remove(notifier);
+
+    m_waitCond.wakeAll();
+}
+
+void QSelectThread::restart()
+{
+    QSelectMutexGrabber lock(m_pipeEnds[1], &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", 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 (!okToRun())
+        return;
+
+    QT_TRYCATCH_LEAVING(m_dispatcher->socketFired(this));
+}
+
+void QSocketActiveObject::deleteLater()
+{
+    if (m_inSocketEvent) {
+        m_deleteLater = true;
+    } else {
+        delete this;
+    }
+}
+
+QEventDispatcherSymbian::QEventDispatcherSymbian(QObject *parent)
+    : QAbstractEventDispatcher(parent),
+      m_activeScheduler(0),
+      m_wakeUpAO(0),
+      m_completeDeferredAOs(0),
+      m_interrupt(false),
+      m_wakeUpDone(0),
+      m_iterationCount(0),
+      m_noSocketEvents(false)
+{
+}
+
+QEventDispatcherSymbian::~QEventDispatcherSymbian()
+{
+    m_processHandle.Close();
+}
+
+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();
+}
+
+void QEventDispatcherSymbian::closingDown()
+{
+    if (m_selectThread.isRunning()) {
+        m_selectThread.stop();
+    }
+
+    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;
+
+    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;
+
+        /*
+         * This QTime variable is used to measure the time it takes to finish
+         * the event loop. If we take too long in the loop, other processes
+         * may be starved and killed. After the first event has completed, we
+         * take the current time, and if the remaining events take longer than
+         * a preset time, we temporarily lower the priority to force a context
+         * switch. For applications that do not take unecessarily long in the
+         * event loop, the priority will not be altered.
+         */
+        QTime time;
+        enum {
+            FirstRun,
+            SubsequentRun,
+            TimeStarted
+        } timeState = FirstRun;
+
+        TProcessPriority priority;
+
+        while (1) {
+            if (block) {
+                // This is where Qt will spend most of its time.
+                CActiveScheduler::Current()->WaitForAnyRequest();
+            } else {
+                if (thread.RequestCount() == 0) {
+                    break;
+                }
+                // This one should return without delay.
+                CActiveScheduler::Current()->WaitForAnyRequest();
+            }
+
+            if (timeState == SubsequentRun) {
+                time.start();
+                timeState = TimeStarted;
+            }
+
+            TInt error;
+            handledSymbianEvent = CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle);
+            if (error) {
+                qWarning("CActiveScheduler::RunIfReady() returned error: %i\n", error);
+                CActiveScheduler::Current()->Error(error);
+            }
+
+            if (!handledSymbianEvent) {
+                qFatal("QEventDispatcherSymbian::processEvents(): Caught Symbian stray signal");
+            }
+            handledAnyEvent = true;
+            if (m_interrupt) {
+                break;
+            }
+            block = false;
+            if (timeState == TimeStarted && time.elapsed() > 100) {
+                priority = m_processHandle.Priority();
+                m_processHandle.SetPriority(EPriorityBackground);
+                time.start();
+                // Slight chance of race condition in the next lines, but nothing fatal
+                // will happen, just wrong priority.
+                if (m_processHandle.Priority() == EPriorityBackground) {
+                    m_processHandle.SetPriority(priority);
+                }
+            }
+            if (timeState == FirstRun)
+                timeState = SubsequentRun;
+        };
+
+        emit awake();
+    } QT_CATCH (const std::exception& ex) {
+#ifndef QT_NO_EXCEPTIONS
+        CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex));
+#endif
+    }
+
+    m_noSocketEvents = oldNoSocketEventsValue;
+
+    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;
+
+    QTimerEvent event(timerInfo->timerId);
+    QCoreApplication::sendEvent(timerInfo->receiver, &event);
+
+    timerInfo->inTimerEvent = false;
+
+    return;
+}
+
+void QEventDispatcherSymbian::socketFired(QSocketActiveObject *socketAO)
+{
+    if (m_noSocketEvents) {
+        m_deferredSocketEvents.append(socketAO);
+        return;
+    }
+
+    QEvent e(QEvent::SockAct);
+    socketAO->m_inSocketEvent = true;
+    QCoreApplication::sendEvent(socketAO->m_notifier, &e);
+    socketAO->m_inSocketEvent = false;
+
+    if (socketAO->m_deleteLater) {
+        delete socketAO;
+    } else {
+        socketAO->iStatus = KRequestPending;
+        socketAO->SetActive();
+        reactivateSocketNotifier(socketAO->m_notifier);
+    }
+}
+
+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)
+{
+    if (m_deferredActiveObjects.isEmpty()) {
+        m_completeDeferredAOs->complete();
+    }
+    m_deferredActiveObjects.append(object);
+}
+
+inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object)
+{
+    m_deferredActiveObjects.removeAll(object);
+}
+
+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;
+        socketFired(m_deferredSocketEvents.takeFirst());
+    }
+
+    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 )
+{
+    QSocketActiveObject *socketAO = q_check_ptr(new QSocketActiveObject(this, notifier));
+    m_notifiers.insert(notifier, socketAO);
+    m_selectThread.requestSocketEvents(notifier, &socketAO->iStatus);
+}
+
+void QEventDispatcherSymbian::unregisterSocketNotifier ( QSocketNotifier * notifier )
+{
+    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)
+{
+    m_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();
+}
+
+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
+}
+
+QT_END_NAMESPACE