diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h index a456dd91395..d3135510b3e 100644 --- a/src/corelib/thread/qfuture.h +++ b/src/corelib/thread/qfuture.h @@ -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; }; diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index b8c82c2ea08..a42454124ea 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -49,6 +49,24 @@ struct ResultStoreInt : QtPrivate::ResultStoreBase ~ResultStoreInt() { clear(); } }; +class LambdaThread : public QThread +{ +public: + LambdaThread(std::function fn) + :m_fn(fn) + { + + } + + void run() override + { + m_fn(); + } + +private: + std::function 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 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 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 it(future); + it.toBack(); + while (it.hasPrevious()) + resultSum += it.previous(); + + QCOMPARE(resultSum, expectedResultCount * (expectedResultCount + 1) / 2); +} class SignalSlotObject : public QObject {