TaskTree: Refactor For loops

Make the syntax more consistent with conditional API.

Change-Id: I52353d0a0044252e1e3bac0b424ac7c22d927262
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
Jarek Kobus
2024-09-14 00:50:35 +02:00
parent 6853e47fb8
commit 45157e7c42
26 changed files with 82 additions and 121 deletions

View File

@@ -1280,6 +1280,11 @@ const GroupItem nullItem = GroupItem({});
const ExecutableItem successItem = Group { finishAllAndSuccess };
const ExecutableItem errorItem = Group { finishAllAndError };
Group operator>>(const For &forItem, const Do &doItem)
{
return {forItem.m_loop, doItem.m_children};
}
// Please note the thread_local keyword below guarantees a separate instance per thread.
// The s_activeTaskTrees is currently used internally only and is not exposed in the public API.
// It serves for withLog() implementation now. Add a note here when a new usage is introduced.

View File

@@ -196,6 +196,10 @@ private:
}
};
class Do;
class For;
class Group;
class TASKING_EXPORT GroupItem
{
public:
@@ -274,8 +278,8 @@ protected:
}
private:
TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
friend class ContainerNode;
friend class For;
friend class TaskNode;
friend class TaskTreePrivate;
friend class ParallelLimitFunctor;
@@ -435,42 +439,36 @@ TASKING_EXPORT extern const GroupItem nullItem;
TASKING_EXPORT extern const ExecutableItem successItem;
TASKING_EXPORT extern const ExecutableItem errorItem;
class TASKING_EXPORT For : public Group
class TASKING_EXPORT For final
{
public:
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}) {}
explicit For(const Loop &loop) : m_loop(loop) {}
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;
}
TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
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);
}
}
Loop m_loop;
};
class TASKING_EXPORT Forever final : public For
class TASKING_EXPORT Do final
{
public:
Forever(const QList<GroupItem> &children) : For(LoopForever(), children) {}
Forever(std::initializer_list<GroupItem> children) : For(LoopForever(), children) {}
explicit Do(const QList<GroupItem> &children) : m_children(children) {}
explicit Do(std::initializer_list<GroupItem> children) : m_children(children) {}
private:
TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
GroupItem m_children;
};
class TASKING_EXPORT Forever final : public ExecutableItem
{
public:
explicit Forever(const QList<GroupItem> &children)
{ addChildren({ For (LoopForever()) >> Do { children } } ); }
explicit Forever(std::initializer_list<GroupItem> children)
{ addChildren({ For (LoopForever()) >> Do { children } } ); }
};
// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()

View File

