diff options
| author | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2020-02-26 10:40:02 +0100 |
|---|---|---|
| committer | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2020-03-31 15:28:23 +0200 |
| commit | 44ceb56455c82df3e6b1c9a2fa373cac14a039f8 (patch) | |
| tree | 74f61f7adae376af0f2a9ee256a911e2bfc1ee9b /src/corelib/thread/qfutureinterface.h | |
| parent | 986cfe312e4c01259f9a81c00dadebb10bc27ac9 (diff) | |
QFuture - add ability to move results from QFuture
QFuture's original design pre-dates C++11 and its
introduction of move semantics. QFuture is documented
as requiring copy-constructible classes and uses copy
operations for results (which in Qt's universe in general
is relatively cheap, due to the use of COW/data sharing).
QFuture::result(), QFuture::results(), QFuture::resultAt()
return copies. Now that the year is 2020, it makes some
sense to add support for move semantics and, in particular,
move-only types, like std::unique_ptr (that cannot be
obtained from QFuture using result etc.). Taking a result
or results from a QFuture renders it invalid. This patch
adds QFuture<T>::takeResults(), takeResult() and isValid().
'Taking' functions are 'enabled_if' for non-void types only
to improve the compiler's diagnostic (which would otherwise
spit some semi-articulate diagnostic).
As a bonus a bug was found in the pre-existing code (after
initially copy and pasted into the new function) - the one
where we incorrectly report ready results in (rather obscure)
filter mode.
Fixes: QTBUG-81941
Fixes: QTBUG-83182
Change-Id: I8ccdfc50aa310a3a79eef2cdc55f5ea210f889c3
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/corelib/thread/qfutureinterface.h')
| -rw-r--r-- | src/corelib/thread/qfutureinterface.h | 86 |
1 files changed, 82 insertions, 4 deletions
diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h index c2e884911fd..1da06beb7de 100644 --- a/src/corelib/thread/qfutureinterface.h +++ b/src/corelib/thread/qfutureinterface.h @@ -45,6 +45,8 @@ #include <QtCore/qexception.h> #include <QtCore/qresultstore.h> +#include <utility> +#include <vector> #include <mutex> QT_REQUIRE_CONFIG(future); @@ -116,6 +118,7 @@ public: bool isPaused() const; bool isThrottled() const; bool isResultReadyAt(int index) const; + bool isValid() const; void cancel(); void setPaused(bool paused); @@ -139,6 +142,7 @@ public: protected: bool refT() const; bool derefT() const; + void reset(); public: #ifndef QFUTURE_TEST @@ -198,13 +202,22 @@ public: inline QFuture<T> future(); // implemented in qfuture.h inline void reportResult(const T *result, int index = -1); + inline void reportAndMoveResult(T &&result, int index = -1); inline void reportResult(const T &result, int index = -1); inline void reportResults(const QVector<T> &results, int beginIndex = -1, int count = -1); - inline void reportFinished(const T *result = nullptr); + inline void reportFinished(const T *result); + void reportFinished() + { + QFutureInterfaceBase::reportFinished(); + QFutureInterfaceBase::runContinuation(); + } inline const T &resultReference(int index) const; inline const T *resultPointer(int index) const; inline QList<T> results(); + + T takeResult(); + std::vector<T> takeResults(); }; template <typename T> @@ -220,13 +233,28 @@ 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, resultCountBefore + store.count()); + this->reportResultsReady(resultCountBefore, store.count()); } else { const int insertIndex = store.addResult<T>(index, result); this->reportResultsReady(insertIndex, insertIndex + 1); } } +template<typename T> +void QFutureInterface<T>::reportAndMoveResult(T &&result, int index) +{ + std::lock_guard<QMutex> locker{mutex()}; + if (queryState(Canceled) || queryState(Finished)) + return; + + QtPrivate::ResultStoreBase &store = resultStoreBase(); + + 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. + reportResultsReady(insertIndex, store.count()); +} + template <typename T> inline void QFutureInterface<T>::reportResult(const T &result, int index) { @@ -258,8 +286,7 @@ inline void QFutureInterface<T>::reportFinished(const T *result) { if (result) reportResult(result); - QFutureInterfaceBase::reportFinished(); - QFutureInterfaceBase::runContinuation(); + reportFinished(); } template <typename T> @@ -283,6 +310,7 @@ inline QList<T> QFutureInterface<T>::results() exceptionStore().throwPossibleException(); return QList<T>(); } + QFutureInterfaceBase::waitForResult(-1); QList<T> res; @@ -297,6 +325,56 @@ inline QList<T> QFutureInterface<T>::results() return res; } +template<typename T> +T QFutureInterface<T>::takeResult() +{ + if (isCanceled()) { + exceptionStore().throwPossibleException(); + return {}; + } + + if (!isValid()) + return {}; + // Note: we wait for all, this is intentional, + // not to mess with other unready results. + waitForResult(-1); + + const std::lock_guard<QMutex> locker{mutex()}; + QtPrivate::ResultIteratorBase position = resultStoreBase().resultAt(0); + T ret(std::move_if_noexcept(position.value<T>())); + reset(); + resultStoreBase().template clear<T>(); + + return ret; +} + +template<typename T> +std::vector<T> QFutureInterface<T>::takeResults() +{ + if (isCanceled()) { + exceptionStore().throwPossibleException(); + return {}; + } + + if (!isValid()) + return {}; + + waitForResult(-1); + std::vector<T> res; + res.reserve(resultCount()); + + const std::lock_guard<QMutex> locker{mutex()}; + + QtPrivate::ResultIteratorBase it = resultStoreBase().begin(); + for (auto endIt = resultStoreBase().end(); it != endIt; ++it) + res.push_back(std::move_if_noexcept(it.value<T>())); + + reset(); + resultStoreBase().template clear<T>(); + + return res; +} + template <> class QFutureInterface<void> : public QFutureInterfaceBase { |
