Source code
Revision control
Copy as Markdown
// Copyright (C) 2025 Jarek Kobus
// Copyright (C) 2025 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
// Qt-Security score:significant reason:default
#ifndef QTASKTREE_QTASKTREERUNNER_H
#define QTASKTREE_QTASKTREERUNNER_H
#include <QtTaskTree/qttasktreeglobal.h>
#include <QtTaskTree/qtasktree.h>
#include <QtCore/QObject>
#include <unordered_map>
QT_BEGIN_NAMESPACE
namespace QtTaskTree {
class QSingleTaskTreeRunnerPrivate;
class QSequentialTaskTreeRunnerPrivate;
class QParallelTaskTreeRunnerPrivate;
using TreeSetupHandler = std::function<void(QTaskTree &)>;
using TreeDoneHandler = std::function<void(const QTaskTree &, DoneWith)>;
template <typename Handler>
TreeSetupHandler wrapTreeSetupHandler(Handler &&handler) {
if constexpr (std::is_same_v<q20::remove_cvref_t<Handler>, TreeSetupHandler>) {
if (!handler)
return {}; // User passed {} for the setup handler.
}
return [handler = std::forward<Handler>(handler)](QTaskTree &taskTree) {
// V, T stands for: [V]oid, [T]askTree
constexpr bool isVT = QtTaskTree::isInvocable<void, Handler, QTaskTree &>();
constexpr bool isV = QtTaskTree::isInvocable<void, Handler>();
static_assert(isVT || isV,
"Tree setup handler needs to take (TaskTree &) or (void) as an argument and has to "
"return void. The passed handler doesn't fulfill these requirements.");
if constexpr (isVT)
std::invoke(handler, taskTree);
else if constexpr (isV)
std::invoke(handler);
};
}
template <typename Handler>
TreeDoneHandler wrapTreeDoneHandler(Handler &&handler) {
if constexpr (std::is_same_v<q20::remove_cvref_t<Handler>, TreeDoneHandler>) {
if (!handler)
return {}; // User passed {} for the done handler.
}
return [handler = std::forward<Handler>(handler)](const QTaskTree &taskTree, DoneWith result) {
// V, T, D stands for: [V]oid, [T]askTree, [D]oneWith
constexpr bool isVTD = QtTaskTree::isInvocable<void, Handler, const QTaskTree &, DoneWith>();
constexpr bool isVT = QtTaskTree::isInvocable<void, Handler, const QTaskTree &>();
constexpr bool isVD = QtTaskTree::isInvocable<void, Handler, DoneWith>();
constexpr bool isV = QtTaskTree::isInvocable<void, Handler>();
static_assert(isVTD || isVT || isVD || isV,
"Task done handler needs to take (const TaskTree &, DoneWith), (const Task &), "
"(DoneWith) or (void) as arguments and has to return void. "
"The passed handler doesn't fulfill these requirements.");
if constexpr (isVTD)
std::invoke(handler, taskTree, result);
else if constexpr (isVT)
std::invoke(handler, taskTree);
else if constexpr (isVD)
std::invoke(handler, result);
else if constexpr (isV)
std::invoke(handler);
};
}
class QSingleTaskTreeRunner final
{
Q_DECLARE_PRIVATE(QSingleTaskTreeRunner)
Q_DISABLE_COPY_MOVE(QSingleTaskTreeRunner)
public:
Q_TASKTREE_EXPORT QSingleTaskTreeRunner();
Q_TASKTREE_EXPORT ~QSingleTaskTreeRunner();
Q_TASKTREE_EXPORT bool isRunning() const;
Q_TASKTREE_EXPORT void cancel() ;
Q_TASKTREE_EXPORT void reset() ;
template <typename SetupHandler = TreeSetupHandler, typename DoneHandler = TreeDoneHandler>
void start(const Group &recipe,
SetupHandler &&setupHandler = {},
DoneHandler &&doneHandler = {},
CallDone callDone = CallDoneFlag::Always)
{
startImpl(recipe,
wrapTreeSetupHandler(std::forward<SetupHandler>(setupHandler)),
wrapTreeDoneHandler(std::forward<DoneHandler>(doneHandler)),
callDone);
}
private:
Q_TASKTREE_EXPORT void startImpl(const Group &recipe,
const TreeSetupHandler &setupHandler,
const TreeDoneHandler &doneHandler,
CallDone callDone);
std::unique_ptr<QSingleTaskTreeRunnerPrivate> d_ptr;
};
class QSequentialTaskTreeRunner final
{
Q_DECLARE_PRIVATE(QSequentialTaskTreeRunner)
Q_DISABLE_COPY_MOVE(QSequentialTaskTreeRunner)
public:
Q_TASKTREE_EXPORT QSequentialTaskTreeRunner();
Q_TASKTREE_EXPORT ~QSequentialTaskTreeRunner();
Q_TASKTREE_EXPORT bool isRunning() const;
Q_TASKTREE_EXPORT void cancel();
Q_TASKTREE_EXPORT void reset();
Q_TASKTREE_EXPORT void cancelCurrent();
Q_TASKTREE_EXPORT void resetCurrent();
template <typename SetupHandler = TreeSetupHandler, typename DoneHandler = TreeDoneHandler>
void enqueue(const Group &recipe,
SetupHandler &&setupHandler = {},
DoneHandler &&doneHandler = {},
CallDone callDone = CallDoneFlag::Always)
{
enqueueImpl(recipe,
wrapTreeSetupHandler(std::forward<SetupHandler>(setupHandler)),
wrapTreeDoneHandler(std::forward<DoneHandler>(doneHandler)),
callDone);
}
private:
Q_TASKTREE_EXPORT void enqueueImpl(const Group &recipe,
const TreeSetupHandler &setupHandler,
const TreeDoneHandler &doneHandler,
CallDone callDone);
std::unique_ptr<QSequentialTaskTreeRunnerPrivate> d_ptr;
};
class QParallelTaskTreeRunner final
{
Q_DECLARE_PRIVATE(QParallelTaskTreeRunner)
Q_DISABLE_COPY_MOVE(QParallelTaskTreeRunner)
public:
Q_TASKTREE_EXPORT QParallelTaskTreeRunner();
Q_TASKTREE_EXPORT ~QParallelTaskTreeRunner();
Q_TASKTREE_EXPORT bool isRunning() const;
Q_TASKTREE_EXPORT void cancel();
Q_TASKTREE_EXPORT void reset();
template <typename SetupHandler = TreeSetupHandler, typename DoneHandler = TreeDoneHandler>
void start(const Group &recipe,
SetupHandler &&setupHandler = {},
DoneHandler &&doneHandler = {},
CallDone callDone = CallDoneFlag::Always)
{
startImpl(recipe,
wrapTreeSetupHandler(std::forward<SetupHandler>(setupHandler)),
wrapTreeDoneHandler(std::forward<DoneHandler>(doneHandler)),
callDone);
}
private:
Q_TASKTREE_EXPORT void startImpl(const Group &recipe,
const TreeSetupHandler &setupHandler,
const TreeDoneHandler &doneHandler,
CallDone callDone);
std::unique_ptr<QParallelTaskTreeRunnerPrivate> d_ptr;
};
template <typename Key>
class QMappedTaskTreeRunner final
{
Q_DISABLE_COPY_MOVE(QMappedTaskTreeRunner)
public:
QMappedTaskTreeRunner() = default;
~QMappedTaskTreeRunner() = default;
bool isRunning() const { return !m_taskTrees.empty(); }
void cancel()
{
while (!m_taskTrees.empty())
m_taskTrees.begin()->second->cancel();
}
void reset() { m_taskTrees.clear(); }
bool isKeyRunning(const Key &key) const { return m_taskTrees.find(key) != m_taskTrees.end(); }
void cancelKey(const Key &key)
{
if (const auto it = m_taskTrees.find(key); it != m_taskTrees.end())
it->second->cancel();
}
void resetKey(const Key &key)
{
if (const auto it = m_taskTrees.find(key); it != m_taskTrees.end())
m_taskTrees.erase(it);
}
template <typename SetupHandler = TreeSetupHandler, typename DoneHandler = TreeDoneHandler>
void start(const Key &key, const Group &recipe,
SetupHandler &&setupHandler = {},
DoneHandler &&doneHandler = {},
CallDone callDone = CallDoneFlag::Always)
{
startImpl(key, recipe,
wrapTreeSetupHandler(std::forward<SetupHandler>(setupHandler)),
wrapTreeDoneHandler(std::forward<DoneHandler>(doneHandler)),
callDone);
}
private:
void startImpl(const Key &key, const Group &recipe,
const TreeSetupHandler &setupHandler,
const TreeDoneHandler &doneHandler,
CallDone callDone)
{
QTaskTree *taskTree = new QTaskTree(recipe);
QObject::connect(taskTree, &QTaskTree::done, taskTree,
[this, key, doneHandler, callDone](DoneWith result) {
const auto it = m_taskTrees.find(key);
QTaskTree *runningTaskTree = it->second.release();
runningTaskTree->deleteLater();
m_taskTrees.erase(it);
if (doneHandler && shouldCallDone(callDone, result))
doneHandler(*runningTaskTree, result);
});
m_taskTrees[key].reset(taskTree);
if (setupHandler)
setupHandler(*taskTree);
taskTree->start();
}
std::unordered_map<Key, std::unique_ptr<QTaskTree>> m_taskTrees;
};
} // namespace QtTaskTree
QT_END_NAMESPACE
#endif // QTASKTREE_QTASKTREERUNNER_H