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:
Sona Kurazyan 2021-10-06 10:04:54 +02:00
parent 22248a4e96
commit ce59ccb334
5 changed files with 106 additions and 9 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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: