[Interest] Ensuring that a queued invocation occurs after deferred deletion

Curtis Mitch mitch.curtis at theqtcompany.com
Tue Mar 22 18:50:02 CET 2016


I recently discovered [1] that Loader defers deletion of items via deleteLater(). Up until that point, I had been treating certain operations in my program as synchronous (as I haven't introduced threads yet). Now that I can't safely assume that UI items will be instantly destroyed, I have to convert these operations into asynchronous ones.

For example, previously, I had this code:

game.quitGame();

My idea is to turn it into this:

game.requestQuitGame();

Within this function, the Game object would set its "ready" property to false, emitting its associated property change signal so that Loaders can set active to false. Then, QMetaObject::invoke would be called with Qt::QueuedConnection to ensure that the Loader's deleteLater() calls would have been carried out *before* tearing down the game and its objects.

In order to confirm that invokeMethod() works the way I thought it did, I added the following debug statements to QEventLoop:

diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp
index dca25ce..7dae9d0 100644
--- a/src/corelib/kernel/qeventloop.cpp
+++ b/src/corelib/kernel/qeventloop.cpp
@@ -151,6 +151,7 @@ bool QEventLoop::processEvents(ProcessEventsFlags flags)

     \sa QCoreApplication::quit(), exit(), processEvents()
 */
+#include <QDebug>
 int QEventLoop::exec(ProcessEventsFlags flags)
 {
     Q_D(QEventLoop);
@@ -200,8 +201,11 @@ int QEventLoop::exec(ProcessEventsFlags flags)
     if (app && app->thread() == thread())
         QCoreApplication::removePostedEvents(app, QEvent::Quit);

-    while (!d->exit.loadAcquire())
+    while (!d->exit.loadAcquire()) {
+        qDebug() << Q_FUNC_INFO << "--- beginning event loop";
         processEvents(flags | WaitForMoreEvents | EventLoopExec);
+        qDebug() << Q_FUNC_INFO << "--- ending event loop";
+    }

     ref.exceptionCaught = false;
     return d->returnCode.load();

It turns out that I misunderstood the documentation; it only says that the slot is invoked when control returns to the event loop of the receiver's thread. So, as I understand it, it's possible that the invocation could happen *before* the deferred deletion of the Loaders' items. As the documentation doesn't specify the order between these two things, I should probably assume that it's not safe to assume anything.

So, I'm left with the problem of how to ensure that a slot is invoked after the Loaders' items have been destroyed. My only thought is to use a zero-second single-shot timer. The question is: is this guaranteed to happen *after* deferred deletion for a given iteration of an event loop? I can't see such a guarantee in the documentation. I even checked the source code of e.g. qeventdispatcher_win.cpp to see if I could find anything, without success.

Another question that's in the back of my mind is: is there a better way to do this?

[1] https://bugreports.qt.io/browse/QTBUG-51995
[2] http://doc.qt.io/qt-5/qt.html#ConnectionType-enum


More information about the Interest mailing list