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.cpp113
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"