diff options
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_symbian.cpp | 52 | ||||
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_symbian_p.h | 4 | ||||
-rw-r--r-- | src/s60installs/bwins/QtCoreu.def | 6 | ||||
-rw-r--r-- | src/s60installs/eabi/QtCoreu.def | 6 | ||||
-rw-r--r-- | tests/auto/qapplication/tst_qapplication.cpp | 112 |
5 files changed, 159 insertions, 21 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp index 4930a16..5cc38e6 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian.cpp +++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp @@ -245,7 +245,7 @@ void QWakeUpActiveObject::RunL() { iStatus = KRequestPending; SetActive(); - QT_TRYCATCH_LEAVING(m_dispatcher->wakeUpWasCalled()); + QT_TRYCATCH_LEAVING(m_dispatcher->wakeUpWasCalled(this)); } QTimerActiveObject::QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo) @@ -349,7 +349,7 @@ void QTimerActiveObject::Run() m_timerInfo->msLeft = m_timerInfo->interval; StartTimer(); - m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId); + m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId, this); } else { // However, we only complete zero timers after the event has finished, // in order to prevent busy looping when doing nested loops. @@ -357,7 +357,7 @@ void QTimerActiveObject::Run() // 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); + m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId, this); iStatus = KRequestPending; SetActive(); @@ -767,6 +767,7 @@ public: }; static RunResult RunMarkedIfReady(TInt &runPriority, TInt minimumPriority, QEventDispatcherSymbian *dispatcher); static bool UseRRActiveScheduler(); + static bool TestAndClearActiveObjectRunningInRRScheduler(CActive* ao); private: // active scheduler access kit, for gaining access to the internals of active objects for @@ -786,9 +787,11 @@ private: TPriQueLink iLink; enum TMarks { - ENewObject, // CBase zero initialization sets this, new objects cannot be run in the processEvents in which they are created - ENotRun, // This object has not yet run in the current processEvents call - ERan // This object has run in the current processEvents call + ENewObject, // CBase zero initialization sets this, new objects cannot be run in the processEvents in which they are created + ENotRun, // This object has not yet run in the current processEvents call + ERunningUnchecked, // This object is running in the current processEvents call, as yet unacknowledged by the event dispatcher + ERunningChecked, // This object is running in a processEvents call, the event dispatcher knows which loop level + ERan // This object has run in the current processEvents call }; int iMark; //TAny* iSpare; }; @@ -836,11 +839,12 @@ QtRRActiveScheduler::RunResult QtRRActiveScheduler::RunMarkedIfReady(TInt &runPr if (active->IsActive() && (active->iStatus!=KRequestPending)) { int& mark = dataAccess->iMark; if (mark == CActiveDataAccess::ENotRun && active->Priority()>=minimumPriority) { - mark = CActiveDataAccess::ERan; + mark = CActiveDataAccess::ERunningUnchecked; runPriority = active->Priority(); dataAccess->iStatus.iFlags&=~TRequestStatusAccess::ERequestActiveFlags; int vptr = *(int*)active; // vptr can be used to identify type when debugging leaves TRAP(error, QT_TRYCATCH_LEAVING(active->RunL())); + mark = CActiveDataAccess::ERan; if (error!=KErrNone) error=active->RunError(error); if (error) { @@ -869,6 +873,16 @@ bool QtRRActiveScheduler::UseRRActiveScheduler() return schedulerCompatibilityNumber == NULL; } +bool QtRRActiveScheduler::TestAndClearActiveObjectRunningInRRScheduler(CActive* ao) +{ + CActiveDataAccess *dataAccess = (CActiveDataAccess*)ao; + if (dataAccess->iMark == CActiveDataAccess::ERunningUnchecked) { + dataAccess->iMark = CActiveDataAccess::ERunningChecked; + return true; + } + return false; +} + #ifdef QT_SYMBIAN_PRIORITY_DROP class QIdleDetectorThread { @@ -1166,7 +1180,7 @@ bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags fla return handledAnyEvent; } -void QEventDispatcherSymbian::timerFired(int timerId) +void QEventDispatcherSymbian::timerFired(int timerId, QTimerActiveObject *ao) { Q_D(QAbstractEventDispatcher); QHash<int, SymbianTimerInfoPtr>::iterator i = m_timerList.find(timerId); @@ -1187,9 +1201,13 @@ void QEventDispatcherSymbian::timerFired(int timerId) m_insideTimerEvent = true; QTimerEvent event(timerInfo->timerId); - //undo the added nesting level around RunIfReady, since Qt's event system also nests - Decrementer dec(d->threadData->loopLevel); - QCoreApplication::sendEvent(timerInfo->receiver, &event); + if (QtRRActiveScheduler::TestAndClearActiveObjectRunningInRRScheduler(ao)) { + //undo the added nesting level around RunIfReady, since Qt's event system also nests + Decrementer dec(d->threadData->loopLevel); + QCoreApplication::sendEvent(timerInfo->receiver, &event); + } else { + QCoreApplication::sendEvent(timerInfo->receiver, &event); + } m_insideTimerEvent = oldInsideTimerEventValue; timerInfo->inTimerEvent = false; @@ -1197,7 +1215,7 @@ void QEventDispatcherSymbian::timerFired(int timerId) return; } -void QEventDispatcherSymbian::wakeUpWasCalled() +void QEventDispatcherSymbian::wakeUpWasCalled(QWakeUpActiveObject *ao) { Q_D(QAbstractEventDispatcher); // The reactivation should happen in RunL, right before the call to this function. @@ -1209,9 +1227,13 @@ void QEventDispatcherSymbian::wakeUpWasCalled() // 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); - //undo the added nesting level around RunIfReady, since Qt's event system also nests - Decrementer dec(d->threadData->loopLevel); - sendPostedEvents(); + if (QtRRActiveScheduler::TestAndClearActiveObjectRunningInRRScheduler(ao)) { + //undo the added nesting level around RunIfReady, since Qt's event system also nests + Decrementer dec(d->threadData->loopLevel); + sendPostedEvents(); + } else { + sendPostedEvents(); + } } void QEventDispatcherSymbian::interrupt() diff --git a/src/corelib/kernel/qeventdispatcher_symbian_p.h b/src/corelib/kernel/qeventdispatcher_symbian_p.h index c520d12..66a439f 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian_p.h +++ b/src/corelib/kernel/qeventdispatcher_symbian_p.h @@ -248,8 +248,8 @@ public: void startingUp(); void closingDown(); - void timerFired(int timerId); - void wakeUpWasCalled(); + void timerFired(int timerId, QTimerActiveObject *ao); + void wakeUpWasCalled(QWakeUpActiveObject *ao); void reactivateSocketNotifier(QSocketNotifier *notifier); void addDeferredActiveObject(QActiveObject *object); diff --git a/src/s60installs/bwins/QtCoreu.def b/src/s60installs/bwins/QtCoreu.def index cfd2cec..d6058d6 100644 --- a/src/s60installs/bwins/QtCoreu.def +++ b/src/s60installs/bwins/QtCoreu.def @@ -3696,7 +3696,7 @@ EXPORTS ?timerEvent@QObject@@MAEXPAVQTimerEvent@@@Z @ 3695 NONAME ; void QObject::timerEvent(class QTimerEvent *) ?timerEvent@QTimeLine@@MAEXPAVQTimerEvent@@@Z @ 3696 NONAME ; void QTimeLine::timerEvent(class QTimerEvent *) ?timerEvent@QTimer@@MAEXPAVQTimerEvent@@@Z @ 3697 NONAME ; void QTimer::timerEvent(class QTimerEvent *) - ?timerFired@QEventDispatcherSymbian@@QAEXH@Z @ 3698 NONAME ; void QEventDispatcherSymbian::timerFired(int) + ?timerFired@QEventDispatcherSymbian@@QAEXH@Z @ 3698 NONAME ABSENT ; void QEventDispatcherSymbian::timerFired(int) ?timerId@QBasicTimer@@QBEHXZ @ 3699 NONAME ; int QBasicTimer::timerId(void) const ?timerId@QTimer@@QBEHXZ @ 3700 NONAME ; int QTimer::timerId(void) const ?timerId@QTimerEvent@@QBEHXZ @ 3701 NONAME ; int QTimerEvent::timerId(void) const @@ -4202,7 +4202,7 @@ EXPORTS ?wakeOne@QWaitCondition@@QAEXXZ @ 4201 NONAME ; void QWaitCondition::wakeOne(void) ?wakeUp@QEventDispatcherSymbian@@UAEXXZ @ 4202 NONAME ; void QEventDispatcherSymbian::wakeUp(void) ?wakeUp@QEventLoop@@QAEXXZ @ 4203 NONAME ; void QEventLoop::wakeUp(void) - ?wakeUpWasCalled@QEventDispatcherSymbian@@QAEXXZ @ 4204 NONAME ; void QEventDispatcherSymbian::wakeUpWasCalled(void) + ?wakeUpWasCalled@QEventDispatcherSymbian@@QAEXXZ @ 4204 NONAME ABSENT ; void QEventDispatcherSymbian::wakeUpWasCalled(void) ?weekNumber@QDate@@QBEHPAH@Z @ 4205 NONAME ; int QDate::weekNumber(int *) const ?width@QRect@@QBEHXZ @ 4206 NONAME ; int QRect::width(void) const ?width@QRectF@@QBEMXZ @ 4207 NONAME ; float QRectF::width(void) const @@ -4889,4 +4889,6 @@ EXPORTS ?removeActiveConnection@QSymbianSocketManager@@QAEXK@Z @ 4888 NONAME ; void QSymbianSocketManager::removeActiveConnection(unsigned long) ?addActiveConnection@QSymbianSocketManager@@QAEXK@Z @ 4889 NONAME ; void QSymbianSocketManager::addActiveConnection(unsigned long) ?activeObjectError@QEventDispatcherSymbian@@QAEXH@Z @ 4890 NONAME ; void QEventDispatcherSymbian::activeObjectError(int) + ?timerFired@QEventDispatcherSymbian@@QAEXHPAVQTimerActiveObject@@@Z @ 4891 NONAME ; void QEventDispatcherSymbian::timerFired(int, class QTimerActiveObject *) + ?wakeUpWasCalled@QEventDispatcherSymbian@@QAEXPAVQWakeUpActiveObject@@@Z @ 4892 NONAME ; void QEventDispatcherSymbian::wakeUpWasCalled(class QWakeUpActiveObject *) diff --git a/src/s60installs/eabi/QtCoreu.def b/src/s60installs/eabi/QtCoreu.def index 44aa48c..7977492 100644 --- a/src/s60installs/eabi/QtCoreu.def +++ b/src/s60installs/eabi/QtCoreu.def @@ -1280,13 +1280,13 @@ EXPORTS _ZN23QCoreApplicationPrivateD1Ev @ 1279 NONAME _ZN23QCoreApplicationPrivateD2Ev @ 1280 NONAME _ZN23QEventDispatcherSymbian10startingUpEv @ 1281 NONAME - _ZN23QEventDispatcherSymbian10timerFiredEi @ 1282 NONAME + _ZN23QEventDispatcherSymbian10timerFiredEi @ 1282 NONAME ABSENT _ZN23QEventDispatcherSymbian11closingDownEv @ 1283 NONAME _ZN23QEventDispatcherSymbian11socketFiredEP19QSocketActiveObject @ 1284 NONAME ABSENT _ZN23QEventDispatcherSymbian13processEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE @ 1285 NONAME _ZN23QEventDispatcherSymbian13registerTimerEiiP7QObject @ 1286 NONAME _ZN23QEventDispatcherSymbian15unregisterTimerEi @ 1287 NONAME - _ZN23QEventDispatcherSymbian15wakeUpWasCalledEv @ 1288 NONAME + _ZN23QEventDispatcherSymbian15wakeUpWasCalledEv @ 1288 NONAME ABSENT _ZN23QEventDispatcherSymbian16hasPendingEventsEv @ 1289 NONAME _ZN23QEventDispatcherSymbian16sendPostedEventsEv @ 1290 NONAME _ZN23QEventDispatcherSymbian16unregisterTimersEP7QObject @ 1291 NONAME @@ -4169,4 +4169,6 @@ EXPORTS _ZN21QSymbianSocketManager22removeActiveConnectionEm @ 4168 NONAME _ZN23QEventDispatcherSymbian17activeObjectErrorEi @ 4169 NONAME _ZNK21QSymbianSocketManager17activeConnectionsEv @ 4170 NONAME + _ZN23QEventDispatcherSymbian10timerFiredEiP18QTimerActiveObject @ 4171 NONAME + _ZN23QEventDispatcherSymbian15wakeUpWasCalledEP19QWakeUpActiveObject @ 4172 NONAME diff --git a/tests/auto/qapplication/tst_qapplication.cpp b/tests/auto/qapplication/tst_qapplication.cpp index 6bc1891..5835fe1 100644 --- a/tests/auto/qapplication/tst_qapplication.cpp +++ b/tests/auto/qapplication/tst_qapplication.cpp @@ -1406,6 +1406,101 @@ public slots: QApplication::sendPostedEvents(0, QEvent::DeferredDelete); QVERIFY(!p); } + +#ifdef Q_OS_SYMBIAN + void deleteLaterAndProcessEventsSymbian() + { + CActiveSchedulerWait *eventLoop = new CActiveSchedulerWait; + currentSymLoop = eventLoop; + + QPointer<QObject> p = this; + deleteLater(); + + // this will not be deleted, but deleteLater on an object within that loop will work + m_ptr = new QObject; + QMetaObject::invokeMethod(m_ptr, "deleteLater", Qt::QueuedConnection); + QTimer::singleShot(100, this, SLOT(quitSymLoop())); + eventLoop->Start(); + QVERIFY(p); + QVERIFY(!m_ptr); + + // further nesting of symbian event loop still works correctly + m_ptr = new QObject; + QMetaObject::invokeMethod(m_ptr, "deleteLater", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "extraSymbianNesting", Qt::QueuedConnection); + QTimer::singleShot(100, this, SLOT(invokeCheckMPtr())); // queue the check event to ensure wakeup runs before we check that deleteLater has not happened + QTimer::singleShot(200, this, SLOT(quitSymLoop())); + QTimer::singleShot(300, this, SLOT(invokeQuitSymLoop())); // need to queue a new event to trigger wakeup, since Symbian's scheduler loop exit doesn't generate events on exit + eventLoop->Start(); + QVERIFY(p); + QVERIFY(!m_ptr); + + // trying to delete this object in a deeper eventloop just won't work + QMetaObject::invokeMethod(this, + "processEventsOnly", + Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "quitSymLoop", Qt::QueuedConnection); + eventLoop->Start(); + QVERIFY(p); + QMetaObject::invokeMethod(this, + "processEventsWithDeferredDeletion", + Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "quitSymLoop", Qt::QueuedConnection); + eventLoop->Start(); + QVERIFY(p); + QMetaObject::invokeMethod(this, + "sendPostedEventsWithDeferredDelete", + Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "quitSymLoop", Qt::QueuedConnection); + eventLoop->Start(); + QVERIFY(p); + + // trying to delete it from this eventloop still doesn't work + QApplication::processEvents(); + QVERIFY(p); + + // however, it *will* work with this magic incantation + QApplication::processEvents(QEventLoop::DeferredDeletion); + QVERIFY(!p); + + delete eventLoop; + currentSymLoop = 0; + } + + void quitSymLoop() + { + currentSymLoop->AsyncStop(); + } + + void invokeQuitSymLoop() + { + QMetaObject::invokeMethod(this, "quitSymLoop", Qt::QueuedConnection); + } + + void extraSymbianNesting() + { + CActiveSchedulerWait *old = currentSymLoop; + CActiveSchedulerWait *thisLevel = new CActiveSchedulerWait; + currentSymLoop = thisLevel; + thisLevel->Start(); + currentSymLoop = old; + delete thisLevel; + } + + void checkMPtr() + { + QVERIFY(m_ptr); + } + + void invokeCheckMPtr() + { + QMetaObject::invokeMethod(this, "checkMPtr", Qt::QueuedConnection); + } + +private: + QPointer<QObject> m_ptr; + CActiveSchedulerWait *currentSymLoop; +#endif }; void tst_QApplication::testDeleteLaterProcessEvents() @@ -1512,6 +1607,23 @@ void tst_QApplication::testDeleteLaterProcessEvents() loop.exec(); QVERIFY(!p); } + +#ifdef Q_OS_SYMBIAN + { + // when the event loop that calls deleteLater() also calls + // processEvents() immediately afterwards, the object should + // not die until the parent loop continues + QApplication app(argc, 0, QApplication::GuiServer); + QEventLoop loop; + EventLoopNester *nester = new EventLoopNester(); + p = nester; + QTimer::singleShot(3000, &loop, SLOT(quit())); + QTimer::singleShot(0, nester, SLOT(deleteLaterAndProcessEventsSymbian())); + + loop.exec(); + QVERIFY(!p); + } +#endif } /* |