diff options
author | mread <qt-info@nokia.com> | 2011-11-08 16:19:16 (GMT) |
---|---|---|
committer | mread <qt-info@nokia.com> | 2011-11-08 16:48:21 (GMT) |
commit | 5f5119e9019cf41ab811db822a5a38c241626382 (patch) | |
tree | 10598fd7397df1c07ee110c2cd2c28454f259b92 /src/corelib | |
parent | 345c3f3502832e4850a6d9ea330ddca0c6e9c8bd (diff) | |
download | Qt-5f5119e9019cf41ab811db822a5a38c241626382.zip Qt-5f5119e9019cf41ab811db822a5a38c241626382.tar.gz Qt-5f5119e9019cf41ab811db822a5a38c241626382.tar.bz2 |
Preventing zero-timer lockups when Symbian's active scheduler used
Qt 4.7 had a deferred active object scheme to give Qt active objects
some round robin behaviour with Symbian's active scheduler. Qt 4.8
has a round robin active scheduler which removed the need for any
special support in Qt's active objects. Except it has been discovered
that sometimes Symbian's active scheduler is used, and if
Qt's zero-timer active objects are running at the time, they can
prevent any other active objects from running in Symbian's scheduler,
which can cause lockups.
This change re-introduces the deferred active object system for Qt
timer active objects. This means that a timer active object will only
run once per processEvents() call when Symbian's active scheduler is
used. Qt's round robin active scheduler already prevents an
active object running more than once per processEvents(), so this
change does not affect the normal case of using Qt's round robin
scheduler.
Task-number: QTTH-1494
Reviewed-by: Shane Kearns
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_symbian.cpp | 125 | ||||
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_symbian_p.h | 34 |
2 files changed, 152 insertions, 7 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp index fca2feb..ef331f6 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian.cpp +++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp @@ -59,6 +59,7 @@ QT_BEGIN_NAMESPACE #define WAKE_UP_PRIORITY CActive::EPriorityStandard #define TIMER_PRIORITY CActive::EPriorityHigh +#define COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY CActive::EPriorityIdle class Incrementer { int &variable; @@ -140,17 +141,49 @@ private: }; /* - * This class can be used as a base class for Qt active objects. + * This class is designed to aid in implementing event handling in a more round robin fashion, + * when Qt active objects are used outside of QtRRActiveScheduler. + * We cannot change active objects that we do not own, but active objects that Qt owns may 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. * Socket active objects can use it to defer their activity. */ QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher) : CActive(priority), - m_dispatcher(dispatcher) + 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() @@ -170,6 +203,9 @@ void QActiveObject::reactivateAndComplete() SetActive(); TRequestStatus *status = &iStatus; QEventDispatcherSymbian::RequestComplete(status, error); + + m_hasRunAgain = false; + m_hasAlreadyRun = false; } QWakeUpActiveObject::QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher) @@ -212,8 +248,8 @@ void QWakeUpActiveObject::RunL() } QTimerActiveObject::QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo) - : CActive(TIMER_PRIORITY), - m_dispatcher(dispatcher), m_timerInfo(timerInfo), m_expectedTimeSinceLastEvent(0) + : QActiveObject(TIMER_PRIORITY, dispatcher), + m_timerInfo(timerInfo), m_expectedTimeSinceLastEvent(0) { // start the timeout timer to ensure initialisation m_timeoutTimer.start(); @@ -290,6 +326,9 @@ void QTimerActiveObject::Run() 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; @@ -341,6 +380,44 @@ 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) { @@ -879,6 +956,7 @@ QEventDispatcherSymbian::QEventDispatcherSymbian(QObject *parent) m_selectThread(0), m_activeScheduler(0), m_wakeUpAO(0), + m_completeDeferredAOs(0), m_interrupt(false), m_wakeUpDone(0), m_iterationCount(0), @@ -903,6 +981,7 @@ void QEventDispatcherSymbian::startingUp() 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(); } @@ -921,6 +1000,7 @@ void QEventDispatcherSymbian::closingDown() delete m_selectThread; m_selectThread = 0; + delete m_completeDeferredAOs; delete m_wakeUpAO; if (m_activeScheduler) { delete m_activeScheduler; @@ -1153,6 +1233,35 @@ inline void QEventDispatcherSymbian::addDeferredSocketActiveObject(QActiveObject m_deferredSocketEvents.append(object); } +inline void QEventDispatcherSymbian::addDeferredActiveObject(QActiveObject *object) +{ + queueDeferredActiveObjectsCompletion(); + m_deferredActiveObjects.append(object); +} + +inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object) +{ + m_deferredActiveObjects.removeAll(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; @@ -1228,6 +1337,14 @@ void QEventDispatcherSymbian::registerTimer ( int timerId, int interval, QObject 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 ) diff --git a/src/corelib/kernel/qeventdispatcher_symbian_p.h b/src/corelib/kernel/qeventdispatcher_symbian_p.h index 1b81599..01f5ab1 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian_p.h +++ b/src/corelib/kernel/qeventdispatcher_symbian_p.h @@ -84,10 +84,15 @@ public: ~QActiveObject(); bool maybeDeferSocketEvent(); - + bool maybeQueueForLater(); void reactivateAndComplete(); protected: QEventDispatcherSymbian *m_dispatcher; + +private: + bool m_hasAlreadyRun : 1; + bool m_hasRunAgain : 1; + int m_iterationCount; }; class QWakeUpActiveObject : public CActive @@ -124,7 +129,7 @@ struct SymbianTimerInfo : public QSharedData typedef QExplicitlySharedDataPointer<SymbianTimerInfo> SymbianTimerInfoPtr; // This is a bit of a proxy class. See comments in SetActive and Start for details. -class QTimerActiveObject : public CActive +class QTimerActiveObject : public QActiveObject { public: QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo); @@ -141,13 +146,28 @@ private: void StartTimer(); private: - QEventDispatcherSymbian *m_dispatcher; SymbianTimerInfo *m_timerInfo; QElapsedTimer m_timeoutTimer; int m_expectedTimeSinceLastEvent; RTimer m_rTimer; }; +class QCompleteDeferredAOs : public CActive +{ +public: + QCompleteDeferredAOs(QEventDispatcherSymbian *dispatcher); + ~QCompleteDeferredAOs(); + + void complete(); + +protected: + void DoCancel(); + void RunL(); + +private: + QEventDispatcherSymbian *m_dispatcher; +}; + class QSocketActiveObject : public QActiveObject { public: @@ -231,6 +251,11 @@ public: void wakeUpWasCalled(); void reactivateSocketNotifier(QSocketNotifier *notifier); + void addDeferredActiveObject(QActiveObject *object); + void removeDeferredActiveObject(QActiveObject *object); + void queueDeferredActiveObjectsCompletion(); + // Can be overridden to activate local active objects too, but do call baseclass! + virtual void reactivateDeferredActiveObjects(); inline int iterationCount() const { return m_iterationCount; } void addDeferredSocketActiveObject(QActiveObject *object); @@ -253,6 +278,7 @@ private: QHash<QSocketNotifier *, QSocketActiveObject *> m_notifiers; QWakeUpActiveObject *m_wakeUpAO; + QCompleteDeferredAOs *m_completeDeferredAOs; volatile bool m_interrupt; QAtomicInt m_wakeUpDone; @@ -263,6 +289,8 @@ private: //deferred until socket events are enabled QList<QActiveObject *> m_deferredSocketEvents; + QList<QActiveObject *> m_deferredActiveObjects; + int m_delay; int m_avgEventTime; QElapsedTimer m_lastIdleRequestTimer; |