summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormread <qt-info@nokia.com>2011-11-08 16:19:16 (GMT)
committermread <qt-info@nokia.com>2011-11-08 16:48:21 (GMT)
commit5f5119e9019cf41ab811db822a5a38c241626382 (patch)
tree10598fd7397df1c07ee110c2cd2c28454f259b92
parent345c3f3502832e4850a6d9ea330ddca0c6e9c8bd (diff)
downloadQt-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
-rw-r--r--src/corelib/kernel/qeventdispatcher_symbian.cpp125
-rw-r--r--src/corelib/kernel/qeventdispatcher_symbian_p.h34
-rw-r--r--src/s60installs/eabi/QtCoreu.def5
3 files changed, 155 insertions, 9 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;
diff --git a/src/s60installs/eabi/QtCoreu.def b/src/s60installs/eabi/QtCoreu.def
index 1fabdb2..5436390 100644
--- a/src/s60installs/eabi/QtCoreu.def
+++ b/src/s60installs/eabi/QtCoreu.def
@@ -1294,7 +1294,7 @@ EXPORTS
_ZN23QEventDispatcherSymbian24reactivateSocketNotifierEP15QSocketNotifier @ 1293 NONAME
_ZN23QEventDispatcherSymbian24sendDeferredSocketEventsEv @ 1294 NONAME
_ZN23QEventDispatcherSymbian24unregisterSocketNotifierEP15QSocketNotifier @ 1295 NONAME
- _ZN23QEventDispatcherSymbian31reactivateDeferredActiveObjectsEv @ 1296 NONAME ABSENT
+ _ZN23QEventDispatcherSymbian31reactivateDeferredActiveObjectsEv @ 1296 NONAME
_ZN23QEventDispatcherSymbian5flushEv @ 1297 NONAME
_ZN23QEventDispatcherSymbian6wakeUpEv @ 1298 NONAME
_ZN23QEventDispatcherSymbian9interruptEv @ 1299 NONAME
@@ -3713,7 +3713,7 @@ EXPORTS
_ZrsR11QDataStreamR12QEasingCurve @ 3712 NONAME
_Z26qt_symbian_SetupThreadHeapiR24SStdEpocThreadCreateInfo @ 3713 NONAME
_ZN24QAbstractDeclarativeData17objectNameChangedE @ 3714 NONAME DATA 4
- _ZN23QEventDispatcherSymbian36queueDeferredActiveObjectsCompletionEv @ 3715 NONAME ABSENT
+ _ZN23QEventDispatcherSymbian36queueDeferredActiveObjectsCompletionEv @ 3715 NONAME
_ZN23QCoreApplicationPrivate18symbianCommandLineEv @ 3716 NONAME
_ZNK11QMetaMethod8revisionEv @ 3717 NONAME
_ZNK13QMetaProperty8revisionEv @ 3718 NONAME
@@ -4164,4 +4164,5 @@ EXPORTS
zlibCompileFlags @ 4163 NONAME
_ZN14QFactoryLoader9updateDirERK7QStringR9QSettings @ 4164 NONAME
_ZN23QCoreApplicationPrivate26rebuildInstallLibraryPathsEv @ 4165 NONAME
+ _ZN13QActiveObject18maybeQueueForLaterEv @ 4166 NONAME