summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/thread/qfutureinterface.h17
-rw-r--r--src/corelib/thread/qpromise.qdoc3
-rw-r--r--src/corelib/thread/qresultstore.cpp14
-rw-r--r--src/corelib/thread/qresultstore.h23
-rw-r--r--tests/auto/corelib/thread/qfuture/tst_qfuture.cpp182
-rw-r--r--tests/auto/corelib/thread/qpromise/tst_qpromise.cpp19
6 files changed, 244 insertions, 14 deletions
diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h
index 6c7c511cc0..9296c63f0b 100644
--- a/src/corelib/thread/qfutureinterface.h
+++ b/src/corelib/thread/qfutureinterface.h
@@ -270,11 +270,12 @@ inline void QFutureInterface<T>::reportResult(const T *result, int index)
if (store.filterMode()) {
const int resultCountBefore = store.count();
- store.addResult<T>(index, result);
- this->reportResultsReady(resultCountBefore, store.count());
+ if (store.addResult<T>(index, result) != -1)
+ this->reportResultsReady(resultCountBefore, store.count());
} else {
const int insertIndex = store.addResult<T>(index, result);
- this->reportResultsReady(insertIndex, insertIndex + 1);
+ if (insertIndex != -1)
+ this->reportResultsReady(insertIndex, insertIndex + 1);
}
}
@@ -289,7 +290,8 @@ void QFutureInterface<T>::reportAndMoveResult(T &&result, int index)
const int oldResultCount = store.count();
const int insertIndex = store.moveResult(index, std::forward<T>(result));
- if (!store.filterMode() || oldResultCount < store.count()) // Let's make sure it's not in pending results.
+ // Let's make sure it's not in pending results.
+ if (insertIndex != -1 && (!store.filterMode() || oldResultCount < store.count()))
reportResultsReady(insertIndex, store.count());
}
@@ -317,11 +319,12 @@ inline void QFutureInterface<T>::reportResults(const QList<T> &_results, int beg
if (store.filterMode()) {
const int resultCountBefore = store.count();
- store.addResults(beginIndex, &_results, count);
- this->reportResultsReady(resultCountBefore, store.count());
+ if (store.addResults(beginIndex, &_results, count) != -1)
+ this->reportResultsReady(resultCountBefore, store.count());
} else {
const int insertIndex = store.addResults(beginIndex, &_results, count);
- this->reportResultsReady(insertIndex, insertIndex + _results.count());
+ if (insertIndex != -1)
+ this->reportResultsReady(insertIndex, insertIndex + _results.count());
}
}
diff --git a/src/corelib/thread/qpromise.qdoc b/src/corelib/thread/qpromise.qdoc
index 9c7925da95..4b7b497b3f 100644
--- a/src/corelib/thread/qpromise.qdoc
+++ b/src/corelib/thread/qpromise.qdoc
@@ -112,6 +112,9 @@
Adds \a result to the internal result collection at \a index position. If
index is unspecified, \a result is added to the end of the collection.
+ \note addResult() rejects \a result if there's already another result in the
+ collection stored at the same index.
+
You can get a result at a specific index by calling QFuture::resultAt().
\note It is possible to specify an arbitrary index and request result at
diff --git a/src/corelib/thread/qresultstore.cpp b/src/corelib/thread/qresultstore.cpp
index b6af27dfa9..a239954dbe 100644
--- a/src/corelib/thread/qresultstore.cpp
+++ b/src/corelib/thread/qresultstore.cpp
@@ -144,6 +144,11 @@ bool ResultIteratorBase::canIncrementVectorIndex() const
return (m_vectorIndex + 1 < mapIterator.value().m_count);
}
+bool ResultIteratorBase::isValid() const
+{
+ return mapIterator.value().isValid();
+}
+
ResultStoreBase::ResultStoreBase()
: insertIndex(0), resultCount(0), m_filterMode(false), filteredResults(0) { }
@@ -196,6 +201,15 @@ int ResultStoreBase::insertResultItem(int index, ResultItem &resultItem)
return storeIndex;
}
+bool ResultStoreBase::containsValidResultItem(int index) const
+{
+ // index might refer to either visible or pending result
+ const bool inPending = m_filterMode && index != -1 && index > insertIndex;
+ const auto &store = inPending ? pendingResults : m_results;
+ auto it = findResult(store, index);
+ return it != ResultIteratorBase(store.end()) && it.isValid();
+}
+
void ResultStoreBase::syncPendingResults()
{
// check if we can insert any of the pending results:
diff --git a/src/corelib/thread/qresultstore.h b/src/corelib/thread/qresultstore.h
index 5f8c7f150a..0a1382fd79 100644
--- a/src/corelib/thread/qresultstore.h
+++ b/src/corelib/thread/qresultstore.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -87,6 +87,8 @@ public:
bool operator!=(const ResultIteratorBase &other) const;
bool isVector() const;
bool canIncrementVectorIndex() const;
+ bool isValid() const;
+
protected:
QMap<int, ResultItem>::const_iterator mapIterator;
int m_vectorIndex;
@@ -139,6 +141,7 @@ public:
protected:
int insertResultItem(int index, ResultItem &resultItem);
void insertResultItemIfValid(int index, ResultItem &resultItem);
+ bool containsValidResultItem(int index) const;
void syncPendingResults();
void syncResultCount();
int updateInsertIndex(int index, int _count);
@@ -169,6 +172,9 @@ public:
template <typename T>
int addResult(int index, const T *result)
{
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
if (result == nullptr)
return addResult(index, static_cast<void *>(nullptr));
@@ -178,18 +184,27 @@ public:
template <typename T>
int moveResult(int index, T &&result)
{
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
return addResult(index, static_cast<void *>(new T(std::move_if_noexcept(result))));
}
template<typename T>
int addResults(int index, const QList<T> *results)
{
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
return addResults(index, new QList<T>(*results), results->count(), results->count());
}
template<typename T>
int addResults(int index, const QList<T> *results, int totalCount)
{
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
if (m_filterMode == true && results->count() != totalCount && 0 == results->count())
return addResults(index, nullptr, 0, totalCount);
@@ -198,12 +213,18 @@ public:
int addCanceledResult(int index)
{
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
return addResult(index, static_cast<void *>(nullptr));
}
template <typename T>
int addCanceledResults(int index, int _count)
{
+ if (containsValidResultItem(index)) // reject if already present
+ return -1;
+
QList<T> empty;
return addResults(index, &empty, _count);
}
diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
index 23ff646fb7..99f2144528 100644
--- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
+++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
@@ -149,6 +149,11 @@ private slots:
void signalConnect();
void waitForFinished();
+ void rejectResultOverwrite_data();
+ void rejectResultOverwrite();
+ void rejectPendingResultOverwrite_data() { rejectResultOverwrite_data(); }
+ void rejectPendingResultOverwrite();
+
private:
using size_type = std::vector<int>::size_type;
@@ -3109,5 +3114,182 @@ void tst_QFuture::waitForFinished()
QVERIFY(waitingThread->isFinished());
}
+void tst_QFuture::rejectResultOverwrite_data()
+{
+ QTest::addColumn<bool>("filterMode");
+ QTest::addColumn<QList<int>>("initResults");
+
+ QTest::addRow("filter-mode-on-1-result") << true << QList<int>({ 456 });
+ QTest::addRow("filter-mode-on-N-results") << true << QList<int>({ 456, 789 });
+ QTest::addRow("filter-mode-off-1-result") << false << QList<int>({ 456 });
+ QTest::addRow("filter-mode-off-N-results") << false << QList<int>({ 456, 789 });
+}
+
+void tst_QFuture::rejectResultOverwrite()
+{
+ QFETCH(bool, filterMode);
+ QFETCH(QList<int>, initResults);
+
+ QFutureInterface<int> iface;
+ iface.setFilterMode(filterMode);
+ auto f = iface.future();
+ QFutureWatcher<int> watcher;
+ watcher.setFuture(f);
+
+ QTestEventLoop eventProcessor;
+ // control the loop by suspend
+ connect(&watcher, &QFutureWatcher<int>::suspending, &eventProcessor, &QTestEventLoop::exitLoop);
+ // internal machinery always emits resultsReadyAt
+ QSignalSpy resultCounter(&watcher, &QFutureWatcher<int>::resultsReadyAt);
+
+ // init
+ if (initResults.size() == 1)
+ iface.reportResult(initResults[0]);
+ else
+ iface.reportResults(initResults);
+ QCOMPARE(f.resultCount(), initResults.size());
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ QCOMPARE(f.results(), initResults);
+
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ // Run event loop, QCoreApplication::postEvent is in use
+ // in QFutureInterface:
+ eventProcessor.enterLoopMSecs(2000);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.count(), 1);
+ f.resume();
+
+ // overwrite with lvalue
+ {
+ int result = -1;
+ const auto originalCount = f.resultCount();
+ iface.reportResult(result, 0);
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ }
+ // overwrite with rvalue
+ {
+ const auto originalCount = f.resultCount();
+ iface.reportResult(-1, 0);
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ }
+ // overwrite with array
+ {
+ const auto originalCount = f.resultCount();
+ iface.reportResults(QList<int> { -1, -2, -3 }, 0);
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ }
+
+ // special case: add result by different index, overlapping with the vector
+ if (initResults.size() > 1) {
+ const auto originalCount = f.resultCount();
+ iface.reportResult(-1, 1);
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(1), initResults[1]);
+ }
+
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ eventProcessor.enterLoopMSecs(2000);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.count(), 1);
+ f.resume();
+ QCOMPARE(f.results(), initResults);
+}
+
+void tst_QFuture::rejectPendingResultOverwrite()
+{
+ QFETCH(bool, filterMode);
+ QFETCH(QList<int>, initResults);
+
+ QFutureInterface<int> iface;
+ iface.setFilterMode(filterMode);
+ auto f = iface.future();
+ QFutureWatcher<int> watcher;
+ watcher.setFuture(f);
+
+ QTestEventLoop eventProcessor;
+ // control the loop by suspend
+ connect(&watcher, &QFutureWatcher<int>::suspending, &eventProcessor, &QTestEventLoop::exitLoop);
+ // internal machinery always emits resultsReadyAt
+ QSignalSpy resultCounter(&watcher, &QFutureWatcher<int>::resultsReadyAt);
+
+ // init
+ if (initResults.size() == 1)
+ iface.reportResult(initResults[0], 1);
+ else
+ iface.reportResults(initResults, 1);
+ QCOMPARE(f.resultCount(), 0); // not visible yet
+ if (!filterMode) {
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ QCOMPARE(f.results(), initResults);
+
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ // Run event loop, QCoreApplication::postEvent is in use
+ // in QFutureInterface:
+ eventProcessor.enterLoopMSecs(2000);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.count(), 1);
+ f.resume();
+ }
+
+ // overwrite with lvalue
+ {
+ int result = -1;
+ const auto originalCount = f.resultCount();
+ iface.reportResult(result, 1);
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ }
+ // overwrite with rvalue
+ {
+ const auto originalCount = f.resultCount();
+ iface.reportResult(-1, 1);
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ }
+ // overwrite with array
+ {
+ const auto originalCount = f.resultCount();
+ iface.reportResults(QList<int> { -1, -2 }, 1);
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ }
+ // special case: add result by different index, overlapping with the vector
+ if (initResults.size() > 1) {
+ const auto originalCount = f.resultCount();
+ iface.reportResult(-1, 2);
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(2), initResults[1]);
+ }
+
+ if (!filterMode) {
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ eventProcessor.enterLoopMSecs(2000);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.count(), 1);
+ f.resume();
+ }
+
+ iface.reportResult(123, 0); // make results at 0 and 1 accessible
+ QCOMPARE(f.resultCount(), initResults.size() + 1);
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ initResults.prepend(123);
+ QCOMPARE(f.results(), initResults);
+}
+
QTEST_MAIN(tst_QFuture)
#include "tst_qfuture.moc"
diff --git a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
index 2b8853ccd7..62e3711d42 100644
--- a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
+++ b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
@@ -169,12 +169,12 @@ void tst_QPromise::addResult()
auto f = promise.future();
// add as lvalue
+ int resultAt0 = 456;
{
- int result = 456;
- promise.addResult(result);
+ promise.addResult(resultAt0);
QCOMPARE(f.resultCount(), 1);
- QCOMPARE(f.result(), result);
- QCOMPARE(f.resultAt(0), result);
+ QCOMPARE(f.result(), resultAt0);
+ QCOMPARE(f.resultAt(0), resultAt0);
}
// add as rvalue
{
@@ -190,13 +190,20 @@ void tst_QPromise::addResult()
QCOMPARE(f.resultCount(), 3);
QCOMPARE(f.resultAt(2), result);
}
- // add at position and overwrite
+ // add as lvalue at position and overwrite
{
int result = -1;
const auto originalCount = f.resultCount();
promise.addResult(result, 0);
QCOMPARE(f.resultCount(), originalCount);
- QCOMPARE(f.resultAt(0), result);
+ QCOMPARE(f.resultAt(0), resultAt0); // overwrite does not work
+ }
+ // add as rvalue at position and overwrite
+ {
+ const auto originalCount = f.resultCount();
+ promise.addResult(-1, 0);
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), resultAt0); // overwrite does not work
}
}