Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
tasktree.h
Go to the documentation of this file.
1// Copyright (C) 2024 Jarek Kobus
2// Copyright (C) 2024 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#ifndef TASKING_TASKTREE_H
6#define TASKING_TASKTREE_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include "tasking_global.h"
20
21#include <QtCore/QList>
22#include <QtCore/QObject>
23
24#include <memory>
25
26QT_BEGIN_NAMESPACE
27template <class T>
28class QFuture;
29
30namespace Tasking {
31
32class Do;
33class For;
34class Group;
35class GroupItem;
37
38Q_NAMESPACE_EXPORT(TASKING_EXPORT)
39
40// WorkflowPolicy:
41// 1. When all children finished with success -> report success, otherwise:
42// a) Report error on first error and stop executing other children (including their subtree).
43// b) On first error - continue executing all children and report error afterwards.
44// 2. When all children finished with error -> report error, otherwise:
45// a) Report success on first success and stop executing other children (including their subtree).
46// b) On first success - continue executing all children and report success afterwards.
47// 3. Stops on first finished child. In sequential mode it will never run other children then the first one.
48// Useful only in parallel mode.
49// 4. Always run all children, let them finish, ignore their results and report success afterwards.
50// 5. Always run all children, let them finish, ignore their results and report error afterwards.
51
53{
54 StopOnError, // 1a - Reports error on first child error, otherwise success (if all children were success).
55 ContinueOnError, // 1b - The same, but children execution continues. Reports success when no children.
56 StopOnSuccess, // 2a - Reports success on first child success, otherwise error (if all children were error).
57 ContinueOnSuccess, // 2b - The same, but children execution continues. Reports error when no children.
58 StopOnSuccessOrError, // 3 - Stops on first finished child and report its result.
59 FinishAllAndSuccess, // 4 - Reports success after all children finished.
60 FinishAllAndError // 5 - Reports error after all children finished.
61};
62Q_ENUM_NS(WorkflowPolicy)
63
70Q_ENUM_NS(SetupResult)
71
72enum class DoneResult
73{
76};
77Q_ENUM_NS(DoneResult)
78
79enum class DoneWith
80{
84};
85Q_ENUM_NS(DoneWith)
86
93Q_ENUM_NS(CallDoneIf)
94
96
97class LoopData;
98class StorageData;
99class TaskTreePrivate;
100
102{
103 Q_OBJECT
104
105Q_SIGNALS:
107
108private:
109 template <typename Task, typename Deleter> friend class TaskAdapter;
110 friend class TaskTreePrivate;
111 TaskInterface() = default;
112#ifdef Q_QDOC
113protected:
114#endif
115 virtual void start() = 0;
116};
117
119{
120public:
121 using Condition = std::function<bool(int)>; // Takes iteration, called prior to each iteration.
122 using ValueGetter = std::function<const void *(int)>; // Takes iteration, returns ptr to ref.
123
124 int iteration() const;
125
126protected:
127 Loop(); // LoopForever
128 Loop(int count, const ValueGetter &valueGetter = {}); // LoopRepeat, LoopList
129 Loop(const Condition &condition); // LoopUntil
130
131 const void *valuePtr() const;
132
133private:
135 friend class TaskTreePrivate;
136 std::shared_ptr<LoopData> m_loopData;
137};
138
139class TASKING_EXPORT LoopForever final : public Loop
140{
141public:
143};
144
145class TASKING_EXPORT LoopRepeat final : public Loop
146{
147public:
148 LoopRepeat(int count) : Loop(count) {}
149};
150
151class TASKING_EXPORT LoopUntil final : public Loop
152{
153public:
154 LoopUntil(const Condition &condition) : Loop(condition) {}
155};
156
157template <typename T>
158class LoopList final : public Loop
159{
160public:
161 LoopList(const QList<T> &list) : Loop(list.size(), [list](int i) { return &list.at(i); }) {}
162 const T *operator->() const { return static_cast<const T *>(valuePtr()); }
163 const T &operator*() const { return *static_cast<const T *>(valuePtr()); }
164};
165
167{
168private:
169 using StorageConstructor = std::function<void *(void)>;
170 using StorageDestructor = std::function<void(void *)>;
171 using StorageHandler = std::function<void(void *)>;
172
173 StorageBase(const StorageConstructor &ctor, const StorageDestructor &dtor);
174
175 void *activeStorageVoid() const;
176
177 friend bool operator==(const StorageBase &first, const StorageBase &second)
178 { return first.m_storageData == second.m_storageData; }
179
180 friend bool operator!=(const StorageBase &first, const StorageBase &second)
181 { return first.m_storageData != second.m_storageData; }
182
183 friend size_t qHash(const StorageBase &storage, uint seed = 0)
184 { return size_t(storage.m_storageData.get()) ^ seed; }
185
186 std::shared_ptr<StorageData> m_storageData;
187
188 template <typename StorageStruct> friend class Storage;
190 friend class StorageData;
191 friend class RuntimeContainer;
192 friend class TaskTree;
193 friend class TaskTreePrivate;
194};
195
196template <typename StorageStruct>
197class Storage final : public StorageBase
198{
199public:
200 Storage() : StorageBase(Storage::ctor(), Storage::dtor()) {}
201#if __cplusplus >= 201803L // C++20: Allow pack expansion in lambda init-capture.
202 template <typename ...Args>
203 Storage(const Args &...args)
204 : StorageBase([...args = args] { return new StorageStruct(args...); }, Storage::dtor()) {}
205#else // C++17
206 template <typename ...Args>
207 Storage(const Args &...args)
208 : StorageBase([argsTuple = std::tuple(args...)] {
209 return std::apply([](const Args &...arguments) { return new StorageStruct(arguments...); }, argsTuple);
210 }, Storage::dtor()) {}
211#endif
212 StorageStruct &operator*() const noexcept { return *activeStorage(); }
213 StorageStruct *operator->() const noexcept { return activeStorage(); }
214 StorageStruct *activeStorage() const {
215 return static_cast<StorageStruct *>(activeStorageVoid());
216 }
217
218private:
219 static StorageConstructor ctor() { return [] { return new StorageStruct(); }; }
220 static StorageDestructor dtor() {
221 return [](void *storage) { delete static_cast<StorageStruct *>(storage); };
222 }
223};
224
226{
227public:
228 // Called when group entered, after group's storages are created
230 // Called when group done, before group's storages are deleted
232
233 template <typename StorageStruct>
234 GroupItem(const Storage<StorageStruct> &storage)
235 : m_type(Type::Storage)
237
238 // TODO: Add tests.
239 GroupItem(const GroupItems &children) : m_type(Type::List) { addChildren(children); }
240 GroupItem(std::initializer_list<GroupItem> children) : m_type(Type::List) { addChildren(children); }
241
242protected:
243 GroupItem(const Loop &loop) : GroupItem(GroupData{{}, {}, {}, loop}) {}
244 // Internal, provided by CustomTask
246 // Called prior to task start, just after createHandler
248 // Called on task done, just before deleteLater
250
257
263
270
278
279 GroupItem() = default;
280 GroupItem(Type type) : m_type(type) { }
281 GroupItem(const GroupData &data)
282 : m_type(Type::GroupData)
283 , m_groupData(data) {}
284 GroupItem(const TaskHandler &handler)
285 : m_type(Type::TaskHandler)
287 void addChildren(const GroupItems &children);
288
289 static GroupItem groupHandler(const GroupHandler &handler) { return GroupItem({handler}); }
290
291 // Checks if Function may be invoked with Args and if Function's return type is Result.
292 template <typename Result, typename Function, typename ...Args,
293 typename DecayedFunction = std::decay_t<Function>>
294 static constexpr bool isInvocable()
295 {
296 // Note, that std::is_invocable_r_v doesn't check Result type properly.
297 if constexpr (std::is_invocable_r_v<Result, DecayedFunction, Args...>)
298 return std::is_same_v<Result, std::invoke_result_t<DecayedFunction, Args...>>;
299 return false;
300 }
301
302private:
303 TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
304 friend class ContainerNode;
305 friend class TaskNode;
306 friend class TaskTreePrivate;
307 friend class ParallelLimitFunctor;
308 friend class WorkflowPolicyFunctor;
309 Type m_type = Type::Group;
310 GroupItems m_children;
311 GroupData m_groupData;
312 QList<StorageBase> m_storageList;
313 TaskHandler m_taskHandler;
314};
315
317{
318public:
319 Group withTimeout(std::chrono::milliseconds timeout,
320 const std::function<void()> &handler = {}) const;
321 Group withLog(const QString &logName) const;
322 template <typename SenderSignalPairGetter>
323 Group withCancel(SenderSignalPairGetter &&getter, std::initializer_list<GroupItem> postCancelRecipe = {}) const;
324 template <typename SenderSignalPairGetter>
325 Group withAccept(SenderSignalPairGetter &&getter) const;
326
327protected:
328 ExecutableItem() = default;
329 ExecutableItem(const TaskHandler &handler) : GroupItem(handler) {}
330
331private:
332 TASKING_EXPORT friend Group operator!(const ExecutableItem &item);
333 TASKING_EXPORT friend Group operator&&(const ExecutableItem &first,
334 const ExecutableItem &second);
335 TASKING_EXPORT friend Group operator||(const ExecutableItem &first,
336 const ExecutableItem &second);
337 TASKING_EXPORT friend Group operator&&(const ExecutableItem &item, DoneResult result);
338 TASKING_EXPORT friend Group operator||(const ExecutableItem &item, DoneResult result);
339
340 Group withCancelImpl(
341 const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper,
342 const GroupItems &postCancelRecipe) const;
343 Group withAcceptImpl(
344 const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper) const;
345};
346
348{
349public:
350 Group(const GroupItems &children) { addChildren(children); }
351 Group(std::initializer_list<GroupItem> children) { addChildren(children); }
352
353 // GroupData related:
354 template <typename Handler>
355 static GroupItem onGroupSetup(Handler &&handler) {
356 return groupHandler({wrapGroupSetup(std::forward<Handler>(handler))});
357 }
358 template <typename Handler>
359 static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDoneIf::SuccessOrError) {
360 return groupHandler({{}, wrapGroupDone(std::forward<Handler>(handler)), callDoneIf});
361 }
362
363private:
364 template <typename Handler>
365 static GroupSetupHandler wrapGroupSetup(Handler &&handler)
366 {
367 // R, V stands for: Setup[R]esult, [V]oid
368 static constexpr bool isR = isInvocable<SetupResult, Handler>();
369 static constexpr bool isV = isInvocable<void, Handler>();
370 static_assert(isR || isV,
371 "Group setup handler needs to take no arguments and has to return void or SetupResult. "
372 "The passed handler doesn't fulfill these requirements.");
373 return [handler = std::move(handler)] {
374 if constexpr (isR)
375 return std::invoke(handler);
376 std::invoke(handler);
378 };
379 }
380 template <typename Handler>
381 static GroupDoneHandler wrapGroupDone(Handler &&handler)
382 {
383 static constexpr bool isDoneResultType = std::is_same_v<std::decay_t<Handler>, DoneResult>;
384 // R, B, V, D stands for: Done[R]esult, [B]ool, [V]oid, [D]oneWith
385 static constexpr bool isRD = isInvocable<DoneResult, Handler, DoneWith>();
386 static constexpr bool isR = isInvocable<DoneResult, Handler>();
387 static constexpr bool isBD = isInvocable<bool, Handler, DoneWith>();
388 static constexpr bool isB = isInvocable<bool, Handler>();
389 static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
390 static constexpr bool isV = isInvocable<void, Handler>();
391 static_assert(isDoneResultType || isRD || isR || isBD || isB || isVD || isV,
392 "Group done handler needs to take (DoneWith) or (void) as an argument and has to "
393 "return void, bool or DoneResult. Alternatively, it may be of DoneResult type. "
394 "The passed handler doesn't fulfill these requirements.");
395 return [handler = std::move(handler)](DoneWith result) {
396 if constexpr (isDoneResultType)
397 return handler;
398 if constexpr (isRD)
399 return std::invoke(handler, result);
400 if constexpr (isR)
401 return std::invoke(handler);
402 if constexpr (isBD)
403 return toDoneResult(std::invoke(handler, result));
404 if constexpr (isB)
405 return toDoneResult(std::invoke(handler));
406 if constexpr (isVD)
407 std::invoke(handler, result);
408 else if constexpr (isV)
409 std::invoke(handler);
410 return toDoneResult(result == DoneWith::Success);
411 };
412 }
413};
414
415template <typename SenderSignalPairGetter>
416Group ExecutableItem::withCancel(SenderSignalPairGetter &&getter,
417 std::initializer_list<GroupItem> postCancelRecipe) const
418{
419 const auto connectWrapper = [getter](QObject *guard, const std::function<void()> &trigger) {
420 const auto senderSignalPair = getter();
421 QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
422 trigger();
423 }, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
424 };
425 return withCancelImpl(connectWrapper, postCancelRecipe);
426}
427
428template <typename SenderSignalPairGetter>
429Group ExecutableItem::withAccept(SenderSignalPairGetter &&getter) const
430{
431 const auto connectWrapper = [getter](QObject *guard, const std::function<void()> &trigger) {
432 const auto senderSignalPair = getter();
433 QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
434 trigger();
435 }, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
436 };
437 return withAcceptImpl(connectWrapper);
438}
439
440template <typename Handler>
441static GroupItem onGroupSetup(Handler &&handler)
442{
443 return Group::onGroupSetup(std::forward<Handler>(handler));
444}
445
446template <typename Handler>
447static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDoneIf::SuccessOrError)
448{
449 return Group::onGroupDone(std::forward<Handler>(handler), callDoneIf);
450}
451
452// Default: 1 (sequential). 0 means unlimited (parallel).
454
455// Default: WorkflowPolicy::StopOnError.
457
461
469
473
474class TASKING_EXPORT For final
475{
476public:
477 explicit For(const Loop &loop) : m_loop(loop) {}
478
479private:
480 TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
481
482 Loop m_loop;
483};
484
485class When;
486
487class TASKING_EXPORT Do final
488{
489public:
490 explicit Do(const GroupItems &children) : m_children(children) {}
491 explicit Do(std::initializer_list<GroupItem> children) : m_children(children) {}
492
493private:
494 TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
495 TASKING_EXPORT friend Group operator>>(const When &whenItem, const Do &doItem);
496
497 GroupItem m_children;
498};
499
500class TASKING_EXPORT Forever final : public ExecutableItem
501{
502public:
503 explicit Forever(const GroupItems &children)
504 { addChildren({ For (LoopForever()) >> Do { children } } ); }
505 explicit Forever(std::initializer_list<GroupItem> children)
506 { addChildren({ For (LoopForever()) >> Do { children } } ); }
507};
508
509// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()
510class TASKING_EXPORT Sync final : public ExecutableItem
511{
512public:
513 template <typename Handler>
514 Sync(Handler &&handler) {
515 addChildren({ onGroupDone(wrapHandler(std::forward<Handler>(handler))) });
516 }
517
518private:
519 template <typename Handler>
520 static auto wrapHandler(Handler &&handler) {
521 // R, B, V stands for: Done[R]esult, [B]ool, [V]oid
522 static constexpr bool isR = isInvocable<DoneResult, Handler>();
523 static constexpr bool isB = isInvocable<bool, Handler>();
524 static constexpr bool isV = isInvocable<void, Handler>();
525 static_assert(isR || isB || isV,
526 "Sync handler needs to take no arguments and has to return void, bool or DoneResult. "
527 "The passed handler doesn't fulfill these requirements.");
528 return handler;
529 }
530};
531
532template <typename Task, typename Deleter = std::default_delete<Task>>
534{
535protected:
536 TaskAdapter() : m_task(new Task) {}
537 Task *task() { return m_task.get(); }
538 const Task *task() const { return m_task.get(); }
539
540private:
541 using TaskType = Task;
542 using DeleterType = Deleter;
543 template <typename Adapter> friend class CustomTask;
544 std::unique_ptr<Task, Deleter> m_task;
545};
546
547template <typename Adapter>
548class CustomTask final : public ExecutableItem
549{
550public:
551 using Task = typename Adapter::TaskType;
552 using Deleter = typename Adapter::DeleterType;
553 static_assert(std::is_base_of_v<TaskAdapter<Task, Deleter>, Adapter>,
554 "The Adapter type for the CustomTask<Adapter> needs to be derived from "
555 "TaskAdapter<Task>.");
558
559 template <typename SetupHandler = TaskSetupHandler, typename DoneHandler = TaskDoneHandler>
560 CustomTask(SetupHandler &&setup = TaskSetupHandler(), DoneHandler &&done = TaskDoneHandler(),
562 : ExecutableItem({&createAdapter, wrapSetup(std::forward<SetupHandler>(setup)),
563 wrapDone(std::forward<DoneHandler>(done)), callDoneIf})
564 {}
565
566private:
567 static Adapter *createAdapter() { return new Adapter; }
568
569 template <typename Handler>
570 static InterfaceSetupHandler wrapSetup(Handler &&handler) {
571 if constexpr (std::is_same_v<Handler, TaskSetupHandler>)
572 return {}; // When user passed {} for the setup handler.
573 // R, V stands for: Setup[R]esult, [V]oid
574 static constexpr bool isR = isInvocable<SetupResult, Handler, Task &>();
575 static constexpr bool isV = isInvocable<void, Handler, Task &>();
576 static_assert(isR || isV,
577 "Task setup handler needs to take (Task &) as an argument and has to return void or "
578 "SetupResult. The passed handler doesn't fulfill these requirements.");
579 return [handler = std::move(handler)](TaskInterface &taskInterface) {
580 Adapter &adapter = static_cast<Adapter &>(taskInterface);
581 if constexpr (isR)
582 return std::invoke(handler, *adapter.task());
583 std::invoke(handler, *adapter.task());
585 };
586 }
587
588 template <typename Handler>
589 static InterfaceDoneHandler wrapDone(Handler &&handler) {
590 if constexpr (std::is_same_v<Handler, TaskDoneHandler>)
591 return {}; // User passed {} for the done handler.
592 static constexpr bool isDoneResultType = std::is_same_v<std::decay_t<Handler>, DoneResult>;
593 // R, B, V, T, D stands for: Done[R]esult, [B]ool, [V]oid, [T]ask, [D]oneWith
594 static constexpr bool isRTD = isInvocable<DoneResult, Handler, const Task &, DoneWith>();
595 static constexpr bool isRT = isInvocable<DoneResult, Handler, const Task &>();
596 static constexpr bool isRD = isInvocable<DoneResult, Handler, DoneWith>();
597 static constexpr bool isR = isInvocable<DoneResult, Handler>();
598 static constexpr bool isBTD = isInvocable<bool, Handler, const Task &, DoneWith>();
599 static constexpr bool isBT = isInvocable<bool, Handler, const Task &>();
600 static constexpr bool isBD = isInvocable<bool, Handler, DoneWith>();
601 static constexpr bool isB = isInvocable<bool, Handler>();
602 static constexpr bool isVTD = isInvocable<void, Handler, const Task &, DoneWith>();
603 static constexpr bool isVT = isInvocable<void, Handler, const Task &>();
604 static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
605 static constexpr bool isV = isInvocable<void, Handler>();
606 static_assert(isDoneResultType || isRTD || isRT || isRD || isR
607 || isBTD || isBT || isBD || isB
608 || isVTD || isVT || isVD || isV,
609 "Task done handler needs to take (const Task &, DoneWith), (const Task &), "
610 "(DoneWith) or (void) as arguments and has to return void, bool or DoneResult. "
611 "Alternatively, it may be of DoneResult type. "
612 "The passed handler doesn't fulfill these requirements.");
613 return [handler = std::move(handler)](const TaskInterface &taskInterface, DoneWith result) {
614 if constexpr (isDoneResultType)
615 return handler;
616 const Adapter &adapter = static_cast<const Adapter &>(taskInterface);
617 if constexpr (isRTD)
618 return std::invoke(handler, *adapter.task(), result);
619 if constexpr (isRT)
620 return std::invoke(handler, *adapter.task());
621 if constexpr (isRD)
622 return std::invoke(handler, result);
623 if constexpr (isR)
624 return std::invoke(handler);
625 if constexpr (isBTD)
626 return toDoneResult(std::invoke(handler, *adapter.task(), result));
627 if constexpr (isBT)
628 return toDoneResult(std::invoke(handler, *adapter.task()));
629 if constexpr (isBD)
630 return toDoneResult(std::invoke(handler, result));
631 if constexpr (isB)
632 return toDoneResult(std::invoke(handler));
633 if constexpr (isVTD)
634 std::invoke(handler, *adapter.task(), result);
635 else if constexpr (isVT)
636 std::invoke(handler, *adapter.task());
637 else if constexpr (isVD)
638 std::invoke(handler, result);
639 else if constexpr (isV)
640 std::invoke(handler);
641 return toDoneResult(result == DoneWith::Success);
642 };
643 }
644};
645
646template <typename Task>
647class SimpleTaskAdapter final : public TaskAdapter<Task>
648{
649public:
650 SimpleTaskAdapter() { this->connect(this->task(), &Task::done, this, &TaskInterface::done); }
651 void start() final { this->task()->start(); }
652};
653
654// A convenient helper, when:
655// 1. Task is derived from QObject.
656// 2. Task::start() method starts the task.
657// 3. Task::done(DoneResult) signal is emitted when the task is finished.
658template <typename Task>
659using SimpleCustomTask = CustomTask<SimpleTaskAdapter<Task>>;
660
661class TASKING_EXPORT TaskTree final : public QObject
662{
664
665public:
666 TaskTree();
667 TaskTree(const Group &recipe);
668 ~TaskTree();
669
670 void setRecipe(const Group &recipe);
671
672 void start();
673 void cancel();
674 bool isRunning() const;
675
676 // Helper methods. They execute a local event loop with ExcludeUserInputEvents.
677 // The passed future is used for listening to the cancel event.
678 // Don't use it in main thread. To be used in non-main threads or in auto tests.
680 DoneWith runBlocking(const QFuture<void> &future);
681 static DoneWith runBlocking(const Group &recipe);
682 static DoneWith runBlocking(const Group &recipe, const QFuture<void> &future);
683
684 int asyncCount() const;
685 int taskCount() const;
686 int progressMaximum() const { return taskCount(); }
687 int progressValue() const; // all finished / skipped / stopped tasks, groups itself excluded
688
689 template <typename StorageStruct, typename Handler>
690 void onStorageSetup(const Storage<StorageStruct> &storage, Handler &&handler) {
691 static_assert(std::is_invocable_v<std::decay_t<Handler>, StorageStruct &>,
692 "Storage setup handler needs to take (Storage &) as an argument. "
693 "The passed handler doesn't fulfill this requirement.");
694 setupStorageHandler(storage,
695 wrapHandler<StorageStruct>(std::forward<Handler>(handler)), {});
696 }
697 template <typename StorageStruct, typename Handler>
698 void onStorageDone(const Storage<StorageStruct> &storage, Handler &&handler) {
699 static_assert(std::is_invocable_v<std::decay_t<Handler>, const StorageStruct &>,
700 "Storage done handler needs to take (const Storage &) as an argument. "
701 "The passed handler doesn't fulfill this requirement.");
702 setupStorageHandler(storage, {},
703 wrapHandler<const StorageStruct>(std::forward<Handler>(handler)));
704 }
705
707 void started();
708 void done(DoneWith result);
709 void asyncCountChanged(int count);
710 void progressValueChanged(int value); // updated whenever task finished / skipped / stopped
711
712private:
713 void setupStorageHandler(const StorageBase &storage,
714 const StorageBase::StorageHandler &setupHandler,
715 const StorageBase::StorageHandler &doneHandler);
716 template <typename StorageStruct, typename Handler>
717 StorageBase::StorageHandler wrapHandler(Handler &&handler) {
718 return [handler](void *voidStruct) {
719 auto *storageStruct = static_cast<StorageStruct *>(voidStruct);
720 std::invoke(handler, *storageStruct);
721 };
722 }
723
725};
726
728{
729public:
731
732private:
733 void start() final;
734};
735
737{
738public:
741
742private:
743 void start() final;
744 std::optional<int> m_timerId;
745};
746
749
750TASKING_EXPORT ExecutableItem timeoutTask(const std::chrono::milliseconds &timeout,
752
753} // namespace Tasking
754
755QT_END_NAMESPACE
756
757#endif // TASKING_TASKTREE_H
void setupDownload(NetworkQuery *query, const QString &progressText)
void setProgress(int progressValue, int progressMaximum, const QString &progressText)
void clearProgress(const QString &progressText)
void updateProgress(int progressValue, int progressMaximum)
std::unique_ptr< QNetworkAccessManager > m_manager
std::unique_ptr< QTemporaryDir > m_temporaryDir
void progressChanged(int progressValue, int progressMaximum, const QString &progressText)
void localDownloadDirChanged(const QUrl &url)
void setOfflineAssetsFilePath(const QUrl &offlineAssetsFilePath)
virtual QUrl resolvedUrl(const QUrl &url) const
void setJsonFileName(const QString &jsonFileName)
void jsonFileNameChanged(const QString &)
void downloadBaseChanged(const QUrl &)
void setPreferredLocalDownloadDir(const QUrl &localDir)
void offlineAssetsFilePathChanged(const QUrl &)
void setDownloadBase(const QUrl &downloadBase)
void setZipFileName(const QString &zipFileName)
void zipFileNameChanged(const QString &)
void preferredLocalDownloadDirChanged(const QUrl &url)
void start() final
This method is called by the running TaskTree for starting the Task instance.
void setThreadPool(QThreadPool *pool)
QFuture< ResultType > future() const
QList< ResultType > results() const
ResultType result() const
void setConcurrentCallData(Function &&function, Args &&...args)
typename Adapter::TaskType Task
Definition tasktree.h:551
typename Adapter::DeleterType Deleter
Definition tasktree.h:552
std::function< SetupResult(Task &)> TaskSetupHandler
Definition tasktree.h:556
CustomTask(SetupHandler &&setup=TaskSetupHandler(), DoneHandler &&done=TaskDoneHandler(), CallDoneIf callDoneIf=CallDoneIf::SuccessOrError)
\typealias Tasking::CustomTask::Task
Definition tasktree.h:560
Do(std::initializer_list< GroupItem > children)
Definition tasktree.h:491
Do(const GroupItems &children)
Definition tasktree.h:490
\inheaderfile solutions/tasking/tasktree.h \inmodule TaskingSolution
Definition tasktree.h:317
Group withCancel(SenderSignalPairGetter &&getter, std::initializer_list< GroupItem > postCancelRecipe={}) const
Definition tasktree.h:416
ExecutableItem(const TaskHandler &handler)
Definition tasktree.h:329
Group withLog(const QString &logName) const
Attaches a custom debug printout to a copy of this ExecutableItem, issued on task startup and after t...
Group withTimeout(std::chrono::milliseconds timeout, const std::function< void()> &handler={}) const
Attaches TimeoutTask to a copy of this ExecutableItem, elapsing after timeout in milliseconds,...
Group withAccept(SenderSignalPairGetter &&getter) const
Definition tasktree.h:429
For(const Loop &loop)
Definition tasktree.h:477
Forever(const GroupItems &children)
Definition tasktree.h:503
Forever(std::initializer_list< GroupItem > children)
Definition tasktree.h:505
\typealias Tasking::GroupItems
Definition tasktree.h:226
GroupItem(const Loop &loop)
Definition tasktree.h:243
GroupItem(std::initializer_list< GroupItem > children)
Definition tasktree.h:240
GroupItem(const TaskHandler &handler)
Definition tasktree.h:284
GroupItem(const GroupData &data)
Definition tasktree.h:281
GroupItem(Type type)
Definition tasktree.h:280
GroupItem(const GroupItems &children)
Constructs a GroupItem element with a given list of items.
Definition tasktree.h:239
GroupItem(const Storage< StorageStruct > &storage)
Constructs a GroupItem element holding the storage object.
Definition tasktree.h:234
static GroupItem groupHandler(const GroupHandler &handler)
Definition tasktree.h:289
void addChildren(const GroupItems &children)
static constexpr bool isInvocable()
Definition tasktree.h:294
\inheaderfile solutions/tasking/tasktree.h \inmodule TaskingSolution
Definition tasktree.h:348
static GroupItem onGroupSetup(Handler &&handler)
Definition tasktree.h:355
static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf=CallDoneIf::SuccessOrError)
Definition tasktree.h:359
Group(const GroupItems &children)
Constructs a group with a given list of children.
Definition tasktree.h:350
Group(std::initializer_list< GroupItem > children)
Constructs a group from std::initializer_list given by children.
Definition tasktree.h:351
const T & operator*() const
Definition tasktree.h:163
LoopList(const QList< T > &list)
Definition tasktree.h:161
const T * operator->() const
Definition tasktree.h:162
LoopRepeat(int count)
Definition tasktree.h:148
LoopUntil(const Condition &condition)
Definition tasktree.h:154
Loop(int count, const ValueGetter &valueGetter={})
Loop(const Condition &condition)
int iteration() const
const void * valuePtr() const
void start() final
This method is called by the running TaskTree for starting the Task instance.
Definition tasktree.h:651
friend bool operator==(const StorageBase &first, const StorageBase &second)
Definition tasktree.h:177
friend size_t qHash(const StorageBase &storage, uint seed=0)
Definition tasktree.h:183
friend class Storage
Definition tasktree.h:188
friend bool operator!=(const StorageBase &first, const StorageBase &second)
Definition tasktree.h:180
StorageStruct * operator->() const noexcept
Returns a pointer to the active StorageStruct object, created by the running task tree.
Definition tasktree.h:213
StorageStruct & operator*() const noexcept
Returns a reference to the active StorageStruct object, created by the running task tree.
Definition tasktree.h:212
Storage()
Creates a storage for the given StorageStruct type.
Definition tasktree.h:200
StorageStruct * activeStorage() const
Returns a pointer to the active StorageStruct object, created by the running task tree.
Definition tasktree.h:214
Storage(const Args &...args)
Definition tasktree.h:207
Sync(Handler &&handler)
Constructs an element that executes a passed handler synchronously.
Definition tasktree.h:514
\inheaderfile solutions/tasking/tasktree.h \inmodule TaskingSolution
Definition tasktree.h:534
const Task * task() const
Definition tasktree.h:538
\inheaderfile solutions/tasking/tasktree.h \inmodule TaskingSolution
Definition tasktree.h:102
virtual void start()=0
This method is called by the running TaskTree for starting the Task instance.
void start() final
This method is called by the running TaskTree for starting the Task instance.
void done(DoneWith result)
This signal is emitted when the task tree finished, passing the final result of the execution.
DoneWith runBlocking(const QFuture< void > &future)
int progressMaximum() const
Returns the maximum progressValue().
Definition tasktree.h:686
TaskTree()
Constructs an empty task tree.
int taskCount() const
Returns the number of asynchronous tasks contained in the stored recipe.
void cancel()
Cancels the execution of the running task tree.
void start()
Starts the task tree.
void onStorageSetup(const Storage< StorageStruct > &storage, Handler &&handler)
Installs a storage setup handler for the storage to pass the initial data dynamically to the running ...
Definition tasktree.h:690
~TaskTree()
Destroys the task tree.
bool isRunning() const
Returns true if the task tree is currently running; otherwise returns false.
void onStorageDone(const Storage< StorageStruct > &storage, Handler &&handler)
Installs a storage done handler for the storage to retrieve the final data dynamically from the runni...
Definition tasktree.h:698
void setRecipe(const Group &recipe)
Sets a given recipe for the task tree.
void asyncCountChanged(int count)
This signal is emitted when the running task tree is about to return control to the caller's event lo...
TaskTree(const Group &recipe)
This is an overloaded member function, provided for convenience. It differs from the above function o...
int progressValue() const
Returns the current progress value, which is between the 0 and progressMaximum().
DoneWith runBlocking()
Executes a local event loop with QEventLoop::ExcludeUserInputEvents and starts the task tree.
static DoneWith runBlocking(const Group &recipe)
Constructs a temporary task tree using the passed recipe and runs it blocking.
void progressValueChanged(int value)
This signal is emitted when the running task tree finished, canceled, or skipped some tasks.
int asyncCount() const
Returns the current real count of asynchronous chains of invocations.
static DoneWith runBlocking(const Group &recipe, const QFuture< void > &future)
void start() final
This method is called by the running TaskTree for starting the Task instance.
TASKING_EXPORT friend Group operator>>(const When &whenItem, const Do &doItem)
static void unzip(QPromise< void > &promise, const QByteArray &content, const QDir &directory, const QString &fileName)
static bool sameFileContent(const QFileInfo &first, const QFileInfo &second)
static void writeAsset(QPromise< void > &promise, const QByteArray &content, const QString &filePath)
static void precheckLocalFile(const QUrl &url)
static QDir baseLocalDir(const QDir &preferredLocalDir)
static QString pathFromUrl(const QUrl &url)
static bool allAssetsPresent(const QList< QUrl > &assetFiles, const QDir &expectedDir)
static void copyAndCheck(QPromise< void > &promise, const QString &sourcePath, const QString &destPath)
static bool canBeALocalBaseDir(const QDir &dir)
static QList< QUrl > filterDownloadableAssets(const QList< QUrl > &assetFiles, const QDir &expectedDir)
static void readAssetsFileContent(QPromise< DownloadableAssets > &promise, const QByteArray &content)
static bool createDirectory(const QDir &dir)
static bool isWritableDir(const QDir &dir)
\inmodule TaskingSolution
const ExecutableItem successItem
SetupResult
Definition tasktree.h:65
WorkflowPolicy
Definition tasktree.h:53
const GroupItem sequential
GroupItem parallelLimit(int limit)
Constructs a group's element describing the \l{Execution Mode}{execution mode}.
Group operator||(const ExecutableItem &first, const ExecutableItem &second)
CustomTask< ConcurrentCallTaskAdapter< T > > ConcurrentCallTask
const GroupItem continueOnSuccess
const GroupItem stopOnError
const GroupItem finishAllAndError
const ExecutableItem errorItem
CustomTask< SimpleTaskAdapter< Task > > SimpleCustomTask
Definition tasktree.h:659
CustomTask< TimeoutTaskAdapter > TimeoutTask
Definition tasktree.h:748
const GroupItem finishAllAndSuccess
Group operator||(const ExecutableItem &item, DoneResult result)
DoneResult toDoneResult(bool success)
const GroupItem parallelIdealThreadCountLimit
ExecutableItem timeoutTask(const std::chrono::milliseconds &timeout, DoneResult result)
CustomTask< TaskTreeTaskAdapter > TaskTreeTask
Definition tasktree.h:747
const GroupItem stopOnSuccessOrError
const GroupItem nullItem
Group operator&&(const ExecutableItem &item, DoneResult result)
const GroupItem parallel
Group operator&&(const ExecutableItem &first, const ExecutableItem &second)
static GroupItem onGroupSetup(Handler &&handler)
\typealias Tasking::GroupItem::GroupSetupHandler
Definition tasktree.h:441
Group operator>>(const For &forItem, const Do &doItem)
Group operator!(const ExecutableItem &item)
const GroupItem continueOnError
GroupItem workflowPolicy(WorkflowPolicy policy)
Constructs a group's \l {Workflow Policy} {workflow policy} element for a given policy.
const GroupItem stopOnSuccess
static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf=CallDoneIf::SuccessOrError)
Constructs a group's element holding the group done handler.
Definition tasktree.h:447
std::optional< WorkflowPolicy > m_workflowPolicy
Definition tasktree.h:267
std::optional< int > m_parallelLimit
Definition tasktree.h:266
std::optional< Loop > m_loop
Definition tasktree.h:268
GroupDoneHandler m_doneHandler
Definition tasktree.h:260
GroupSetupHandler m_setupHandler
Definition tasktree.h:259
InterfaceSetupHandler m_setupHandler
Definition tasktree.h:253
InterfaceCreateHandler m_createHandler
Definition tasktree.h:252
InterfaceDoneHandler m_doneHandler
Definition tasktree.h:254
#define TASKING_EXPORT