QFuture: Wait for result on iterator advance
Wait for the result at the target index if the future is running and the iterator index is past the current result count. Determine if there is a result at the target index after waitForResult() returns, and return -1/end if not. Also support decrementing the end iterator. In this case wait for the future to finish in order to get the final result count. Task-number: QTBUG-59811 Change-Id: I8fcc711bab2e72c3c5196a55b794d25e18bb324d Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
486c55d743
commit
6faa4d4a87
@ -111,33 +111,79 @@ public:
|
||||
typedef const T &reference;
|
||||
|
||||
inline const_iterator() {}
|
||||
inline const_iterator(QFuture const * const _future, int _index) : future(_future), index(_index) {}
|
||||
inline const_iterator(QFuture const * const _future, int _index)
|
||||
: future(_future), index(advanceIndex(_index, 0)) { }
|
||||
inline const_iterator(const const_iterator &o) : future(o.future), index(o.index) {}
|
||||
inline const_iterator &operator=(const const_iterator &o)
|
||||
{ future = o.future; index = o.index; return *this; }
|
||||
inline const T &operator*() const { return future->d.resultReference(index); }
|
||||
inline const T *operator->() const { return future->d.resultPointer(index); }
|
||||
|
||||
inline bool operator!=(const const_iterator &other) const
|
||||
inline bool operator!=(const const_iterator &other) const { return index != other.index; }
|
||||
inline bool operator==(const const_iterator &o) const { return !operator!=(o); }
|
||||
inline const_iterator &operator++()
|
||||
{ index = advanceIndex(index, 1); return *this; }
|
||||
inline const_iterator &operator--()
|
||||
{ index = advanceIndex(index, -1); return *this; }
|
||||
inline const_iterator operator++(int)
|
||||
{
|
||||
if (index == -1 && other.index == -1) // comparing end != end?
|
||||
return false;
|
||||
if (other.index == -1)
|
||||
return (future->isRunning() || (index < future->resultCount()));
|
||||
return (index != other.index);
|
||||
const_iterator r = *this;
|
||||
index = advanceIndex(index, 1);
|
||||
return r;
|
||||
}
|
||||
inline const_iterator operator--(int)
|
||||
{
|
||||
const_iterator r = *this;
|
||||
index = advanceIndex(index, -1);
|
||||
return r;
|
||||
}
|
||||
inline const_iterator operator+(int j) const
|
||||
{ return const_iterator(future, advanceIndex(index, j)); }
|
||||
inline const_iterator operator-(int j) const
|
||||
{ return const_iterator(future, advanceIndex(index, -j)); }
|
||||
inline const_iterator &operator+=(int j)
|
||||
{ index = advanceIndex(index, j); return *this; }
|
||||
inline const_iterator &operator-=(int j)
|
||||
{ index = advanceIndex(index, -j); return *this; }
|
||||
friend inline const_iterator operator+(int j, const_iterator k)
|
||||
{ return const_iterator(k.future, k.advanceIndex(k.index, j)); }
|
||||
|
||||
private:
|
||||
/*! \internal
|
||||
|
||||
Advances the iterator index \a idx \a n steps, waits for the
|
||||
result at the target index, and returns the target index.
|
||||
|
||||
The index may be -1, indicating the end iterator, either
|
||||
as the argument or as the return value. The end iterator
|
||||
may be decremented.
|
||||
|
||||
The caller is responsible for not advancing the iterator
|
||||
before begin() or past end(), with the exception that
|
||||
attempting to advance a non-end iterator past end() for
|
||||
a running future is allowed and will return the end iterator.
|
||||
|
||||
Note that n == 0 is valid and will wait for the result
|
||||
at the given index.
|
||||
*/
|
||||
int advanceIndex(int idx, int n) const
|
||||
{
|
||||
// The end iterator can be decremented, leave as-is for other cases
|
||||
if (idx == -1 && n >= 0)
|
||||
return idx;
|
||||
|
||||
// Special case for decrementing the end iterator: wait for
|
||||
// finished to get the total result count.
|
||||
if (idx == -1 && future->isRunning())
|
||||
future->d.waitForFinished();
|
||||
|
||||
// Wait for result at target index
|
||||
const int targetIndex = (idx == -1) ? future->resultCount() + n : idx + n;
|
||||
future->d.waitForResult(targetIndex);
|
||||
|
||||
// After waiting there is either a result or the end was reached
|
||||
return (targetIndex < future->resultCount()) ? targetIndex : -1;
|
||||
}
|
||||
|
||||
inline bool operator==(const const_iterator &o) const { return !operator!=(o); }
|
||||
inline const_iterator &operator++() { ++index; return *this; }
|
||||
inline const_iterator operator++(int) { const_iterator r = *this; ++index; return r; }
|
||||
inline const_iterator &operator--() { --index; return *this; }
|
||||
inline const_iterator operator--(int) { const_iterator r = *this; --index; return r; }
|
||||
inline const_iterator operator+(int j) const { return const_iterator(future, index + j); }
|
||||
inline const_iterator operator-(int j) const { return const_iterator(future, index - j); }
|
||||
inline const_iterator &operator+=(int j) { index += j; return *this; }
|
||||
inline const_iterator &operator-=(int j) { index -= j; return *this; }
|
||||
friend inline const_iterator operator+(int j, const_iterator k) { return k + j; }
|
||||
private:
|
||||
QFuture const * future;
|
||||
int index;
|
||||
};
|
||||
|
@ -49,6 +49,24 @@ struct ResultStoreInt : QtPrivate::ResultStoreBase
|
||||
~ResultStoreInt() { clear<int>(); }
|
||||
};
|
||||
|
||||
class LambdaThread : public QThread
|
||||
{
|
||||
public:
|
||||
LambdaThread(std::function<void ()> fn)
|
||||
:m_fn(fn)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
m_fn();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void ()> m_fn;
|
||||
};
|
||||
|
||||
class tst_QFuture: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -67,6 +85,7 @@ private slots:
|
||||
void resultsAsList();
|
||||
void implicitConversions();
|
||||
void iterators();
|
||||
void iteratorsThread();
|
||||
void pause();
|
||||
void throttling();
|
||||
void voidConversions();
|
||||
@ -1144,6 +1163,54 @@ void tst_QFuture::iterators()
|
||||
}
|
||||
}
|
||||
}
|
||||
void tst_QFuture::iteratorsThread()
|
||||
{
|
||||
const int expectedResultCount = 10;
|
||||
const int delay = 10;
|
||||
QFutureInterface<int> futureInterface;
|
||||
|
||||
// Create result producer thread. The results are
|
||||
// produced with delays in order to make the consumer
|
||||
// wait.
|
||||
QSemaphore sem;
|
||||
LambdaThread thread = {[=, &futureInterface, &sem](){
|
||||
for (int i = 1; i <= expectedResultCount; i += 2) {
|
||||
int result = i;
|
||||
futureInterface.reportResult(&result);
|
||||
result = i + 1;
|
||||
futureInterface.reportResult(&result);
|
||||
}
|
||||
|
||||
sem.acquire(2);
|
||||
futureInterface.reportFinished();
|
||||
}};
|
||||
|
||||
futureInterface.reportStarted();
|
||||
QFuture<int> future = futureInterface.future();
|
||||
|
||||
// Iterate over results while the thread is producing them.
|
||||
thread.start();
|
||||
int resultCount = 0;
|
||||
int resultSum = 0;
|
||||
for (int result : future) {
|
||||
sem.release();
|
||||
++resultCount;
|
||||
resultSum += result;
|
||||
}
|
||||
thread.wait();
|
||||
|
||||
QCOMPARE(resultCount, expectedResultCount);
|
||||
QCOMPARE(resultSum, expectedResultCount * (expectedResultCount + 1) / 2);
|
||||
|
||||
// Reverse iterate
|
||||
resultSum = 0;
|
||||
QFutureIterator<int> it(future);
|
||||
it.toBack();
|
||||
while (it.hasPrevious())
|
||||
resultSum += it.previous();
|
||||
|
||||
QCOMPARE(resultSum, expectedResultCount * (expectedResultCount + 1) / 2);
|
||||
}
|
||||
|
||||
class SignalSlotObject : public QObject
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user