@@ -138,8 +138,7 @@ static ExecutableItem serialNumberRecipe(const QString &avdName, const Storage<Q
return Group {
outputStorage,
AndroidConfig::devicesCommandOutputRecipe(outputStorage),
For {
iterator,
For (iterator) >> Do {
parallel,
stopOnSuccess,
Group {

View File

@@ -417,13 +417,11 @@ GroupItem AndroidDeployQtStep::runRecipe()
onGroupDone(onSerialNumberGroupDone)
},
deployRecipe(),
For {
iterator,
For (iterator) >> Do {
parallelIdealThreadCountLimit,
AsyncTask<void>(onRemoveFileSetup)
},
For {
iterator,
For (iterator) >> Do {
ProcessTask(onAdbSetup, onAdbDone)
}
};

View File

@@ -858,8 +858,7 @@ 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 = For {
iterator,
m_avdListRecipe = For (iterator) >> Do {
storage,
ProcessTask(onProcessSetup, onProcessDone)
};

View File

@@ -582,8 +582,7 @@ static ExecutableItem preStartRecipe(RunnerStorage *storage)
return Group {
argsStorage,
onGroupSetup(onArgsSetup),
For {
iterator,
For (iterator) >> Do {
ProcessTask(onPreCommandSetup, onPreCommandDone, CallDoneIf::Error)
},
Group {
@@ -614,8 +613,7 @@ static ExecutableItem postDoneRecipe(RunnerStorage *storage)
return Group {
finishAllAndSuccess,
For {
iterator,
For (iterator) >> Do {
ProcessTask(onProcessSetup)
},
onGroupDone(onDone)
@@ -674,8 +672,7 @@ static ExecutableItem uploadDebugServerRecipe(RunnerStorage *storage, const QStr
return Group {
tempDebugServerPathStorage,
For {
iterator,
For (iterator) >> Do {
ProcessTask(onDeviceFileExistsSetup, onDeviceFileExistsDone)
},
Sync(onTempDebugServerPath),

View File

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

View File

@@ -417,8 +417,7 @@ void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths,
if (!results.isEmpty())
emit testParseResultsReady(results);
};
const For recipe {
LoopRepeat(filteredFiles.size()),
const Group recipe = For (LoopRepeat(filteredFiles.size())) >> Do {
parallelLimit(limit),
storage,
onGroupSetup([storage, filteredFiles] { *storage = filteredFiles.cbegin(); }),

View File

@@ -461,8 +461,7 @@ void TestRunner::runTestsHelper()
}
};
const For recipe {
iterator,
const Group recipe = For (iterator) >> Do {
finishAllAndSuccess,
Group {
storage,

View File

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

View File

@@ -181,8 +181,7 @@ GroupItem clangToolTask(CppEditor::ClangToolType toolType,
error});
};
return For {
iterator,
return For (iterator) >> Do {
parallelLimit(qMax(1, input.runSettings.parallelJobs())),
finishAllAndSuccess,
Group {

View File

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

View File

@@ -142,8 +142,7 @@ DiffFilesController::DiffFilesController(IDocument *document)
setDiffFiles(finalList);
};
const For recipe {
iterator,
const Group recipe = For (iterator) >> Do {
parallelIdealThreadCountLimit,
finishAllAndSuccess,
storage,

View File

@@ -534,8 +534,7 @@ ShowController::ShowController(IDocument *document, const QString &id)
updateDescription(*data);
};
const For recipe {
iterator,
const Group recipe = For (iterator) >> Do {
parallel,
continueOnSuccess,
ProcessTask(onFollowSetup, onFollowDone, CallDoneIf::Success),

View File

@@ -196,8 +196,7 @@ static Group installRecipe(
return DoneResult::Success;
};
return For {
installOptionsIt,
return For (installOptionsIt) >> Do {
storage,
parallelIdealThreadCountLimit,
Group{

View File

@@ -246,8 +246,7 @@ static QList<FileNode *> scanForFilesHelper(
}
};
const For recipe {
iterator,
const Group recipe = For (iterator) >> Do {
Utils::HostOsInfo::isLinuxHost() ? parallelLimit(2) : parallelIdealThreadCountLimit,
Utils::AsyncTask<DirectoryScanResult>(onSetup, onDone)
};

View File

@@ -1646,13 +1646,11 @@ private:
};
const Group recipe {
For {
iteratorParentDirs,
For (iteratorParentDirs) >> Do {
parallelIdealThreadCountLimit,
AsyncTask<expected_str<void>>(onCreateDirSetup, onCreateDirDone),
},
For {
iterator,
For (iterator) >> Do {
parallelLimit(2),
counterStorage,
AsyncTask<expected_str<void>>(onCopySetup, onCopyDone),

View File

@@ -278,8 +278,7 @@ GroupItem GenericLinuxDeviceTesterPrivate::commandTasks() const
emit q->errorMessage(message);
};
return For {
iterator,
return For (iterator) >> Do {
continueOnError,
onGroupSetup([this] {
emit q->progressMessage(Tr::tr("Checking if required commands are available..."));

View File

@@ -2741,8 +2741,7 @@ void tst_Tasking::testTree_data()
createSuccessTask(2)
};
const For rootSequentialSuccess {
LoopRepeat(2),
const Group rootSequentialSuccess = For (LoopRepeat(2)) >> Do {
sequential,
successItems
};
@@ -2757,8 +2756,7 @@ void tst_Tasking::testTree_data()
{2, Handler::Success}
};
const For rootParallelSuccess {
LoopRepeat(2),
const Group rootParallelSuccess = For (LoopRepeat(2)) >> Do {
parallel,
successItems
};
@@ -2773,8 +2771,7 @@ void tst_Tasking::testTree_data()
{2, Handler::Success}
};
const For rootParallelLimitSuccess {
LoopRepeat(2),
const Group rootParallelLimitSuccess = For (LoopRepeat(2)) >> Do {
parallelLimit(2),
successItems
};
@@ -2795,8 +2792,7 @@ void tst_Tasking::testTree_data()
createFailingTask(2)
};
const For rootSequentialError {
LoopRepeat(2),
const Group rootSequentialError = For (LoopRepeat(2)) >> Do {
sequential,
errorItems
};
@@ -2807,8 +2803,7 @@ void tst_Tasking::testTree_data()
{2, Handler::Error}
};
const For rootParallelError {
LoopRepeat(2),
const Group rootParallelError = For (LoopRepeat(2)) >> Do {
parallel,
errorItems
};
@@ -2823,8 +2818,7 @@ void tst_Tasking::testTree_data()
{2, Handler::Canceled}
};
const For rootParallelLimitError {
LoopRepeat(2),
const Group rootParallelLimitError = For (LoopRepeat(2)) >> Do {
parallelLimit(2),
errorItems
};
@@ -2883,8 +2877,7 @@ void tst_Tasking::testTree_data()
TestTask(onSetupStop(2), onDone(2))
};
const For rootSequential {
loop,
const Group rootSequential = For(loop) >> Do {
sequential,
items
};
@@ -2902,8 +2895,7 @@ void tst_Tasking::testTree_data()
{22, Handler::Setup}
};
const For rootParallel {
loop,
const Group rootParallel = For(loop) >> Do {
parallel,
items
};
@@ -2921,8 +2913,7 @@ void tst_Tasking::testTree_data()
{21, Handler::Success}
};
const For rootParallelLimit {
loop,
const Group rootParallelLimit = For(loop) >> Do {
parallelLimit(2),
items
};
@@ -2950,8 +2941,7 @@ void tst_Tasking::testTree_data()
{
// Check if task tree finishes with the right progress value when LoopUntil(false).
const For root {
LoopUntil([](int) { return false; }),
const Group root = For(LoopUntil([](int) { return false; })) >> Do {
storage,
createSuccessTask(1)
};
@@ -2961,11 +2951,9 @@ void tst_Tasking::testTree_data()
{
// Check if task tree finishes with the right progress value when nested LoopUntil(false).
const For root {
LoopUntil([](int index) { return index < 2; }),
const Group root = For (LoopUntil([](int index) { return index < 2; })) >> Do {
storage,
For {
LoopUntil([](int) { return false; }),
For (LoopUntil([](int) { return false; })) >> Do {
createSuccessTask(1)
}
};
@@ -2975,13 +2963,12 @@ void tst_Tasking::testTree_data()
{
// Check if LoopUntil is executed with empty loop body.
const For root {
LoopUntil([storage](int iteration) {
storage->m_log.append({iteration, Handler::Iteration});
return iteration < 3;
}),
storage
};
const LoopUntil iterator([storage](int iteration) {
storage->m_log.append({iteration, Handler::Iteration});
return iteration < 3;
});
const Group root = For(iterator) >> Do { storage };
const Log log {
{0, Handler::Iteration},
@@ -3007,8 +2994,7 @@ void tst_Tasking::testTree_data()
{
// Check if task tree finishes with the right progress value when nested LoopUntil(false).
const For root {
LoopUntil([](int index) { return index < 2; }),
const Group root = For (LoopUntil([](int index) { return index < 2; })) >> Do {
storage,
Group {
onGroupSetup([] { return SetupResult::StopWithSuccess; }),
@@ -3186,8 +3172,7 @@ void tst_Tasking::testTree_data()
return DoneResult::Error;
};
const For root {
iterator,
const Group root = For (iterator) >> Do {
storage,
parallel,
TestTask(onSetup, onDone)

View File

@@ -149,8 +149,7 @@ private slots:
parentDir.filePath(destDirName));
};
const For recipe = {
iterator,
const Group recipe = For (iterator) >> Do {
parallelIdealThreadCountLimit, // Parallelize tree generation
AsyncTask<void>(onCopySetup)
};
@@ -169,8 +168,7 @@ private slots:
parentDir.filePath(dirName(iterator.iteration() + 1)));
};
const For recipe = {
iterator,
const Group recipe = For (iterator) >> Do {
parallelIdealThreadCountLimit, // Parallelize tree removal
AsyncTask<void>(onSetup)
};

View File

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

View File

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

View File

@@ -93,8 +93,7 @@ void Images::process()
labels[it]->setText(tr("Image\nData\nError."));
};
const For recipe {
iterator,
const Group recipe = For (iterator) >> Do {
finishAllAndSuccess,
parallel,
onGroupSetup(onRootSetup),

View File

@@ -16,7 +16,7 @@ int main(int argc, char **argv)
QApplication app(argc, argv);
GlueInterface iface;
TaskTree taskTree(recipe(&iface));
TaskTree taskTree({recipe(&iface)});
TrafficLight widget(&iface);
widget.show();

View File

@@ -11,7 +11,7 @@
using namespace Tasking;
using namespace std::chrono;
Group recipe(GlueInterface *iface)
ExecutableItem recipe(GlueInterface *iface)
{
return Forever {
finishAllAndSuccess,

View File

@@ -4,10 +4,10 @@
#ifndef RECIPE_H
#define RECIPE_H
namespace Tasking { class Group; }
namespace Tasking { class ExecutableItem; }
class GlueInterface;
Tasking::Group recipe(GlueInterface *iface);
Tasking::ExecutableItem recipe(GlueInterface *iface);
#endif // RECIPE_H