diff options
Diffstat (limited to 'src/corelib/thread/qfutureinterface.cpp')
-rw-r--r-- | src/corelib/thread/qfutureinterface.cpp | 425 |
1 files changed, 302 insertions, 123 deletions
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp index 76af95e3a3..f83306af00 100644 --- a/src/corelib/thread/qfutureinterface.cpp +++ b/src/corelib/thread/qfutureinterface.cpp @@ -1,53 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 // qfutureinterface.h included from qfuture.h #include "qfuture.h" #include "qfutureinterface_p.h" #include <QtCore/qatomic.h> +#include <QtCore/qcoreapplication.h> #include <QtCore/qthread.h> +#include <QtCore/qvarlengtharray.h> #include <private/qthreadpool_p.h> +#include <private/qobject_p.h> -#ifdef interface -# undef interface -#endif +// GCC 12 gets confused about QFutureInterfaceBase::state, for some non-obvious +// reason +// warning: ‘unsigned int __atomic_or_fetch_4(volatile void*, unsigned int, int)’ writing 4 bytes into a region of size 0 overflows the destination [-Wstringop-overflow=] +QT_WARNING_DISABLE_GCC("-Wstringop-overflow") QT_BEGIN_NAMESPACE @@ -59,6 +27,7 @@ namespace { class ThreadPoolThreadReleaser { QThreadPool *m_pool; public: + Q_NODISCARD_CTOR explicit ThreadPoolThreadReleaser(QThreadPool *pool) : m_pool(pool) { if (pool) pool->releaseThread(); } @@ -71,6 +40,67 @@ const auto suspendingOrSuspended = } // unnamed namespace +class QObjectContinuationWrapper : public QObject +{ + Q_OBJECT +public: + explicit QObjectContinuationWrapper(QObject *parent = nullptr) + : QObject(parent) + { + } + +signals: + void run(); +}; + +void QtPrivate::watchContinuationImpl(const QObject *context, QSlotObjectBase *slotObj, + QFutureInterfaceBase &fi) +{ + Q_ASSERT(context); + Q_ASSERT(slotObj); + + auto slot = SlotObjUniquePtr(slotObj); + + auto *watcher = new QObjectContinuationWrapper; + watcher->moveToThread(context->thread()); + + // We need to protect acccess to the watcher. The context object (and in turn, the watcher) + // could be destroyed while the continuation that emits the signal is running. We have to + // prevent that. + // The mutex has to be recursive, because the continuation itself could delete the context + // object (and thus the watcher), which will try to lock the mutex from the same thread twice. + auto watcherMutex = std::make_shared<QRecursiveMutex>(); + const auto destroyWatcher = [watcherMutex, watcher]() mutable { + QMutexLocker lock(watcherMutex.get()); + delete watcher; + }; + + // ### we're missing a convenient way to `QObject::connect()` to a `QSlotObjectBase`... + QObject::connect(watcher, &QObjectContinuationWrapper::run, + // for the following, cf. QMetaObject::invokeMethodImpl(): + // we know `slot` is a lambda returning `void`, so we can just + // `call()` with `obj` and `args[0]` set to `nullptr`: + context, [slot = std::move(slot)] { + void *args[] = { nullptr }; // for `void` return value + slot->call(nullptr, args); + }); + QObject::connect(watcher, &QObjectContinuationWrapper::run, watcher, &QObject::deleteLater); + QObject::connect(context, &QObject::destroyed, watcher, destroyWatcher); + + fi.setContinuation([watcherMutex, watcher = QPointer(watcher)] + (const QFutureInterfaceBase &parentData) + { + Q_UNUSED(parentData); + QMutexLocker lock(watcherMutex.get()); + if (watcher) + emit watcher->run(); + }); +} + +QFutureCallOutInterface::~QFutureCallOutInterface() + = default; + +Q_IMPL_EVENT_COMMON(QFutureCallOutEvent) QFutureInterfaceBase::QFutureInterfaceBase(State initialState) : d(new QFutureInterfaceBasePrivate(initialState)) @@ -84,7 +114,7 @@ QFutureInterfaceBase::QFutureInterfaceBase(const QFutureInterfaceBase &other) QFutureInterfaceBase::~QFutureInterfaceBase() { - if (!d->refCount.deref()) + if (d && !d->refCount.deref()) delete d; } @@ -100,24 +130,52 @@ static inline int switch_off(QAtomicInt &a, int which) static inline int switch_from_to(QAtomicInt &a, int from, int to) { - int newValue; - int expected = a.loadRelaxed(); - do { - newValue = (expected & ~from) | to; - } while (!a.testAndSetRelaxed(expected, newValue, expected)); - return newValue; + const auto adjusted = [&](int old) { return (old & ~from) | to; }; + int value = a.loadRelaxed(); + while (!a.testAndSetRelaxed(value, adjusted(value), value)) + qYieldCpu(); + return value; } void QFutureInterfaceBase::cancel() { + cancel(CancelMode::CancelOnly); +} + +void QFutureInterfaceBase::cancel(QFutureInterfaceBase::CancelMode mode) +{ QMutexLocker locker(&d->m_mutex); - if (d->state.loadRelaxed() & Canceled) - return; - switch_from_to(d->state, suspendingOrSuspended, Canceled); + const auto oldState = d->state.loadRelaxed(); + + switch (mode) { + case CancelMode::CancelAndFinish: + if ((oldState & Finished) && (oldState & Canceled)) + return; + switch_from_to(d->state, suspendingOrSuspended | Running, Canceled | Finished); + break; + case CancelMode::CancelOnly: + if (oldState & Canceled) + return; + switch_from_to(d->state, suspendingOrSuspended, Canceled); + break; + } + + // Cancel the continuations chain + QFutureInterfaceBasePrivate *next = d->continuationData; + while (next) { + next->continuationState = QFutureInterfaceBasePrivate::Canceled; + next = next->continuationData; + } + d->waitCondition.wakeAll(); d->pausedWaitCondition.wakeAll(); - d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); + + if (!(oldState & Canceled)) + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); + if (mode == CancelMode::CancelAndFinish && !(oldState & Finished)) + d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Finished)); + d->isValid = false; } @@ -153,7 +211,7 @@ void QFutureInterfaceBase::reportSuspended() const // i.e. no more events will be reported. QMutexLocker locker(&d->m_mutex); - const int state = d->state; + const int state = d->state.loadRelaxed(); if (!(state & Suspending) || (state & Suspended)) return; @@ -299,13 +357,13 @@ int QFutureInterfaceBase::progressValue() const int QFutureInterfaceBase::progressMinimum() const { const QMutexLocker lock(&d->m_mutex); - return d->m_progressMinimum; + return d->m_progress ? d->m_progress->minimum : 0; } int QFutureInterfaceBase::progressMaximum() const { const QMutexLocker lock(&d->m_mutex); - return d->m_progressMaximum; + return d->m_progress ? d->m_progress->maximum : 0; } int QFutureInterfaceBase::resultCount() const @@ -317,7 +375,7 @@ int QFutureInterfaceBase::resultCount() const QString QFutureInterfaceBase::progressText() const { QMutexLocker locker(&d->m_mutex); - return d->m_progressText; + return d->m_progress ? d->m_progress->text : QString(); } bool QFutureInterfaceBase::isProgressUpdateNeeded() const @@ -351,13 +409,18 @@ void QFutureInterfaceBase::reportException(const QException &exception) } } +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) void QFutureInterfaceBase::reportException(std::exception_ptr exception) +#else +void QFutureInterfaceBase::reportException(const std::exception_ptr &exception) +#endif { QMutexLocker locker(&d->m_mutex); if (d->state.loadRelaxed() & (Canceled|Finished)) return; - d->m_exceptionStore.setException(exception); + d->hasException = true; + d->data.setException(exception); switch_on(d->state, Canceled); d->waitCondition.wakeAll(); d->pausedWaitCondition.wakeAll(); @@ -377,7 +440,7 @@ void QFutureInterfaceBase::reportFinished() void QFutureInterfaceBase::setExpectedResultCount(int resultCount) { - if (d->manualProgress == false) + if (d->m_progress) setProgressRange(0, resultCount); d->m_expectedResultCount = resultCount; } @@ -394,12 +457,16 @@ bool QFutureInterfaceBase::queryState(State state) const int QFutureInterfaceBase::loadState() const { + // Used from ~QPromise, so this check is needed + if (!d) + return QFutureInterfaceBase::State::NoState; return d->state.loadRelaxed(); } void QFutureInterfaceBase::waitForResult(int resultIndex) { - d->m_exceptionStore.throwPossibleException(); + if (d->hasException) + d->data.m_exceptionStore.rethrowException(); QMutexLocker lock(&d->m_mutex); if (!isRunningOrPending()) @@ -416,7 +483,8 @@ void QFutureInterfaceBase::waitForResult(int resultIndex) while (isRunningOrPending() && !d->internal_isResultReadyAt(waitIndex)) d->waitCondition.wait(&d->m_mutex); - d->m_exceptionStore.throwPossibleException(); + if (d->hasException) + d->data.m_exceptionStore.rethrowException(); } void QFutureInterfaceBase::waitForFinished() @@ -434,7 +502,8 @@ void QFutureInterfaceBase::waitForFinished() d->waitCondition.wait(&d->m_mutex); } - d->m_exceptionStore.throwPossibleException(); + if (d->hasException) + d->data.m_exceptionStore.rethrowException(); } void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex) @@ -444,8 +513,8 @@ void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex) d->waitCondition.wakeAll(); - if (d->manualProgress == false) { - if (d->internal_updateProgress(d->m_progressValue + endIndex - beginIndex) == false) { + if (!d->m_progress) { + if (d->internal_updateProgressValue(d->m_progressValue + endIndex - beginIndex) == false) { d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, beginIndex, endIndex)); @@ -454,7 +523,7 @@ void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex) d->sendCallOuts(QFutureCallOutEvent(QFutureCallOutEvent::Progress, d->m_progressValue, - d->m_progressText), + QString()), QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, beginIndex, endIndex)); @@ -481,7 +550,8 @@ QThreadPool *QFutureInterfaceBase::threadPool() const void QFutureInterfaceBase::setFilterMode(bool enable) { QMutexLocker locker(&d->m_mutex); - resultStoreBase().setFilterMode(enable); + if (!hasException()) + resultStoreBase().setFilterMode(enable); } /*! @@ -503,8 +573,10 @@ void QFutureInterfaceBase::setFilterMode(bool enable) void QFutureInterfaceBase::setProgressRange(int minimum, int maximum) { QMutexLocker locker(&d->m_mutex); - d->m_progressMinimum = minimum; - d->m_progressMaximum = qMax(minimum, maximum); + if (!d->m_progress) + d->m_progress.reset(new QFutureInterfaceBasePrivate::ProgressData()); + d->m_progress->minimum = minimum; + d->m_progress->maximum = qMax(minimum, maximum); d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, minimum, maximum)); d->m_progressValue = minimum; } @@ -524,12 +596,12 @@ void QFutureInterfaceBase::setProgressValueAndText(int progressValue, const QString &progressText) { QMutexLocker locker(&d->m_mutex); - if (d->manualProgress == false) - d->manualProgress = true; + if (!d->m_progress) + d->m_progress.reset(new QFutureInterfaceBasePrivate::ProgressData()); - const bool useProgressRange = (d->m_progressMaximum != 0) || (d->m_progressMinimum != 0); + const bool useProgressRange = (d->m_progress->maximum != 0) || (d->m_progress->minimum != 0); if (useProgressRange - && ((progressValue < d->m_progressMinimum) || (progressValue > d->m_progressMaximum))) { + && ((progressValue < d->m_progress->minimum) || (progressValue > d->m_progress->maximum))) { return; } @@ -542,7 +614,7 @@ void QFutureInterfaceBase::setProgressValueAndText(int progressValue, if (d->internal_updateProgress(progressValue, progressText)) { d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Progress, d->m_progressValue, - d->m_progressText)); + d->m_progress->text)); } } @@ -551,58 +623,65 @@ QMutex &QFutureInterfaceBase::mutex() const return d->m_mutex; } +bool QFutureInterfaceBase::hasException() const +{ + return d->hasException; +} + QtPrivate::ExceptionStore &QFutureInterfaceBase::exceptionStore() { - return d->m_exceptionStore; + Q_ASSERT(d->hasException); + return d->data.m_exceptionStore; } QtPrivate::ResultStoreBase &QFutureInterfaceBase::resultStoreBase() { - return d->m_results; + Q_ASSERT(!d->hasException); + return d->data.m_results; } const QtPrivate::ResultStoreBase &QFutureInterfaceBase::resultStoreBase() const { - return d->m_results; + Q_ASSERT(!d->hasException); + return d->data.m_results; } QFutureInterfaceBase &QFutureInterfaceBase::operator=(const QFutureInterfaceBase &other) { - other.d->refCount.ref(); - if (!d->refCount.deref()) - delete d; - d = other.d; + QFutureInterfaceBase copy(other); + swap(copy); return *this; } +// ### Qt 7: inline void QFutureInterfaceBase::swap(QFutureInterfaceBase &other) noexcept { qSwap(d, other.d); } -bool QFutureInterfaceBase::refT() const +bool QFutureInterfaceBase::refT() const noexcept { return d->refCount.refT(); } -bool QFutureInterfaceBase::derefT() const +bool QFutureInterfaceBase::derefT() const noexcept { - return d->refCount.derefT(); + // Called from ~QFutureInterface + return !d || d->refCount.derefT(); } void QFutureInterfaceBase::reset() { d->m_progressValue = 0; - d->m_progressMinimum = 0; - d->m_progressMaximum = 0; - d->setState(QFutureInterfaceBase::NoState); + d->m_progress.reset(); d->progressTime.invalidate(); d->isValid = false; } void QFutureInterfaceBase::rethrowPossibleException() { - exceptionStore().throwPossibleException(); + if (hasException()) + exceptionStore().rethrowException(); } QFutureInterfaceBasePrivate::QFutureInterfaceBasePrivate(QFutureInterfaceBase::State initialState) @@ -611,25 +690,54 @@ QFutureInterfaceBasePrivate::QFutureInterfaceBasePrivate(QFutureInterfaceBase::S progressTime.invalidate(); } +QFutureInterfaceBasePrivate::~QFutureInterfaceBasePrivate() +{ + if (hasException) + data.m_exceptionStore.~ExceptionStore(); + else + data.m_results.~ResultStoreBase(); +} + int QFutureInterfaceBasePrivate::internal_resultCount() const { - return m_results.count(); // ### subtract canceled results. + return hasException ? 0 : data.m_results.count(); // ### subtract canceled results. } bool QFutureInterfaceBasePrivate::internal_isResultReadyAt(int index) const { - return (m_results.contains(index)); + return hasException ? false : (data.m_results.contains(index)); } bool QFutureInterfaceBasePrivate::internal_waitForNextResult() { - if (m_results.hasNextResult()) + if (hasException) + return false; + + if (data.m_results.hasNextResult()) return true; - while ((state.loadRelaxed() & QFutureInterfaceBase::Running) && m_results.hasNextResult() == false) + while ((state.loadRelaxed() & QFutureInterfaceBase::Running) + && data.m_results.hasNextResult() == false) waitCondition.wait(&m_mutex); - return !(state.loadRelaxed() & QFutureInterfaceBase::Canceled) && m_results.hasNextResult(); + return !(state.loadRelaxed() & QFutureInterfaceBase::Canceled) + && data.m_results.hasNextResult(); +} + +bool QFutureInterfaceBasePrivate::internal_updateProgressValue(int progress) +{ + if (m_progressValue >= progress) + return false; + + m_progressValue = progress; + + if (progressTime.isValid() && m_progressValue != 0) // make sure the first and last steps are emitted. + if (progressTime.elapsed() < (1000 / MaxProgressEmitsPerSecond)) + return false; + + progressTime.start(); + return true; + } bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress, @@ -638,10 +746,12 @@ bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress, if (m_progressValue >= progress) return false; + Q_ASSERT(m_progress); + m_progressValue = progress; - m_progressText = progressText; + m_progress->text = progressText; - if (progressTime.isValid() && m_progressValue != m_progressMaximum) // make sure the first and last steps are emitted. + if (progressTime.isValid() && m_progressValue != m_progress->maximum) // make sure the first and last steps are emitted. if (progressTime.elapsed() < (1000 / MaxProgressEmitsPerSecond)) return false; @@ -671,7 +781,7 @@ void QFutureInterfaceBasePrivate::sendCallOut(const QFutureCallOutEvent &callOut if (outputConnections.isEmpty()) return; - for (int i = 0; i < outputConnections.count(); ++i) + for (int i = 0; i < outputConnections.size(); ++i) outputConnections.at(i)->postCallOutEvent(callOutEvent); } @@ -681,65 +791,75 @@ void QFutureInterfaceBasePrivate::sendCallOuts(const QFutureCallOutEvent &callOu if (outputConnections.isEmpty()) return; - for (int i = 0; i < outputConnections.count(); ++i) { - QFutureCallOutInterface *interface = outputConnections.at(i); - interface->postCallOutEvent(callOutEvent1); - interface->postCallOutEvent(callOutEvent2); + for (int i = 0; i < outputConnections.size(); ++i) { + QFutureCallOutInterface *iface = outputConnections.at(i); + iface->postCallOutEvent(callOutEvent1); + iface->postCallOutEvent(callOutEvent2); } } // This function connects an output interface (for example a QFutureWatcher) // to this future. While holding the lock we check the state and ready results -// and add the appropriate callouts to the queue. In order to avoid deadlocks, -// the actual callouts are made at the end while not holding the lock. -void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface *interface) +// and add the appropriate callouts to the queue. +void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface *iface) { QMutexLocker locker(&m_mutex); const auto currentState = state.loadRelaxed(); if (currentState & QFutureInterfaceBase::Started) { - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started)); - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, - m_progressMinimum, - m_progressMaximum)); - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started)); + if (m_progress) { + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, + m_progress->minimum, + m_progress->maximum)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, + m_progressValue, + m_progress->text)); + } else { + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, + 0, + 0)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, m_progressValue, - m_progressText)); + QString())); + } } - QtPrivate::ResultIteratorBase it = m_results.begin(); - while (it != m_results.end()) { - const int begin = it.resultIndex(); - const int end = begin + it.batchSize(); - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, + if (!hasException) { + QtPrivate::ResultIteratorBase it = data.m_results.begin(); + while (it != data.m_results.end()) { + const int begin = it.resultIndex(); + const int end = begin + it.batchSize(); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, begin, end)); - it.batchedAdvance(); + it.batchedAdvance(); + } } if (currentState & QFutureInterfaceBase::Suspended) - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspended)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspended)); else if (currentState & QFutureInterfaceBase::Suspending) - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspending)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspending)); if (currentState & QFutureInterfaceBase::Canceled) - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); if (currentState & QFutureInterfaceBase::Finished) - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Finished)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Finished)); - outputConnections.append(interface); + outputConnections.append(iface); } -void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterface *interface) +void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterface *iface) { QMutexLocker lock(&m_mutex); - const int index = outputConnections.indexOf(interface); + const qsizetype index = outputConnections.indexOf(iface); if (index == -1) return; outputConnections.removeAt(index); - interface->callOutInterfaceDisconnected(); + iface->callOutInterfaceDisconnected(); } void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState) @@ -749,26 +869,70 @@ void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState) void QFutureInterfaceBase::setContinuation(std::function<void(const QFutureInterfaceBase &)> func) { + setContinuation(std::move(func), nullptr); +} + +void QFutureInterfaceBase::setContinuation(std::function<void(const QFutureInterfaceBase &)> func, + QFutureInterfaceBasePrivate *continuationFutureData) +{ QMutexLocker lock(&d->continuationMutex); + // If the state is ready, run continuation immediately, // otherwise save it for later. if (isFinished()) { lock.unlock(); func(*this); - } else { + lock.relock(); + } + // Unless the continuation has been cleaned earlier, we have to + // store the move-only continuation, to guarantee that the associated + // future's data stays alive. + if (d->continuationState != QFutureInterfaceBasePrivate::Cleaned) { + if (d->continuation) { + qWarning() << "Adding a continuation to a future which already has a continuation. " + "The existing continuation is overwritten."; + } d->continuation = std::move(func); + d->continuationData = continuationFutureData; } } +void QFutureInterfaceBase::cleanContinuation() +{ + if (!d) + return; + + QMutexLocker lock(&d->continuationMutex); + d->continuation = nullptr; + d->continuationState = QFutureInterfaceBasePrivate::Cleaned; + d->continuationData = nullptr; +} + void QFutureInterfaceBase::runContinuation() const { QMutexLocker lock(&d->continuationMutex); if (d->continuation) { + // Save the continuation in a local function, to avoid calling + // a null std::function below, in case cleanContinuation() is + // called from some other thread right after unlock() below. + auto fn = std::move(d->continuation); lock.unlock(); - d->continuation(*this); + fn(*this); + + lock.relock(); + // Unless the continuation has been cleaned earlier, we have to + // store the move-only continuation, to guarantee that the associated + // future's data stays alive. + if (d->continuationState != QFutureInterfaceBasePrivate::Cleaned) + d->continuation = std::move(fn); } } +bool QFutureInterfaceBase::isChainCanceled() const +{ + return isCanceled() || d->continuationState == QFutureInterfaceBasePrivate::Canceled; +} + void QFutureInterfaceBase::setLaunchAsync(bool value) { d->launchAsync = value; @@ -779,4 +943,19 @@ bool QFutureInterfaceBase::launchAsync() const return d->launchAsync; } +namespace QtFuture { + +QFuture<void> makeReadyVoidFuture() +{ + QFutureInterface<void> promise; + promise.reportStarted(); + promise.reportFinished(); + + return promise.future(); +} + +} // namespace QtFuture + QT_END_NAMESPACE + +#include "qfutureinterface.moc" |