[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