GCC Code Coverage Report


Directory: ./
File: modules/task/include/task.hpp
Date: 2026-08-18 01:54:56
Exec Total Coverage
Lines: 105 111 94.6%
Functions: 40 41 97.6%
Branches: 112 195 57.4%

Line Branch Exec Source
1 #pragma once
2
3 #include <omp.h>
4
5 #include <array>
6 #include <chrono>
7 #include <cstdint>
8 #include <cstdlib>
9 #include <fstream>
10 #include <iostream>
11 #include <memory>
12 #include <sstream>
13 #include <stdexcept>
14 #include <string>
15 #include <string_view>
16 #include <util/include/util.hpp>
17 #include <utility>
18
19 namespace ppc::task {
20
21 /// @brief Represents the type of task (parallelization technology).
22 /// @details Used to select the implementation type in tests and execution logic.
23 enum class TypeOfTask : uint8_t {
24 /// Use all available implementations
25 kALL,
26 /// MPI (Message Passing Interface)
27 kMPI,
28 /// OpenMP (Open Multi-Processing)
29 kOMP,
30 /// Sequential implementation
31 kSEQ,
32 /// Standard Thread Library (STL threads)
33 kSTL,
34 /// Intel Threading Building Blocks (TBB)
35 kTBB,
36 /// Unknown task type
37 kUnknown,
38 };
39
40 using TaskMapping = std::pair<TypeOfTask, std::string_view>;
41 using TaskMappingArray = std::array<TaskMapping, 6>;
42
43 inline constexpr TaskMappingArray kTaskTypeMappings = {{{TypeOfTask::kALL, "all"},
44 {TypeOfTask::kMPI, "mpi"},
45 {TypeOfTask::kOMP, "omp"},
46 {TypeOfTask::kSEQ, "seq"},
47 {TypeOfTask::kSTL, "stl"},
48 {TypeOfTask::kTBB, "tbb"}}};
49
50 constexpr std::string_view TypeOfTaskToString(TypeOfTask type) {
51
4/6
✓ Branch 0 taken 1466 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 1576 times.
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3082 for (const auto &[key, value] : kTaskTypeMappings) {
52
4/6
✓ Branch 0 taken 398 times.
✓ Branch 1 taken 1068 times.
✓ Branch 2 taken 428 times.
✓ Branch 3 taken 1148 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3042 if (key == type) {
53 826 return value;
54 }
55 }
56 return "unknown";
57 }
58
59 76 constexpr TypeOfTask TypeOfTaskFromString(std::string_view type) {
60
1/2
✓ Branch 0 taken 308 times.
✗ Branch 1 not taken.
308 for (const auto &[key, value] : kTaskTypeMappings) {
61 232 if (value == type) {
62 76 return key;
63 }
64 }
65 return TypeOfTask::kUnknown;
66 }
67
68 /// @brief Indicates whether a task is enabled or disabled.
69 enum class StatusOfTask : uint8_t {
70 /// Task is enabled and should be executed
71 kEnabled,
72 /// Task is disabled and will be skipped
73 kDisabled,
74 };
75
76 constexpr std::string_view StatusOfTaskToString(StatusOfTask status_of_task) {
77
1/2
✓ Branch 0 taken 318 times.
✗ Branch 1 not taken.
398 return status_of_task == StatusOfTask::kDisabled ? "disabled" : "enabled";
78 }
79
80
2/2
✓ Branch 0 taken 388 times.
✓ Branch 1 taken 10 times.
398 inline StatusOfTask StatusOfTaskFromString(std::string_view status_of_task) {
81
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (status_of_task == "enabled") {
82 388 return StatusOfTask::kEnabled;
83 }
84 10 if (status_of_task == "disabled") {
85 10 return StatusOfTask::kDisabled;
86 }
87 throw std::runtime_error("Unknown task status: " + std::string(status_of_task));
88 }
89
90 /// @brief Returns a string representation of the task status.
91 /// @param status_of_task Task status (enabled or disabled).
92 /// @return "enabled" if the task is enabled, otherwise "disabled".
93 inline std::string GetStringTaskStatus(StatusOfTask status_of_task) {
94
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
20 return std::string(StatusOfTaskToString(status_of_task));
95 }
96
97 enum class TaskCategory : uint8_t {
98 kUnknown,
99 kThreads,
100 kProcesses,
101 };
102
103 constexpr std::string_view TaskCategoryToString(TaskCategory category) {
104 switch (category) {
105 case TaskCategory::kThreads:
106 return "threads";
107 case TaskCategory::kProcesses:
108 return "processes";
109 case TaskCategory::kUnknown:
110 return "";
111 }
112 return "";
113 }
114
115 318 constexpr TaskCategory TaskCategoryFromSettingsPath(std::string_view settings_task_path) {
116
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 150 times.
318 if (settings_task_path.starts_with("threads")) {
117 return TaskCategory::kThreads;
118 }
119
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
168 if (settings_task_path.starts_with("processes")) {
120 168 return TaskCategory::kProcesses;
121 }
122 return TaskCategory::kUnknown;
123 }
124
125
15/30
✗ Branch 0 not taken.
✓ Branch 1 taken 122 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 112 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 112 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 112 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 112 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 112 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 28 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 28 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 28 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 28 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 28 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 28 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 28 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 28 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 28 times.
1252 struct TaskDescriptor {
126 TypeOfTask type = TypeOfTask::kUnknown;
127 StatusOfTask status = StatusOfTask::kEnabled;
128 TaskCategory category = TaskCategory::kUnknown;
129 std::string display_name;
130 };
131
132 /// @brief Returns a string representation of the task type based on the JSON settings file.
133 /// @param type_of_task Type of the task.
134 /// @param settings_file_path Path to the JSON file containing task type strings.
135 /// @param settings_task_path Optional dot-separated nested path inside the `tasks` object.
136 /// @return Formatted string combining the task type and its corresponding value from the file.
137 /// @throws std::runtime_error If the file cannot be opened or the requested settings key is missing.
138 478 inline StatusOfTask GetTaskStatus(TypeOfTask type_of_task, const std::string &settings_file_path,
139 std::string_view settings_task_path = {}) {
140 478 std::ifstream file(settings_file_path);
141
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 458 times.
478 if (!file.is_open()) {
142
2/4
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
40 throw std::runtime_error("Failed to open " + settings_file_path);
143 }
144
145 auto list_settings = ppc::util::InitJSONPtr();
146
2/2
✓ Branch 1 taken 448 times.
✓ Branch 2 taken 10 times.
458 file >> *list_settings;
147
148
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 428 times.
448 const std::string_view type_str = TypeOfTaskToString(type_of_task);
149 448 if (type_str == "unknown") {
150 20 return StatusOfTask::kEnabled;
151 }
152
153
1/2
✓ Branch 0 taken 1392 times.
✗ Branch 1 not taken.
1392 auto get_required_node = [&settings_file_path](const nlohmann::json &node, const std::string &key,
154 const std::string &settings_key_path) -> const nlohmann::json & {
155
1/2
✓ Branch 0 taken 1392 times.
✗ Branch 1 not taken.
1392 if (!node.is_object() || !node.contains(key)) {
156
2/4
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
60 throw std::runtime_error("Missing settings key '" + settings_key_path + "' in " + settings_file_path);
157 }
158 1372 return *node.find(key);
159 428 };
160
161
1/2
✓ Branch 1 taken 428 times.
✗ Branch 2 not taken.
468 std::string settings_key_path = "tasks";
162
2/4
✓ Branch 1 taken 428 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 428 times.
✗ Branch 5 not taken.
428 const auto *settings_node = &get_required_node(*list_settings, "tasks", settings_key_path);
163
2/2
✓ Branch 0 taken 546 times.
✓ Branch 1 taken 80 times.
626 for (size_t start = 0; start < settings_task_path.size();) {
164 546 const size_t separator = settings_task_path.find('.', start);
165
2/2
✓ Branch 0 taken 348 times.
✓ Branch 1 taken 198 times.
546 const size_t key_size = separator == std::string_view::npos ? settings_task_path.size() - start : separator - start;
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 546 times.
546 if (key_size == 0) {
167 throw std::runtime_error("Empty settings key in '" + std::string(settings_task_path) + "' from " +
168 settings_file_path);
169 }
170
1/2
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
546 const std::string key(settings_task_path.substr(start, key_size));
171
2/4
✓ Branch 1 taken 546 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
556 settings_key_path += "." + key;
172
2/2
✓ Branch 1 taken 536 times.
✓ Branch 2 taken 10 times.
546 settings_node = &get_required_node(*settings_node, key, settings_key_path);
173
2/2
✓ Branch 0 taken 198 times.
✓ Branch 1 taken 338 times.
536 if (separator == std::string_view::npos) {
174 break;
175 }
176
1/2
✓ Branch 0 taken 198 times.
✗ Branch 1 not taken.
198 start = separator + 1;
177 }
178
179
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 10 times.
30 const std::string type_key(type_str);
180
1/2
✓ Branch 1 taken 418 times.
✗ Branch 2 not taken.
418 settings_key_path += "." + type_key;
181
2/2
✓ Branch 1 taken 408 times.
✓ Branch 2 taken 10 times.
418 const auto &type_node = get_required_node(*settings_node, type_key, settings_key_path);
182
3/6
✓ Branch 1 taken 398 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 398 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
816 return StatusOfTaskFromString(type_node.get<std::string>());
183 478 }
184
185 160 inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string &settings_file_path,
186 std::string_view settings_task_path = {}) {
187 160 const StatusOfTask status = GetTaskStatus(type_of_task, settings_file_path, settings_task_path);
188
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 80 times.
100 const std::string_view type_str = TypeOfTaskToString(type_of_task);
189
2/2
✓ Branch 1 taken 70 times.
✓ Branch 2 taken 10 times.
100 if (type_str == "unknown") {
190 20 return std::string(type_str);
191 }
192
2/6
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 80 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
240 return std::string(type_str) + "_" + std::string(StatusOfTaskToString(status));
193 }
194
195 enum class StateOfTesting : uint8_t {
196 kFunc,
197 kPerf,
198 };
199
200 template <typename InType, typename OutType>
201 /// @brief Base abstract class representing a generic task with a defined pipeline.
202 /// @tparam InType Input data type.
203 /// @tparam OutType Output data type.
204 class Task {
205 public:
206 using InputType = InType;
207 using OutputType = OutType;
208
209 40 Task() = default;
210
10/20
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 10 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 10 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 5 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 10 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 10 times.
✗ Branch 24 not taken.
✓ Branch 26 taken 10 times.
✗ Branch 27 not taken.
✓ Branch 29 taken 10 times.
✗ Branch 30 not taken.
273 explicit Task(InType input, TypeOfTask type_of_task) : input_(std::move(input)), type_of_task_(type_of_task) {}
211 Task(const Task &) = delete;
212 Task(Task &&) = delete;
213 Task &operator=(const Task &) = delete;
214 Task &operator=(Task &&) = delete;
215
216 /// @brief Validates input data and task attributes before execution.
217 /// @return True if validation is successful.
218 418 virtual bool Validation() final {
219
2/2
✓ Branch 0 taken 283 times.
✓ Branch 1 taken 10 times.
418 if (stage_ == PipelineStage::kNone || stage_ == PipelineStage::kDone) {
220 398 stage_ = PipelineStage::kValidation;
221 } else {
222 20 stage_ = PipelineStage::kException;
223
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
20 throw std::runtime_error("Validation should be called before preprocessing");
224 }
225 398 return ValidationImpl();
226 }
227
228 /// @brief Performs preprocessing on the input data.
229 /// @return True if preprocessing is successful.
230 398 virtual bool PreProcessing() final {
231
2/2
✓ Branch 0 taken 263 times.
✓ Branch 1 taken 20 times.
398 if (stage_ == PipelineStage::kValidation) {
232 358 stage_ = PipelineStage::kPreProcessing;
233 } else {
234 40 stage_ = PipelineStage::kException;
235
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
40 throw std::runtime_error("Preprocessing should be called after validation");
236 }
237
1/2
✓ Branch 0 taken 263 times.
✗ Branch 1 not taken.
358 if (state_of_testing_ == StateOfTesting::kFunc) {
238 358 InternalTimeTest();
239 }
240 358 return PreProcessingImpl();
241 }
242
243 /// @brief Executes the main logic of the task.
244 /// @return True if execution is successful.
245 348 virtual bool Run() final {
246
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 10 times.
348 if (stage_ == PipelineStage::kPreProcessing || stage_ == PipelineStage::kRun) {
247 328 stage_ = PipelineStage::kRun;
248 } else {
249 20 stage_ = PipelineStage::kException;
250
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
20 throw std::runtime_error("Run should be called after preprocessing");
251 }
252 328 return RunImpl();
253 }
254
255 /// @brief Performs postprocessing on the output data.
256 /// @return True if postprocessing is successful.
257 398 virtual bool PostProcessing() final {
258
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 35 times.
398 if (stage_ == PipelineStage::kRun) {
259 328 stage_ = PipelineStage::kDone;
260 } else {
261 70 stage_ = PipelineStage::kException;
262
1/2
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
70 throw std::runtime_error("Postprocessing should be called after run");
263 }
264
1/2
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
328 if (state_of_testing_ == StateOfTesting::kFunc) {
265 328 InternalTimeTest();
266 }
267 288 return PostProcessingImpl();
268 }
269
270 /// @brief Sets the current testing mode.
271 void SetStateOfTesting(StateOfTesting state_of_testing) {
272 state_of_testing_ = state_of_testing;
273 }
274
275 /// @brief Returns the current testing mode.
276 [[nodiscard]] StateOfTesting GetStateOfTesting() const {
277 return state_of_testing_;
278 }
279
280 /// @brief Returns the dynamic task type.
281 /// @return Current dynamic task type.
282 [[nodiscard]] TypeOfTask GetDynamicTypeOfTask() const {
283
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 return type_of_task_;
284 }
285
286 /// @brief Returns the static task type.
287 /// @return Static task type (default: kUnknown).
288 static constexpr TypeOfTask GetStaticTypeOfTask() {
289 return TypeOfTask::kUnknown;
290 }
291
292 /// @brief Returns a reference to the input data.
293 /// @return Reference to the task's input data.
294 [[nodiscard]] const InType &GetInput() const {
295 return input_;
296 }
297
298 /// @brief Returns a reference to the output data.
299 /// @return Reference to the task's output data.
300 [[nodiscard]] const OutType &GetOutput() const {
301
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
168 return output_;
302 }
303
304 /// @brief Destructor. Verifies that the pipeline was executed in the correct order.
305 /// @note Terminates the program if the pipeline order is incorrect or incomplete.
306 230 virtual ~Task() {
307 240 if (stage_ != PipelineStage::kDone && stage_ != PipelineStage::kException) {
308 ppc::util::DestructorFailureFlag::Set();
309 }
310 #if _OPENMP >= 201811
311 omp_pause_resource_all(omp_pause_soft);
312 #endif
313
2/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
240 }
314
315 protected:
316 /// @brief Returns mutable access to the input for task implementations.
317 InType &GetMutableInput() {
318 return input_;
319 }
320
321 /// @brief Returns mutable access to the output for task implementations.
322 OutType &GetMutableOutput() {
323 return output_;
324 }
325
326 /// @brief Measures execution time between preprocessing and postprocessing steps.
327 /// @throws std::runtime_error If execution exceeds the allowed time limit.
328 686 virtual void InternalTimeTest() final {
329
2/2
✓ Branch 0 taken 263 times.
✓ Branch 1 taken 248 times.
686 if (stage_ == PipelineStage::kPreProcessing) {
330 358 tmp_time_point_ = std::chrono::high_resolution_clock::now();
331 }
332
333
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 263 times.
686 if (stage_ == PipelineStage::kDone) {
334 328 auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() -
335 tmp_time_point_)
336 .count();
337 328 auto diff = static_cast<double>(duration) * 1e-9;
338
339 328 const auto max_time = ppc::util::GetTaskMaxTime();
340
2/2
✓ Branch 0 taken 228 times.
✓ Branch 1 taken 20 times.
328 if (diff < max_time) {
341 return;
342 }
343
344 40 std::stringstream err_msg;
345
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 err_msg << "\nTask execute time need to be: ";
346
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 err_msg << "time < " << max_time << " secs.\n";
347
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 err_msg << "Original time in secs: " << diff << '\n';
348
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
120 throw std::runtime_error(err_msg.str().c_str());
349 40 }
350 }
351
352 /// @brief User-defined validation logic.
353 /// @return True if validation is successful.
354 virtual bool ValidationImpl() = 0;
355
356 /// @brief User-defined preprocessing logic.
357 /// @return True if preprocessing is successful.
358 virtual bool PreProcessingImpl() = 0;
359
360 /// @brief User-defined task execution logic.
361 /// @return True if a run is successful.
362 virtual bool RunImpl() = 0;
363
364 /// @brief User-defined postprocessing logic.
365 /// @return True if postprocessing is successful.
366 virtual bool PostProcessingImpl() = 0;
367
368 private:
369 InType input_{};
370 OutType output_{};
371 StateOfTesting state_of_testing_ = StateOfTesting::kFunc;
372 TypeOfTask type_of_task_ = TypeOfTask::kUnknown;
373 std::chrono::high_resolution_clock::time_point tmp_time_point_;
374 enum class PipelineStage : uint8_t {
375 kNone,
376 kValidation,
377 kPreProcessing,
378 kRun,
379 kDone,
380 kException,
381 } stage_ = PipelineStage::kNone;
382 };
383
384 /// @brief Smart pointer alias for Task.
385 /// @tparam InType Input data type.
386 /// @tparam OutType Output data type.
387 template <typename InType, typename OutType>
388 using TaskPtr = std::unique_ptr<Task<InType, OutType>>;
389
390 /// @brief Constructs and returns a pointer to a task with the given input.
391 /// @tparam TaskType Type of the task to create.
392 /// @tparam InType Type of the input.
393 /// @param in Input to pass to the task constructor.
394 /// @return Unique pointer to the newly created task.
395 template <typename TaskType, typename InType>
396 336 std::unique_ptr<TaskType> TaskGetter(InType &&in) {
397 336 return std::make_unique<TaskType>(std::forward<InType>(in));
398 }
399
400 } // namespace ppc::task
401