Optimize QPromise destructor
Unify cancel and finish in QPromise destructor in a single call. This saves us one extra mutex lock and atomic state change. Task-number: QTBUG-84977 Change-Id: Iac06302c39a2863008b27325fcf6792d4f58c8ae Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
22248a4e96
commit
ce59ccb334
@ -110,14 +110,36 @@ static inline int switch_from_to(QAtomicInt &a, int from, int to)
|
||||
|
||||
void QFutureInterfaceBase::cancel()
|
||||
{
|
||||
QMutexLocker locker(&d->m_mutex);
|
||||
if (d->state.loadRelaxed() & Canceled)
|
||||
return;
|
||||
cancel(CancelMode::CancelOnly);
|
||||
}
|
||||
|
||||
void QFutureInterfaceBase::cancel(QFutureInterfaceBase::CancelMode mode)
|
||||
{
|
||||
QMutexLocker locker(&d->m_mutex);
|
||||
|
||||
const auto oldState = d->state.loadRelaxed();
|
||||
|
||||
switch (mode) {
|
||||
case CancelMode::CancelAndFinish:
|
||||
if ((oldState & Finished) && (oldState & Canceled))
|
||||
return;
|
||||
switch_from_to(d->state, suspendingOrSuspended | Running, Canceled | Finished);
|
||||
break;
|
||||
case CancelMode::CancelOnly:
|
||||
if (oldState & Canceled)
|
||||
return;
|
||||
switch_from_to(d->state, suspendingOrSuspended, Canceled);
|
||||
break;
|
||||
}
|
||||
|
||||
switch_from_to(d->state, suspendingOrSuspended, Canceled);
|
||||
d->waitCondition.wakeAll();
|
||||
d->pausedWaitCondition.wakeAll();
|
||||
d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled));
|
||||
|
||||
if (!(oldState & Canceled))
|
||||
d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled));
|
||||
if (mode == CancelMode::CancelAndFinish && !(oldState & Finished))
|
||||
d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Finished));
|
||||
|
||||
d->isValid = false;
|
||||
}
|
||||
|
||||
|
@ -154,6 +154,8 @@ public:
|
||||
int loadState() const;
|
||||
|
||||
void cancel();
|
||||
void cancelAndFinish() { cancel(CancelMode::CancelAndFinish); }
|
||||
|
||||
void setSuspended(bool suspend);
|
||||
void toggleSuspended();
|
||||
void reportSuspended() const;
|
||||
@ -216,6 +218,9 @@ protected:
|
||||
bool launchAsync() const;
|
||||
|
||||
bool isRunningOrPending() const;
|
||||
|
||||
enum class CancelMode { CancelOnly, CancelAndFinish };
|
||||
void cancel(CancelMode mode);
|
||||
};
|
||||
|
||||
inline void swap(QFutureInterfaceBase &lhs, QFutureInterfaceBase &rhs) noexcept
|
||||
|
@ -70,10 +70,8 @@ public:
|
||||
return;
|
||||
// Otherwise, if computation is not finished at this point, cancel
|
||||
// potential waits
|
||||
if (!(state & QFutureInterfaceBase::State::Finished)) {
|
||||
d.cancel();
|
||||
finish(); // required to finalize the state
|
||||
}
|
||||
if (!(state & QFutureInterfaceBase::State::Finished))
|
||||
d.cancelAndFinish(); // cancel and finalize the state
|
||||
}
|
||||
|
||||
// Core QPromise APIs
|
||||
|
@ -107,6 +107,7 @@ private slots:
|
||||
void futureInterface();
|
||||
void refcounting();
|
||||
void cancel();
|
||||
void cancelAndFinish();
|
||||
void statePropagation();
|
||||
void multipleResults();
|
||||
void indexedResults();
|
||||
@ -865,6 +866,39 @@ void tst_QFuture::cancel()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFuture::cancelAndFinish()
|
||||
{
|
||||
{
|
||||
QFutureInterface<void> fi;
|
||||
|
||||
fi.reportStarted();
|
||||
fi.cancelAndFinish();
|
||||
|
||||
QVERIFY(fi.isStarted());
|
||||
QVERIFY(!fi.isRunning());
|
||||
QVERIFY(!fi.isSuspended());
|
||||
QVERIFY(!fi.isSuspending());
|
||||
QVERIFY(fi.isCanceled());
|
||||
QVERIFY(fi.isFinished());
|
||||
}
|
||||
|
||||
// The same with suspended state
|
||||
{
|
||||
QFutureInterface<void> fi;
|
||||
|
||||
fi.reportStarted();
|
||||
fi.setSuspended(true);
|
||||
fi.cancelAndFinish();
|
||||
|
||||
QVERIFY(fi.isStarted());
|
||||
QVERIFY(!fi.isRunning());
|
||||
QVERIFY(!fi.isSuspended());
|
||||
QVERIFY(!fi.isSuspending());
|
||||
QVERIFY(fi.isCanceled());
|
||||
QVERIFY(fi.isFinished());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFuture::statePropagation()
|
||||
{
|
||||
QFuture<void> f1;
|
||||
|
@ -46,6 +46,8 @@ private slots:
|
||||
void startFinish();
|
||||
void progressValueChanged();
|
||||
void canceled();
|
||||
void cancelAndFinish_data();
|
||||
void cancelAndFinish();
|
||||
void resultAt();
|
||||
void resultReadyAt();
|
||||
void futureSignals();
|
||||
@ -229,6 +231,42 @@ void tst_QFutureWatcher::canceled()
|
||||
future.waitForFinished();
|
||||
}
|
||||
|
||||
void tst_QFutureWatcher::cancelAndFinish_data()
|
||||
{
|
||||
QTest::addColumn<bool>("isCanceled");
|
||||
QTest::addColumn<bool>("isFinished");
|
||||
|
||||
QTest::addRow("running") << false << false;
|
||||
QTest::addRow("canceled") << true << false;
|
||||
QTest::addRow("finished") << false << true;
|
||||
QTest::addRow("canceledAndFinished") << true << true;
|
||||
}
|
||||
|
||||
void tst_QFutureWatcher::cancelAndFinish()
|
||||
{
|
||||
QFETCH(bool, isCanceled);
|
||||
QFETCH(bool, isFinished);
|
||||
|
||||
QFutureInterface<void> fi;
|
||||
QFutureWatcher<void> futureWatcher;
|
||||
QSignalSpy finishedSpy(&futureWatcher, &QFutureWatcher<void>::finished);
|
||||
QSignalSpy canceledSpy(&futureWatcher, &QFutureWatcher<void>::canceled);
|
||||
futureWatcher.setFuture(fi.future());
|
||||
|
||||
fi.reportStarted();
|
||||
|
||||
if (isCanceled)
|
||||
fi.cancel();
|
||||
if (isFinished)
|
||||
fi.reportFinished();
|
||||
|
||||
fi.cancelAndFinish();
|
||||
|
||||
// The signals should be emitted only once
|
||||
QTRY_COMPARE(canceledSpy.count(), 1);
|
||||
QTRY_COMPARE(finishedSpy.count(), 1);
|
||||
}
|
||||
|
||||
class IntTask : public RunFunctionTaskBase<int>
|
||||
{
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user