If I understand the following code from the QFutureWatcher documentation correctly, then there is a race condition between the last to lines:
// Instantiate the objects and connect to the finished signal.
MyClass myObject;
QFutureWatcher<int> watcher;
connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished()));
// Start the computation.
QFuture<int> future = QtConcurrent::run(...);
watcher.setFuture(future);
If the function ...
in the QtConcurrent::run(...)
finishes before the next line is called, then the watcher.finished()
signal will never be triggered. Is my assumption correct? How do I work around this bug?
From http://doc.qt.io/qt-4.8/qfuturewatcher.html#setFuture
One of the signals might be emitted for the current state of the
future. For example, if the future is already stopped, the finished
signal will be emitted.
In other words, if QtConcurrent::run(...)
completes before setFuture
is called, setFuture
will still emit a signal on the current state of the QFuture
. So, you don't need to do anything in order to avoid a race condition.
However, depending on the rest of your code, you may need to call QFuture::waitForFinished()
in order to ensure that your MyClass
, QFuture
and QFutureWatcher
do not go out of scope before QtConcurrent::run(...)
completes.
I was running this snipped in a unit test and QSignalSpy
was not getting the signals from QFutureWatcher
. I solved the issue by calling explicitely QCoreApplication::processEvents()
before the check:
QFutureWatcher<int> watcher;
QSignalSpy spy(&watcher, &QFutureWatcher<int>::finished);
QFuture<int> future = QtConcurrent::run([](){
qDebug() << "compute";
return 42;
});
watcher.setFuture(future);
QCOMPARE(watcher.result(), 42);
QCOMPARE(spy.count(), 0);
QCoreApplication::processEvents();
QCOMPARE(spy.count(), 1);
The signal is probably emitted from a thread, and in this situation, the signal is queued instead of being directly executed.