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:
Morten Johan Sørvig 2017-12-11 21:12:10 +01:00 committed by Allan Sandfeld Jensen
parent 486c55d743
commit 6faa4d4a87
2 changed files with 132 additions and 19 deletions

View File

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

View File

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