summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qfutureinterface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread/qfutureinterface.cpp')
-rw-r--r--src/corelib/thread/qfutureinterface.cpp425
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"