diff options
Diffstat (limited to 'src/corelib/thread/qfutureinterface.cpp')
-rw-r--r-- | src/corelib/thread/qfutureinterface.cpp | 146 |
1 files changed, 110 insertions, 36 deletions
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp index eedfd7ceeb..f83306af00 100644 --- a/src/corelib/thread/qfutureinterface.cpp +++ b/src/corelib/thread/qfutureinterface.cpp @@ -6,13 +6,11 @@ #include "qfutureinterface_p.h" #include <QtCore/qatomic.h> +#include <QtCore/qcoreapplication.h> #include <QtCore/qthread.h> -#include <QtCore/private/qsimd_p.h> // for qYieldCpu() +#include <QtCore/qvarlengtharray.h> #include <private/qthreadpool_p.h> - -#ifdef interface -# undef interface -#endif +#include <private/qobject_p.h> // GCC 12 gets confused about QFutureInterfaceBase::state, for some non-obvious // reason @@ -29,6 +27,7 @@ namespace { class ThreadPoolThreadReleaser { QThreadPool *m_pool; public: + Q_NODISCARD_CTOR explicit ThreadPoolThreadReleaser(QThreadPool *pool) : m_pool(pool) { if (pool) pool->releaseThread(); } @@ -41,6 +40,63 @@ 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; @@ -618,7 +674,6 @@ void QFutureInterfaceBase::reset() { d->m_progressValue = 0; d->m_progress.reset(); - d->setState(QFutureInterfaceBase::NoState); d->progressTime.invalidate(); d->isValid = false; } @@ -737,37 +792,36 @@ void QFutureInterfaceBasePrivate::sendCallOuts(const QFutureCallOutEvent &callOu return; for (int i = 0; i < outputConnections.size(); ++i) { - QFutureCallOutInterface *interface = outputConnections.at(i); - interface->postCallOutEvent(callOutEvent1); - interface->postCallOutEvent(callOutEvent2); + 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)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started)); if (m_progress) { - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, - m_progress->minimum, - m_progress->maximum)); - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, - m_progressValue, - m_progress->text)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, + m_progress->minimum, + m_progress->maximum)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, + m_progressValue, + m_progress->text)); } else { - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, - 0, - 0)); - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, - m_progressValue, - QString())); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, + 0, + 0)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, + m_progressValue, + QString())); } } @@ -776,36 +830,36 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface while (it != data.m_results.end()) { const int begin = it.resultIndex(); const int end = begin + it.batchSize(); - interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, - begin, - end)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, + begin, + end)); 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) @@ -834,6 +888,10 @@ void QFutureInterfaceBase::setContinuation(std::function<void(const QFutureInter // 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; } @@ -847,6 +905,7 @@ void QFutureInterfaceBase::cleanContinuation() QMutexLocker lock(&d->continuationMutex); d->continuation = nullptr; d->continuationState = QFutureInterfaceBasePrivate::Cleaned; + d->continuationData = nullptr; } void QFutureInterfaceBase::runContinuation() const @@ -884,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" |