Don't queue events in QFutureWatcher when pause is requested

When QFutureWatcher (or QFutureInterface) is paused, it doesn't
mean that it will take effect immediately: the pending tasks may
still be in progress and keep reporting results. At the moment
QFutureWatcher will queue those events and report only with the
next resume. This behavior is wrong, QFutureWatcher should not
decide when to report events, the sender should decide when is the
right time. There's no benefit in reporting already happened events
with delay. Because of this, even the pause event itself was being
reported after resume.

Fixed the behavior by removing the logic of queueing events when
the state is set to "paused". It seems unlikely that the users of
QFutureWatcher rely on reporting events with delay.

[ChangeLog][Important Behavior Changes][QtCore] QFutureWatcher will not
immediately stop delivering progress and result ready signals when the
future is paused. At the moment of pausing there may be still computations
that are in progress and cannot be stopped. Signals for such computations
will be still delivered after pause, instead of being postponed and
reported only after next resume.

Fixes: QTBUG-12152
Change-Id: I9f0b545ac096578c52cc72d60575c018c01e3368
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
bb10
Sona Kurazyan 2020-05-19 14:35:39 +02:00
parent d8a2456fbf
commit d7d6a1fe32
3 changed files with 23 additions and 30 deletions

View File

@ -137,9 +137,11 @@ void QFutureWatcherBase::cancel()
If \a paused is true, this function pauses the asynchronous computation
represented by the future(). If the computation is already paused, this
function does nothing. This QFutureWatcher will stop delivering progress
and result ready signals while the future is paused. Signal delivery will
continue once the computation is resumed.
function does nothing. QFutureWatcher will not immediately stop delivering
progress and result ready signals when the future is paused. At the moment
of pausing there may still be computations that are in progress and cannot
be stopped. Signals for such computations will still be delivered after
pause.
If \a paused is false, this function resumes the asynchronous computation.
If the computation was not previously paused, this function does nothing.
@ -314,25 +316,7 @@ bool QFutureWatcherBase::event(QEvent *event)
Q_D(QFutureWatcherBase);
if (event->type() == QEvent::FutureCallOut) {
QFutureCallOutEvent *callOutEvent = static_cast<QFutureCallOutEvent *>(event);
if (futureInterface().isPaused()) {
d->pendingCallOutEvents.append(callOutEvent->clone());
return true;
}
if (callOutEvent->callOutType == QFutureCallOutEvent::Resumed
&& !d->pendingCallOutEvents.isEmpty()) {
// send the resume
d->sendCallOutEvent(callOutEvent);
// next send all pending call outs
for (int i = 0; i < d->pendingCallOutEvents.count(); ++i)
d->sendCallOutEvent(d->pendingCallOutEvents.at(i));
qDeleteAll(d->pendingCallOutEvents);
d->pendingCallOutEvents.clear();
} else {
d->sendCallOutEvent(callOutEvent);
}
d->sendCallOutEvent(callOutEvent);
return true;
}
return QObject::event(event);
@ -403,8 +387,6 @@ void QFutureWatcherBase::disconnectOutputInterface(bool pendingAssignment)
if (pendingAssignment) {
Q_D(QFutureWatcherBase);
d->pendingResultsReady.storeRelaxed(0);
qDeleteAll(d->pendingCallOutEvents);
d->pendingCallOutEvents.clear();
d->finished = false; /* May soon be amended, during connectOutputInterface() */
}
@ -541,7 +523,15 @@ void QFutureWatcherBasePrivate::sendCallOutEvent(QFutureCallOutEvent *event)
*/
/*! \fn template <typename T> void QFutureWatcher<T>::paused()
This signal is emitted when the watched future is paused.
This signal is emitted when the state of the watched future is
set to paused.
\note This signal only informs that pause has been requested. It
doesn't indicate that all background operations are stopped. Signals
for computations that were in progress at the moment of pausing will
still be delivered.
\sa setPaused(), pause()
*/
/*! \fn template <typename T> void QFutureWatcher<T>::resumed()

View File

@ -74,7 +74,6 @@ public:
void sendCallOutEvent(QFutureCallOutEvent *event);
QList<QFutureCallOutEvent *> pendingCallOutEvents;
QAtomicInt pendingResultsReady;
int maximumPendingResultsReady;

View File

@ -678,18 +678,22 @@ void tst_QFutureWatcher::pauseEvents()
QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
QVERIFY(resultReadySpy.isValid());
QSignalSpy pauseSpy(&watcher, &QFutureWatcher<int>::paused);
QVERIFY(pauseSpy.isValid());
watcher.setFuture(iface.future());
watcher.pause();
QTRY_COMPARE(pauseSpy.count(), 1);
int value = 0;
iface.reportFinished(&value);
QTest::qWait(10);
QCOMPARE(resultReadySpy.count(), 0);
// A result is reported, although the watcher is paused.
// The corresponding event should be also reported.
QTRY_COMPARE(resultReadySpy.count(), 1);
watcher.resume();
QTRY_VERIFY2(!resultReadySpy.isEmpty(), "Result didn't arrive");
QCOMPARE(resultReadySpy.count(), 1);
}
{
QFutureInterface<int> iface;