TaskTree: Introduce For element

Improve code readability by requiring iteratable Group
to be named For.

The For's c'tor requires an iterator element as a first arg.

The For's c'tor allows for passing exactly one iterator element.

It's not possible to place iterators inside Group element anymore.

Change-Id: I9dfe2c0da058abac161f66c4e336da2417c383f1
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2024-07-09 21:36:17 +02:00
parent c841a99db9
commit 779390829c
19 changed files with 110 additions and 76 deletions

View File

@@ -207,13 +207,12 @@ public:
: m_type(Type::Storage)
, m_storageList{storage} {}
GroupItem(const Loop &loop) : GroupItem(GroupData{{}, {}, {}, loop}) {}
// TODO: Add tests.
GroupItem(const QList<GroupItem> &children) : m_type(Type::List) { addChildren(children); }
GroupItem(std::initializer_list<GroupItem> children) : m_type(Type::List) { addChildren(children); }
protected:
GroupItem(const Loop &loop) : GroupItem(GroupData{{}, {}, {}, loop}) {}
// Internal, provided by CustomTask
using InterfaceCreateHandler = std::function<TaskInterface *(void)>;
// Called prior to task start, just after createHandler
@@ -274,6 +273,7 @@ protected:
private:
friend class ContainerNode;
friend class For;
friend class TaskNode;
friend class TaskTreePrivate;
friend class ParallelLimitFunctor;
@@ -433,11 +433,42 @@ TASKING_EXPORT extern const GroupItem nullItem;
TASKING_EXPORT extern const ExecutableItem successItem;
TASKING_EXPORT extern const ExecutableItem errorItem;
class TASKING_EXPORT Forever final : public Group
class TASKING_EXPORT For : public Group
{
public:
Forever(const QList<GroupItem> &children) : Group({LoopForever(), children}) {}
Forever(std::initializer_list<GroupItem> children) : Group({LoopForever(), children}) {}
template <typename ...Args>
For(const Loop &loop, const Args &...args)
: Group(withLoop(loop, args...)) { }
protected:
For(const Loop &loop, const QList<GroupItem> &children) : Group({loop, children}) {}
For(const Loop &loop, std::initializer_list<GroupItem> children) : Group({loop, children}) {}
private:
template <typename ...Args>
QList<GroupItem> withLoop(const Loop &loop, const Args &...args) {
QList<GroupItem> children{GroupItem(loop)};
appendChildren(std::make_tuple(args...), &children);
return children;
}
template <typename Tuple, std::size_t N = 0>
void appendChildren(const Tuple &tuple, QList<GroupItem> *children) {
constexpr auto TupleSize = std::tuple_size_v<Tuple>;
if constexpr (TupleSize > 0) {
// static_assert(workflowPolicyCount<Tuple>() <= 1, "Too many workflow policies in one group.");
children->append(std::get<N>(tuple));
if constexpr (N + 1 < TupleSize)
appendChildren<Tuple, N + 1>(tuple, children);
}
}
};
class TASKING_EXPORT Forever final : public For
{
public:
Forever(const QList<GroupItem> &children) : For(LoopForever(), children) {}
Forever(std::initializer_list<GroupItem> children) : For(LoopForever(), children) {}
};
// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()

View File

@@ -830,9 +830,9 @@ AndroidDeviceManagerInstance::AndroidDeviceManagerInstance(QObject *parent)
// otherwise, Android Studio would give an error during parsing also. So this fix
// aim to keep support for Qt Creator and Android Studio.
m_avdListRecipe = Group {
storage,
m_avdListRecipe = For {
iterator,
storage,
ProcessTask(onProcessSetup, onProcessDone)
};
}

View File

