Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
task.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/assert.hpp>
5
6#include <taskflow/core/task.hpp>
7
8#include <concepts>
9#include <cstddef>
10#include <ranges>
11#include <string>
12#include <string_view>
13#include <type_traits>
14
15namespace helios::async {
16
17/**
18 * @brief Represents a single task within a task graph.
19 * @details Task objects are lightweight handles - copying them is safe and
20 * efficient. Wraps `tf::Task` and provides methods to manage task dependencies,
21 * work assignment, and metadata. Tasks can be static (simple callable) or
22 * dynamic (subtasks). The underlying task data is managed by the` TaskGraph`.
23 * @note Not thread-safe.
24 */
25class Task {
26public:
27 Task() = default;
28 Task(const Task&) = default;
29 Task(Task&&) = default;
30 ~Task() = default;
31
32 Task& operator=(const Task&) = default;
33 Task& operator=(Task&&) = default;
34
35 /**
36 * @brief Resets the task handle to an empty state.
37 * @note Not thread-safe.
38 */
39 void Reset() { task_.reset(); }
40
41 /**
42 * @brief Removes the work callable from this task.
43 * @note Not thread-safe.
44 */
45 void ResetWork() { task_.reset_work(); }
46
47 /**
48 * @brief Assigns work to this task.
49 * @warning Triggers assertion if current task is empty.
50 * @tparam C Callable type
51 * @param callable Function to execute when this task runs
52 * @return Reference to this task for method chaining
53 */
54 template <AnyTask C>
55 Task& Work(C&& callable);
56
57 /**
58 * @brief Makes this task run before the specified tasks.
59 * @warning Triggers assertion if current task is empty.
60 * @tparam Tasks Task parameter pack
61 * @param tasks Tasks that should run after this task
62 * @return Reference to this task for method chaining
63 */
64 template <typename... Tasks>
65 requires(std::same_as<std::remove_cvref_t<Tasks>, Task> && ...)
66 Task& Precede(Tasks&&... tasks);
67
68 /**
69 * @brief Makes this task run before all tasks in the specified range.
70 * @warning Triggers assertion in next cases:
71 * - Current task is empty.
72 * - Any task in the range is empty.
73 *
74 * @tparam R Range type containing Task objects
75 * @param tasks Range of tasks that should run after this task
76 * @return Reference to this task for method chaining
77 */
78 template <std::ranges::range R>
79 requires std::same_as<std::ranges::range_value_t<R>, Task>
80 Task& Precede(const R& tasks);
81
82 /**
83 * @brief Makes this task run after the specified tasks.
84 * @warning Triggers assertion in next cases:
85 * - Current task is empty.
86 * - Any task in the parameter pack is empty.
87 *
88 * @tparam Tasks Task parameter pack
89 * @param tasks Tasks that should run before this task
90 * @return Reference to this task for method chaining
91 */
92 template <typename... Tasks>
93 requires(std::same_as<std::remove_cvref_t<Tasks>, Task> && ...)
94 Task& Succeed(Tasks&&... tasks);
95
96 /**
97 * @brief Makes this task run after all tasks in the specified range.
98 * @warning Triggers assertion in next cases:
99 * - Current task is empty.
100 * - Any task in the range is empty.
101 *
102 * @tparam R Range type containing Task objects
103 * @param tasks Range of tasks that should run before this task
104 * @return Reference to this task for method chaining
105 */
106 template <std::ranges::range R>
107 requires std::same_as<std::ranges::range_value_t<R>, Task>
108 Task& Succeed(const R& tasks);
109
110 /**
111 * @brief Assigns a name to this task.
112 * @warning Triggers assertion if current task is empty.
113 * @param name Name for the task (must not be empty)
114 * @return Reference to this task for method chaining
115 */
116 Task& Name(const std::string& name);
117
118 [[nodiscard]] bool operator==(const Task& other) const {
119 return task_ == other.task_;
120 }
121
122 [[nodiscard]] bool operator!=(const Task& other) const {
123 return !(*this == other);
124 }
125
126 /**
127 * @brief Checks if this task has assigned work.
128 * @return True if task has work, false otherwise.
129 */
130 [[nodiscard]] bool HasWork() const { return task_.has_work(); }
131
132 /**
133 * @brief Checks if this task handle is empty (not associated with any task).
134 * @return True if task id empty, false otherwise.
135 */
136 [[nodiscard]] bool Empty() const { return task_.empty(); }
137
138 /**
139 * @brief Returns a hash value for this task.
140 * @return Hash of task or 0 if current task is empty.
141 */
142 [[nodiscard]] size_t Hash() const;
143
144 /**
145 * @brief Gets the number of tasks that depend on this task.
146 * @return Count of successors or '0' if current task is empty.
147 */
148 [[nodiscard]] size_t SuccessorsCount() const;
149
150 /**
151 * @brief Gets the number of tasks this task depends on.
152 * @return Count of predecessors or 0 if current task is empty.
153 */
154 [[nodiscard]] size_t PredecessorsCount() const;
155
156 /**
157 * @brief Gets the number of strong dependencies this task has.
158 * @return Count of strong dependencies or 0 if current task is empty.
159 */
160 [[nodiscard]] size_t StrongDependenciesCount() const;
161
162 /**
163 * @brief Gets the number of weak dependencies this task has.
164 * @return Count of weak dependencies or 0 if current task is empty.
165 */
166 [[nodiscard]] size_t WeakDependenciesCount() const;
167
168 /**
169 * @brief Gets the name of this task.
170 * @return Returns empty `std::string_view` if current task is empty.
171 */
172 [[nodiscard]] std::string_view Name() const;
173
174 /**
175 * @brief Gets the type of this task.
176 * @details Returns `TaskType::Undefined` if current task is empty.
177 */
178 [[nodiscard]] TaskType Type() const;
179
180private:
181 explicit Task(const tf::Task& task) : task_(task) {}
182
183 [[nodiscard]] tf::Task& UnderlyingTask() noexcept { return task_; }
184 [[nodiscard]] const tf::Task& UnderlyingTask() const noexcept {
185 return task_;
186 }
187
188 tf::Task task_;
189
190 friend class TaskGraph;
191 friend class SubTaskGraph;
192 friend class TaskGraphBuilder;
193 friend class Executor;
194};
195
196template <AnyTask C>
197inline Task& Task::Work(C&& callable) {
198 HELIOS_ASSERT(!Empty(), "Cannot assign work to empty task!");
199 task_.work(std::forward<C>(callable));
200 return *this;
201}
202
203template <typename... Tasks>
204 requires(std::same_as<std::remove_cvref_t<Tasks>, Task> && ...)
205inline Task& Task::Precede(Tasks&&... tasks) {
206 HELIOS_ASSERT(!Empty(), "Task cannot be empty!");
207 return Precede(std::to_array({std::forward<Tasks>(tasks)...}));
208}
209
210template <std::ranges::range R>
211 requires std::same_as<std::ranges::range_value_t<R>, Task>
212inline Task& Task::Precede(const R& tasks) {
213 HELIOS_ASSERT(!Empty(), "Task cannot be empty!");
214 auto view = tasks | std::ranges::views::filter(
215 [](const auto& task) { return !task.Empty(); });
216 for (const auto& task : view) {
217 task_.precede(task.task_);
218 }
219 return *this;
220}
221
222template <typename... Tasks>
223 requires(std::same_as<std::remove_cvref_t<Tasks>, Task> && ...)
224inline Task& Task::Succeed(Tasks&&... tasks) {
225 HELIOS_ASSERT(!Empty(), "Task cannot be empty!");
226 return Succeed(std::to_array({std::forward<Tasks>(tasks)...}));
227}
228
229template <std::ranges::range R>
230 requires std::same_as<std::ranges::range_value_t<R>, Task>
231inline Task& Task::Succeed(const R& tasks) {
232 HELIOS_ASSERT(!Empty(), "Task cannot be empty!");
233 auto view = tasks | std::ranges::views::filter(
234 [](const auto& task) { return !task.Empty(); });
235 for (const auto& task : view) {
236 task_.succeed(task.task_);
237 }
238 return *this;
239}
240
241inline Task& Task::Name(const std::string& name) {
242 HELIOS_ASSERT(!Empty(), "Cannot assign name to empty task!");
243 HELIOS_ASSERT(!name.empty(), "name cannot be empty!");
244 task_.name(name);
245 return *this;
246}
247
248inline size_t Task::Hash() const {
249 if (Empty()) [[unlikely]] {
250 return 0;
251 }
252
253 return task_.hash_value();
254}
255
256inline size_t Task::SuccessorsCount() const {
257 if (Empty()) [[unlikely]] {
258 return 0;
259 }
260
261 return task_.num_successors();
262}
263
264inline size_t Task::PredecessorsCount() const {
265 if (Empty()) [[unlikely]] {
266 return 0;
267 }
268
269 return task_.num_predecessors();
270}
271
272inline size_t Task::StrongDependenciesCount() const {
273 if (Empty()) [[unlikely]] {
274 return 0;
275 }
276
277 return task_.num_strong_dependencies();
278}
279
280inline size_t Task::WeakDependenciesCount() const {
281 if (Empty()) [[unlikely]] {
282 return 0;
283 }
284
285 return task_.num_weak_dependencies();
286}
287
288inline std::string_view Task::Name() const {
289 if (Empty()) [[unlikely]] {
290 return {};
291 }
292
293 return task_.name();
294}
295
296inline TaskType Task::Type() const {
297 if (Empty()) [[unlikely]] {
299 }
300
301 return details::ConvertTaskType(task_.type());
302}
303
304} // namespace helios::async
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:259
Represents a single task within a task graph.
Definition task.hpp:25
std::string_view Name() const
Gets the name of this task.
Definition task.hpp:288
size_t Hash() const
Returns a hash value for this task.
Definition task.hpp:248
void ResetWork()
Removes the work callable from this task.
Definition task.hpp:45
size_t WeakDependenciesCount() const
Gets the number of weak dependencies this task has.
Definition task.hpp:280
size_t SuccessorsCount() const
Gets the number of tasks that depend on this task.
Definition task.hpp:256
size_t StrongDependenciesCount() const
Gets the number of strong dependencies this task has.
Definition task.hpp:272
friend class TaskGraphBuilder
Definition task.hpp:192
Task & operator=(const Task &)=default
bool operator!=(const Task &other) const
Definition task.hpp:122
friend class Executor
Definition task.hpp:193
friend class TaskGraph
Definition task.hpp:190
Task & Work(C &&callable)
Assigns work to this task.
Definition task.hpp:197
Task(const Task &)=default
void Reset()
Resets the task handle to an empty state.
Definition task.hpp:39
Task & operator=(Task &&)=default
Task(Task &&)=default
Task & Precede(Tasks &&... tasks)
Makes this task run before the specified tasks.
Definition task.hpp:205
Task & Succeed(Tasks &&... tasks)
Makes this task run after the specified tasks.
Definition task.hpp:224
bool Empty() const
Checks if this task handle is empty (not associated with any task).
Definition task.hpp:136
size_t PredecessorsCount() const
Gets the number of tasks this task depends on.
Definition task.hpp:264
friend class SubTaskGraph
Definition task.hpp:191
bool HasWork() const
Checks if this task has assigned work.
Definition task.hpp:130
TaskType Type() const
Gets the type of this task.
Definition task.hpp:296
static constexpr TaskType ConvertTaskType(tf::TaskType type) noexcept
Converts Taskflow task type to Helios task type.
Definition common.hpp:86
TaskType
Types of tasks in the async system.
Definition common.hpp:18
STL namespace.