diff options
Diffstat (limited to 'src/corelib/thread/qfutureinterface.h')
-rw-r--r-- | src/corelib/thread/qfutureinterface.h | 280 |
1 files changed, 180 insertions, 100 deletions
diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h index 2e15cbb013..ea7d40ad90 100644 --- a/src/corelib/thread/qfutureinterface.h +++ b/src/corelib/thread/qfutureinterface.h @@ -1,61 +1,27 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QFUTUREINTERFACE_H #define QFUTUREINTERFACE_H -#include <QtCore/qrunnable.h> #include <QtCore/qmutex.h> -#include <QtCore/qexception.h> #include <QtCore/qresultstore.h> +#ifndef QT_NO_EXCEPTIONS +#include <exception> +#endif #include <utility> -#include <vector> -#include <mutex> QT_REQUIRE_CONFIG(future); +QT_FORWARD_DECLARE_CLASS(QRunnable) +QT_FORWARD_DECLARE_CLASS(QException) QT_BEGIN_NAMESPACE template <typename T> class QFuture; class QThreadPool; +class QFutureInterfaceBase; class QFutureInterfaceBasePrivate; class QFutureWatcherBase; class QFutureWatcherBasePrivate; @@ -64,6 +30,8 @@ namespace QtPrivate { template<typename Function, typename ResultType, typename ParentResultType> class Continuation; +class ExceptionStore; + template<class Function, class ResultType> class CanceledHandler; @@ -71,6 +39,10 @@ class CanceledHandler; template<class Function, class ResultType> class FailureHandler; #endif + +void Q_CORE_EXPORT watchContinuationImpl(const QObject *context, + QtPrivate::QSlotObjectBase *slotObj, + QFutureInterfaceBase &fi); } class Q_CORE_EXPORT QFutureInterfaceBase @@ -91,6 +63,10 @@ public: QFutureInterfaceBase(State initialState = NoState); QFutureInterfaceBase(const QFutureInterfaceBase &other); + QFutureInterfaceBase(QFutureInterfaceBase &&other) noexcept + : d(std::exchange(other.d, nullptr)) {} + QFutureInterfaceBase &operator=(const QFutureInterfaceBase &other); + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QFutureInterfaceBase) virtual ~QFutureInterfaceBase(); // reporting functions available to the engine author: @@ -99,7 +75,11 @@ public: void reportCanceled(); #ifndef QT_NO_EXCEPTIONS void reportException(const QException &e); +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) void reportException(std::exception_ptr e); +#else + void reportException(const std::exception_ptr &e); +#endif #endif void reportResultsReady(int beginIndex, int endIndex); @@ -143,6 +123,8 @@ public: int loadState() const; void cancel(); + void cancelAndFinish() { cancel(CancelMode::CancelAndFinish); } + void setSuspended(bool suspend); void toggleSuspended(); void reportSuspended() const; @@ -155,18 +137,28 @@ public: void suspendIfRequested(); QMutex &mutex() const; + bool hasException() const; QtPrivate::ExceptionStore &exceptionStore(); QtPrivate::ResultStoreBase &resultStoreBase(); const QtPrivate::ResultStoreBase &resultStoreBase() const; inline bool operator==(const QFutureInterfaceBase &other) const { return d == other.d; } inline bool operator!=(const QFutureInterfaceBase &other) const { return d != other.d; } - QFutureInterfaceBase &operator=(const QFutureInterfaceBase &other); + + // ### Qt 7: inline + void swap(QFutureInterfaceBase &other) noexcept; + + template<typename T> + static QFutureInterfaceBase get(const QFuture<T> &future); // implemented in qfuture.h + + bool isChainCanceled() const; protected: - bool refT() const; - bool derefT() const; + // ### Qt 7: remove const from refT/derefT + bool refT() const noexcept; + bool derefT() const noexcept; void reset(); + void rethrowPossibleException(); public: #ifndef QFUTURE_TEST @@ -189,16 +181,33 @@ private: friend class QtPrivate::FailureHandler; #endif + friend Q_CORE_EXPORT void QtPrivate::watchContinuationImpl( + const QObject *context, QtPrivate::QSlotObjectBase *slotObj, QFutureInterfaceBase &fi); + + template<class T> + friend class QPromise; + protected: - void setContinuation(std::function<void()> func); + void setContinuation(std::function<void(const QFutureInterfaceBase &)> func); + void setContinuation(std::function<void(const QFutureInterfaceBase &)> func, + QFutureInterfaceBasePrivate *continuationFutureData); + void cleanContinuation(); void runContinuation() const; void setLaunchAsync(bool value); bool launchAsync() const; bool isRunningOrPending() const; + + enum class CancelMode { CancelOnly, CancelAndFinish }; + void cancel(CancelMode mode); }; +inline void swap(QFutureInterfaceBase &lhs, QFutureInterfaceBase &rhs) noexcept +{ + lhs.swap(rhs); +} + template <typename T> class QFutureInterface : public QFutureInterfaceBase { @@ -213,32 +222,36 @@ public: { refT(); } + QFutureInterface(const QFutureInterfaceBase &dd) : QFutureInterfaceBase(dd) { refT(); } + QFutureInterface(QFutureInterfaceBase &&dd) noexcept : QFutureInterfaceBase(std::move(dd)) { refT(); } + QFutureInterface &operator=(const QFutureInterface &other) + { + QFutureInterface copy(other); + swap(copy); + return *this; + } + QFutureInterface(QFutureInterface &&other) = default; + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QFutureInterface) + ~QFutureInterface() { - if (!derefT()) + if (!derefT() && !hasException()) resultStoreBase().template clear<T>(); } static QFutureInterface canceledResult() { return QFutureInterface(State(Started | Finished | Canceled)); } - QFutureInterface &operator=(const QFutureInterface &other) - { - other.refT(); - if (!derefT()) - resultStoreBase().template clear<T>(); - QFutureInterfaceBase::operator=(other); - return *this; - } - 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(T &&result, int index = -1); - inline void reportResult(const T &result, int index = -1); - inline void reportResults(const QList<T> &results, int beginIndex = -1, int count = -1); - inline void reportFinished(const T *result); + template <typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool> = true> + inline bool reportAndEmplaceResult(int index, Args&&...args); + inline bool reportResult(const T *result, int index = -1); + inline bool reportAndMoveResult(T &&result, int index = -1); + inline bool reportResult(T &&result, int index = -1); + inline bool reportResult(const T &result, int index = -1); + inline bool reportResults(const QList<T> &results, int beginIndex = -1, int count = -1); + inline bool reportFinished(const T *result); void reportFinished() { QFutureInterfaceBase::reportFinished(); @@ -250,95 +263,137 @@ public: inline QList<T> results(); T takeResult(); +#if 0 + // TODO: Enable and make it return a QList, when QList is fixed to support move-only types std::vector<T> takeResults(); +#endif + +#ifndef QT_NO_EXCEPTIONS + void reportException(const std::exception_ptr &e) + { + if (hasException()) + return; + + resultStoreBase().template clear<T>(); + QFutureInterfaceBase::reportException(e); + } + void reportException(const QException &e) + { + if (hasException()) + return; + + resultStoreBase().template clear<T>(); + QFutureInterfaceBase::reportException(e); + } +#endif }; template <typename T> -inline void QFutureInterface<T>::reportResult(const T *result, int index) +inline bool QFutureInterface<T>::reportResult(const T *result, int index) { - std::lock_guard<QMutex> locker{mutex()}; - if (this->queryState(Canceled) || this->queryState(Finished)) { - return; - } + QMutexLocker<QMutex> locker{&mutex()}; + if (this->queryState(Canceled) || this->queryState(Finished)) + return false; + Q_ASSERT(!hasException()); QtPrivate::ResultStoreBase &store = resultStoreBase(); + const int resultCountBefore = store.count(); + const int insertIndex = store.addResult<T>(index, result); + if (insertIndex == -1) + return false; if (store.filterMode()) { - const int resultCountBefore = store.count(); - store.addResult<T>(index, result); this->reportResultsReady(resultCountBefore, store.count()); } else { - const int insertIndex = store.addResult<T>(index, result); this->reportResultsReady(insertIndex, insertIndex + 1); } + return true; } template<typename T> -void QFutureInterface<T>::reportAndMoveResult(T &&result, int index) +template<typename...Args, std::enable_if_t<std::is_constructible_v<T, Args...>, bool>> +bool QFutureInterface<T>::reportAndEmplaceResult(int index, Args&&...args) { - std::lock_guard<QMutex> locker{mutex()}; + QMutexLocker<QMutex> locker{&mutex()}; if (queryState(Canceled) || queryState(Finished)) - return; + return false; + Q_ASSERT(!hasException()); 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. + const int insertIndex = store.emplaceResult<T>(index, std::forward<Args>(args)...); + // Let's make sure it's not in pending results. + if (insertIndex != -1 && (!store.filterMode() || oldResultCount < store.count())) reportResultsReady(insertIndex, store.count()); + return insertIndex != -1; } template<typename T> -void QFutureInterface<T>::reportResult(T &&result, int index) +bool QFutureInterface<T>::reportAndMoveResult(T &&result, int index) { - reportAndMoveResult(std::move(result), index); + return reportAndEmplaceResult(index, std::move(result)); +} + +template<typename T> +bool QFutureInterface<T>::reportResult(T &&result, int index) +{ + return reportAndMoveResult(std::move(result), index); } template <typename T> -inline void QFutureInterface<T>::reportResult(const T &result, int index) +inline bool QFutureInterface<T>::reportResult(const T &result, int index) { - reportResult(&result, index); + return reportResult(&result, index); } template<typename T> -inline void QFutureInterface<T>::reportResults(const QList<T> &_results, int beginIndex, int count) +inline bool QFutureInterface<T>::reportResults(const QList<T> &_results, int beginIndex, int count) { - std::lock_guard<QMutex> locker{mutex()}; - if (this->queryState(Canceled) || this->queryState(Finished)) { - return; - } + QMutexLocker<QMutex> locker{&mutex()}; + if (this->queryState(Canceled) || this->queryState(Finished)) + return false; + Q_ASSERT(!hasException()); auto &store = resultStoreBase(); + const int resultCountBefore = store.count(); + const int insertIndex = store.addResults(beginIndex, &_results, count); + if (insertIndex == -1) + return false; if (store.filterMode()) { - const int resultCountBefore = store.count(); - store.addResults(beginIndex, &_results, count); this->reportResultsReady(resultCountBefore, store.count()); } else { - const int insertIndex = store.addResults(beginIndex, &_results, count); - this->reportResultsReady(insertIndex, insertIndex + _results.count()); + this->reportResultsReady(insertIndex, insertIndex + _results.size()); } + return true; } template <typename T> -inline void QFutureInterface<T>::reportFinished(const T *result) +inline bool QFutureInterface<T>::reportFinished(const T *result) { + bool resultReported = false; if (result) - reportResult(result); + resultReported = reportResult(result); reportFinished(); + return resultReported; } template <typename T> inline const T &QFutureInterface<T>::resultReference(int index) const { - std::lock_guard<QMutex> locker{mutex()}; + Q_ASSERT(!hasException()); + + QMutexLocker<QMutex> locker{&mutex()}; return resultStoreBase().resultAt(index).template value<T>(); } template <typename T> inline const T *QFutureInterface<T>::resultPointer(int index) const { - std::lock_guard<QMutex> locker{mutex()}; + Q_ASSERT(!hasException()); + + QMutexLocker<QMutex> locker{&mutex()}; return resultStoreBase().resultAt(index).template pointer<T>(); } @@ -346,14 +401,14 @@ template <typename T> inline QList<T> QFutureInterface<T>::results() { if (this->isCanceled()) { - exceptionStore().throwPossibleException(); + rethrowPossibleException(); return QList<T>(); } QFutureInterfaceBase::waitForResult(-1); QList<T> res; - std::lock_guard<QMutex> locker{mutex()}; + QMutexLocker<QMutex> locker{&mutex()}; QtPrivate::ResultIteratorBase it = resultStoreBase().begin(); while (it != resultStoreBase().end()) { @@ -373,7 +428,9 @@ T QFutureInterface<T>::takeResult() // not to mess with other unready results. waitForResult(-1); - const std::lock_guard<QMutex> locker{mutex()}; + Q_ASSERT(!hasException()); + + const QMutexLocker<QMutex> locker{&mutex()}; QtPrivate::ResultIteratorBase position = resultStoreBase().resultAt(0); T ret(std::move_if_noexcept(position.value<T>())); reset(); @@ -382,16 +439,20 @@ T QFutureInterface<T>::takeResult() return ret; } +#if 0 template<typename T> std::vector<T> QFutureInterface<T>::takeResults() { Q_ASSERT(isValid()); waitForResult(-1); + + Q_ASSERT(!hasException()); + std::vector<T> res; res.reserve(resultCount()); - const std::lock_guard<QMutex> locker{mutex()}; + const QMutexLocker<QMutex> locker{&mutex()}; QtPrivate::ResultIteratorBase it = resultStoreBase().begin(); for (auto endIt = resultStoreBase().end(); it != endIt; ++it) @@ -402,30 +463,49 @@ std::vector<T> QFutureInterface<T>::takeResults() return res; } +#endif + +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wweak-vtables") // QTBUG-125115 template <> class QFutureInterface<void> : public QFutureInterfaceBase { public: - explicit QFutureInterface<void>(State initialState = NoState) + explicit QFutureInterface(State initialState = NoState) : QFutureInterfaceBase(initialState) { } + QFutureInterface(const QFutureInterfaceBase &dd) : QFutureInterfaceBase(dd) { } + static QFutureInterface<void> canceledResult() { return QFutureInterface(State(Started | Finished | Canceled)); } inline QFuture<void> future(); // implemented in qfuture.h - void reportResult(const void *, int) { } - void reportResults(const QList<void> &, int) { } - void reportFinished(const void * = nullptr) + bool reportResult(const void *, int) { return false; } + bool reportResults(const QList<void> &, int) { return false; } + bool reportFinished(const void *) + { + reportFinished(); + return false; + } + void reportFinished() { QFutureInterfaceBase::reportFinished(); QFutureInterfaceBase::runContinuation(); } }; +QT_WARNING_POP // Clang -Wweak-vtables + +template<typename T> +inline void swap(QFutureInterface<T> &a, QFutureInterface<T> &b) noexcept +{ + a.swap(b); +} + QT_END_NAMESPACE #endif // QFUTUREINTERFACE_H |