@@ -179,20 +179,19 @@ void AndroidQmlPreviewWorker::startPidWatcher()
const TimeoutTask timeout([](std::chrono::milliseconds &timeout) { timeout = 2s; });
const Group root {
Group {
const Group recipe {
For {
pidIterator,
ProcessTask(onPidSetup, onPidDone, CallDoneIf::Success),
timeout
}.withTimeout(20s),
Group {
For {
alivePidIterator,
ProcessTask(onPidSetup, onAlivePidDone),
timeout
}
};
m_pidRunner.start(root);
m_pidRunner.start(recipe);
}
void AndroidQmlPreviewWorker::startLogcat()

View File

@@ -294,14 +294,14 @@ static GroupItem installationRecipe(const Storage<DialogStorage> &dialogStorage,
return Group {
onGroupSetup(onSetup),
Group {
finishAllAndSuccess,
For {
uninstallIterator,
finishAllAndSuccess,
ProcessTask(onUninstallSetup, onDone)
},
Group {
finishAllAndSuccess,
For {
installIterator,
finishAllAndSuccess,
ProcessTask(onInstallSetup, onDone)
}
};

View File

@@ -417,14 +417,14 @@ void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths,
if (!results.isEmpty())
emit testParseResultsReady(results);
};
const Group root {
const For recipe {
LoopRepeat(filteredFiles.size()),
parallelLimit(limit),
storage,
onGroupSetup([storage, filteredFiles] { *storage = filteredFiles.cbegin(); }),
LoopRepeat(filteredFiles.size()),
AsyncTask<TestParseResultPtr>(onSetup, onDone, CallDoneIf::Success)
};
m_taskTreeRunner.start(root);
m_taskTreeRunner.start(recipe);
}
void TestCodeParser::onTaskStarted(Id type)

View File

@@ -459,15 +459,15 @@ void TestRunner::runTestsHelper()
}
};
const Group root {
finishAllAndSuccess,
const For recipe {
iterator,
finishAllAndSuccess,
Group {
storage,
ProcessTask(onSetup, onDone)
}
};
m_taskTreeRunner.start(root);
m_taskTreeRunner.start(recipe);
}
static void processOutput(TestOutputReader *outputreader, const QString &msg, OutputFormat format)

View File

@@ -691,7 +691,7 @@ static Group authorizationRecipe()
*serverUrlStorage = unauthorizedDashboardStorage->url;
}),
},
Group {
For {
LoopUntil(onCredentialLoopCondition),
CredentialQueryTask(onGetCredentialSetup, onGetCredentialDone),
Group {

View File

@@ -224,10 +224,10 @@ GroupItem clangToolTask(const AnalyzeUnits &units,
error});
};
return Group {
return For {
iterator,
parallelLimit(qMax(1, input.runSettings.parallelJobs())),
finishAllAndSuccess,
iterator,
Group {
storage,
onGroupSetup(onSetup),

View File

@@ -383,9 +383,9 @@ void LocatorMatcher::start()
parallel,
collectorStorage,
AsyncTask<LocatorFilterEntries>(onCollectorSetup, onCollectorDone),
Group {
parallelLimit(d->m_parallelLimit),
For {
iterator,
parallelLimit(d->m_parallelLimit),
TaskTreeTask(onTaskTreeSetup)
}
};

View File

@@ -142,16 +142,16 @@ DiffFilesController::DiffFilesController(IDocument *document)
setDiffFiles(finalList);
};
const Group root {
const For recipe {
iterator,
parallelIdealThreadCountLimit,
finishAllAndSuccess,
storage,
iterator,
onGroupSetup(onSetup),
AsyncTask<FileData>(onDiffSetup, onDiffDone, CallDoneIf::Success),
onGroupDone(onDone)
};
setReloadRecipe(root);
setReloadRecipe(recipe);
}
class DiffCurrentFileController : public DiffFilesController

View File

@@ -506,10 +506,10 @@ ShowController::ShowController(IDocument *document, const QString &id)
updateDescription(*data);
};
const Group recipe {
const For recipe {
iterator,
parallel,
continueOnSuccess,
iterator,
ProcessTask(onFollowSetup, onFollowDone, CallDoneIf::Success),
onGroupDone(onDone, CallDoneIf::Error)
};

View File

@@ -196,10 +196,10 @@ static Group installRecipe(
return DoneResult::Success;
};
return Group{
return For {
installOptionsIt,
storage,
parallelIdealThreadCountLimit,
installOptionsIt,
Group{
onGroupSetup([emitResult, storage, installOptionsIt] {
const QString fileName = installOptionsIt->url.fileName();

View File

@@ -237,12 +237,12 @@ static QList<FileNode *> scanForFilesHelper(
}
};
const Group group{
Utils::HostOsInfo::isLinuxHost() ? parallelLimit(2) : parallelIdealThreadCountLimit,
const For recipe {
iterator,
Utils::HostOsInfo::isLinuxHost() ? parallelLimit(2) : parallelIdealThreadCountLimit,
Utils::AsyncTask<DirectoryScanResult>(onSetup, onDone)
};
TaskTree::runBlocking(group);
TaskTree::runBlocking(recipe);
}
return fileNodes;
}

View File

@@ -1679,21 +1679,21 @@ private:
}
};
const Group group{
Group{
parallelIdealThreadCountLimit,
const Group recipe {
For {
iteratorParentDirs,
parallelIdealThreadCountLimit,
AsyncTask<expected_str<void>>(onCreateDirSetup, onCreateDirDone),
},
Group{
parallelLimit(2),
For {
iterator,
parallelLimit(2),
counterStorage,
AsyncTask<expected_str<void>>(onCopySetup, onCopyDone),
},
};
m_taskTree.start(group, {}, [this](DoneWith result) {
m_taskTree.start(recipe, {}, [this](DoneWith result) {
ProcessResultData resultData;
if (result != DoneWith::Success) {
resultData.m_exitCode = -1;

View File

@@ -278,15 +278,14 @@ GroupItem GenericLinuxDeviceTesterPrivate::commandTasks() const
emit q->errorMessage(message);
};
const Group root {
return For {
iterator,
continueOnError,
onGroupSetup([this] {
emit q->progressMessage(Tr::tr("Checking if required commands are available..."));
}),
iterator,
ProcessTask(onSetup, onDone)
};
return root;
}
} // namespace Internal

View File

@@ -2735,12 +2735,12 @@ void tst_Tasking::testTree_data()
{
const QList<GroupItem> successItems {
storage,
LoopRepeat(2),
createSuccessTask(1),
createSuccessTask(2)
};
const Group rootSequentialSuccess {
const For rootSequentialSuccess {
LoopRepeat(2),
sequential,
successItems
};
@@ -2755,7 +2755,8 @@ void tst_Tasking::testTree_data()
{2, Handler::Success}
};
const Group rootParallelSuccess {
const For rootParallelSuccess {
LoopRepeat(2),
parallel,
successItems
};
@@ -2770,7 +2771,8 @@ void tst_Tasking::testTree_data()
{2, Handler::Success}
};
const Group rootParallelLimitSuccess {
const For rootParallelLimitSuccess {
LoopRepeat(2),
parallelLimit(2),
successItems
};
@@ -2787,12 +2789,12 @@ void tst_Tasking::testTree_data()
const QList<GroupItem> errorItems {
storage,
LoopRepeat(2),
createSuccessTask(1),
createFailingTask(2)
};
const Group rootSequentialError {
const For rootSequentialError {
LoopRepeat(2),
sequential,
errorItems
};
@@ -2803,7 +2805,8 @@ void tst_Tasking::testTree_data()
{2, Handler::Error}
};
const Group rootParallelError {
const For rootParallelError {
LoopRepeat(2),
parallel,
errorItems
};
@@ -2818,7 +2821,8 @@ void tst_Tasking::testTree_data()
{2, Handler::Canceled}
};
const Group rootParallelLimitError {
const For rootParallelLimitError {
LoopRepeat(2),
parallelLimit(2),
errorItems
};
@@ -2873,12 +2877,12 @@ void tst_Tasking::testTree_data()
const QList<GroupItem> items {
storage,
loop,
TestTask(onSetupContinue(1), onDone(1)),
TestTask(onSetupStop(2), onDone(2))
};
const Group rootSequential {
const For rootSequential {
loop,
sequential,
items
};
@@ -2896,7 +2900,8 @@ void tst_Tasking::testTree_data()
{22, Handler::Setup}
};
const Group rootParallel {
const For rootParallel {
loop,
parallel,
items
};
@@ -2914,7 +2919,8 @@ void tst_Tasking::testTree_data()
{21, Handler::Success}
};
const Group rootParallelLimit {
const For rootParallelLimit {
loop,
parallelLimit(2),
items
};
@@ -2942,9 +2948,9 @@ void tst_Tasking::testTree_data()
{
// Check if task tree finishes with the right progress value when LoopUntil(false).
const Group root {
storage,
const For root {
LoopUntil([](int) { return false; }),
storage,
createSuccessTask(1)
};
QTest::newRow("ProgressWithLoopUntilFalse")
@@ -2953,10 +2959,10 @@ void tst_Tasking::testTree_data()
{
// Check if task tree finishes with the right progress value when nested LoopUntil(false).
const Group root {
storage,
const For root {
LoopUntil([](int index) { return index < 2; }),
Group {
storage,
For {
LoopUntil([](int) { return false; }),
createSuccessTask(1)
}
@@ -2978,9 +2984,9 @@ void tst_Tasking::testTree_data()
{
// Check if task tree finishes with the right progress value when nested LoopUntil(false).
const Group root {
storage,
const For root {
LoopUntil([](int index) { return index < 2; }),
storage,
Group {
onGroupSetup([] { return SetupResult::StopWithSuccess; }),
createSuccessTask(1)
@@ -3157,10 +3163,10 @@ void tst_Tasking::testTree_data()
return DoneResult::Error;
};
const Group root {
const For root {
iterator,
storage,
parallel,
iterator,
TestTask(onSetup, onDone)
};

View File

@@ -509,9 +509,9 @@ void AssetDownloader::start()
onGroupSetup(onSkipIfAllAssetsPresent),
NetworkQueryTask(onZipDownloadSetup, onZipDownloadDone),
ConcurrentCallTask<void>(onUnzipSetup, onUnzipDone),
Group {
parallelIdealThreadCountLimit,
For {
downloadIterator,
parallelIdealThreadCountLimit,
onGroupSetup(onAssetsDownloadGroupSetup),
Group {
assetStorage,
@@ -519,9 +519,9 @@ void AssetDownloader::start()
ConcurrentCallTask<void>(onAssetWriteSetup, onAssetWriteDone)
}
},
Group {
parallelIdealThreadCountLimit,
For {
copyIterator,
parallelIdealThreadCountLimit,
onGroupSetup(onAssetsCopyGroupSetup),
ConcurrentCallTask<void>(onAssetCopySetup, onAssetCopyDone)
}

View File

@@ -74,7 +74,7 @@ Group recipe(const Storage<ExternalData> &externalStorage)
internalStorage,
NetworkQueryTask(onDownloadSetup, onDownloadDone),
ConcurrentCallTask<QImage>(onReadSetup, onReadDone),
Group {
For {
repeater,
parallelIdealThreadCountLimit,
ConcurrentCallTask<QImage>(onScaleSetup, onScaleDone)

View File

@@ -93,10 +93,10 @@ void Images::process()
labels[it]->setText(tr("Image\nData\nError."));
};
const QList<GroupItem> tasks {
const For recipe {
iterator,
finishAllAndSuccess,
parallel,
iterator,
onGroupSetup(onRootSetup),
Group {
storage,
@@ -105,8 +105,7 @@ void Images::process()
},
onGroupDone(onRootDone, CallDoneIf::Success)
};
taskTreeRunner.start(tasks);
taskTreeRunner.start(recipe);
}
void Images::initLayout(qsizetype count)