diff options
Diffstat (limited to 'src/corelib/thread/qfutureinterface.cpp')
-rw-r--r-- | src/corelib/thread/qfutureinterface.cpp | 113 |
1 files changed, 81 insertions, 32 deletions
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp index 5556551447..f83306af00 100644 --- a/src/corelib/thread/qfutureinterface.cpp +++ b/src/corelib/thread/qfutureinterface.cpp @@ -6,14 +6,11 @@ #include "qfutureinterface_p.h" #include <QtCore/qatomic.h> +#include <QtCore/qcoreapplication.h> #include <QtCore/qthread.h> #include <QtCore/qvarlengtharray.h> -#include <QtCore/private/qsimd_p.h> // for qYieldCpu() #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 @@ -30,6 +27,7 @@ namespace { class ThreadPoolThreadReleaser { QThreadPool *m_pool; public: + Q_NODISCARD_CTOR explicit ThreadPoolThreadReleaser(QThreadPool *pool) : m_pool(pool) { if (pool) pool->releaseThread(); } @@ -42,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; @@ -619,7 +674,6 @@ void QFutureInterfaceBase::reset() { d->m_progressValue = 0; d->m_progress.reset(); - d->setState(QFutureInterfaceBase::NoState); d->progressTime.invalidate(); d->isValid = false; } @@ -738,37 +792,34 @@ 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); - QVarLengthArray<std::unique_ptr<QFutureCallOutEvent>, 3> events; - const auto currentState = state.loadRelaxed(); if (currentState & QFutureInterfaceBase::Started) { - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::Started)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started)); if (m_progress) { - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, m_progress->minimum, m_progress->maximum)); - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::Progress, + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, m_progressValue, m_progress->text)); } else { - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, 0, 0)); - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::Progress, + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Progress, m_progressValue, QString())); } @@ -779,7 +830,7 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface while (it != data.m_results.end()) { const int begin = it.resultIndex(); const int end = begin + it.batchSize(); - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ResultsReady, begin, end)); it.batchedAdvance(); @@ -787,32 +838,28 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface } if (currentState & QFutureInterfaceBase::Suspended) - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::Suspended)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspended)); else if (currentState & QFutureInterfaceBase::Suspending) - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::Suspending)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Suspending)); if (currentState & QFutureInterfaceBase::Canceled) - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); if (currentState & QFutureInterfaceBase::Finished) - events.emplace_back(new QFutureCallOutEvent(QFutureCallOutEvent::Finished)); + iface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Finished)); - outputConnections.append(interface); - - locker.unlock(); - for (auto &&event : events) - interface->postCallOutEvent(*event); + outputConnections.append(iface); } -void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterface *interface) +void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterface *iface) { QMutexLocker lock(&m_mutex); - const qsizetype 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) @@ -910,3 +957,5 @@ QFuture<void> makeReadyVoidFuture() } // namespace QtFuture QT_END_NAMESPACE + +#include "qfutureinterface.moc" |