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