diff options
author | Murray Read <ext-murray.2.read@nokia.com> | 2012-02-08 14:16:27 (GMT) |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-02-09 15:23:50 (GMT) |
commit | a13b2a248e5091ddf21e3c5ac08c9ddf0b940b5b (patch) | |
tree | 8f1e58987a7c2178af754d871d40782296fad496 /tests | |
parent | a784bdcabe895ab927cbc28118d427c6e932b9fc (diff) | |
download | Qt-a13b2a248e5091ddf21e3c5ac08c9ddf0b940b5b.zip Qt-a13b2a248e5091ddf21e3c5ac08c9ddf0b940b5b.tar.gz Qt-a13b2a248e5091ddf21e3c5ac08c9ddf0b940b5b.tar.bz2 |
Avoiding early deleteLater in Symbian with better loopLevel tracking
There have been a number of app crashes where deleteLater has been
triggering too early, causing an object to be deleted before it has
been finished with. This was happening when deleteLater was issued
then Symbian's active scheduler loop was nested. Qt keeps track of
loop nesting level to implement deleteLater correctly, but it was
only tracking the event loop in processEvents and QEventLoop correctly.
The wakeup and timer active objects were assuming they were always
run from processEvents and its round robin active scheduler and were
adjusting the loop level to account for this. However if they happened
to run in another event loop, eg the active scheduler, the loop level
adjustment meant that it looked like the event loop was re-running at
the same level, which allowed deleteLater to act.
The fix is to mark active objects as being run from the RR scheduler,
then the wakeup and timer active objects can be tested to see which
type of scheduler they are actually running in. With this knowledge,
the correct loop level adjustment can be made, and deleteLater runs
at the correct time.
Task-number: ou1cimx1#947013
Change-Id: Id05cd63ad10e100ea807cc276844aaa36c614351
Reviewed-by: Gareth Stockwell <ext-gareth.stockwell@nokia.com>
Reviewed-by: Shane Kearns <ext-shane.2.kearns@nokia.com>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/qapplication/tst_qapplication.cpp | 112 |
1 files changed, 112 insertions, 0 deletions
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 } /* |