GCC Code Coverage Report


Directory: ./
File: modules/task/include/task.hpp
Date: 2026-07-02 01:34:19
Exec Total Coverage
Lines: 106 111 95.5%
Functions: 40 40 100.0%
Branches: 102 170 60.0%

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 1426 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 1536 times.
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3002 for (const auto &[key, value] : kTaskTypeMappings) {
52
4/6
✓ Branch 0 taken 388 times.
✓ Branch 1 taken 1038 times.
✓ Branch 2 taken 418 times.
✓ Branch 3 taken 1118 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2962 if (key == type) {
53 806 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 308 times.
✗ Branch 1 not taken.
388 return status_of_task == StatusOfTask::kDisabled ? "disabled" : "enabled";
78 }
79
80
2/2
✓ Branch 0 taken 378 times.
✓ Branch 1 taken 10 times.
388 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 378 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 308 constexpr TaskCategory TaskCategoryFromSettingsPath(std::string_view settings_task_path) {
116
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 140 times.
308 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
2/5
✗ Branch 0 not taken.
✓ Branch 1 taken 924 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 168 times.
✗ Branch 4 not taken.
3368 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 468 inline StatusOfTask GetTaskStatus(TypeOfTask type_of_task, const std::string &settings_file_path,
139 std::string_view settings_task_path = {}) {
140 468 std::ifstream file(settings_file_path);
141
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 448 times.
468 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 438 times.
✓ Branch 2 taken 10 times.
448 file >> *list_settings;
147
148
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 418 times.
438 const std::string_view type_str = TypeOfTaskToString(type_of_task);
149 438 if (type_str == "unknown") {
150 20 return StatusOfTask::kEnabled;
151 }
152
153
1/2
✓ Branch 0 taken 1362 times.
✗ Branch 1 not taken.
1362 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 1362 times.
✗ Branch 1 not taken.
1362 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 1342 return *node.find(key);
159 418 };
160
161
1/2
✓ Branch 1 taken 418 times.
✗ Branch 2 not taken.
458 std::string settings_key_path = "tasks";
162
2/4
✓ Branch 1 taken 418 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 418 times.
✗ Branch 5 not taken.
418 const auto *settings_node = &get_required_node(*list_settings, "tasks", settings_key_path);
163
2/2
✓ Branch 0 taken 536 times.
✓ Branch 1 taken 80 times.
616 for (size_t start = 0; start < settings_task_path.size();) {
164 536 const size_t separator = settings_task_path.find('.', start);
165
2/2
✓ Branch 0 taken 338 times.
✓ Branch 1 taken 198 times.
536 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 536 times.
536 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 536 times.
✗ Branch 2 not taken.
536 const std::string key(settings_task_path.substr(start, key_size));
171
2/4
✓ Branch 1 taken 536 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
546 settings_key_path += "." + key;
172
2/2
✓ Branch 1 taken 526 times.
✓ Branch 2 taken 10 times.
536 settings_node = &get_required_node(*settings_node, key, settings_key_path);
173
2/2
✓ Branch 0 taken 198 times.
✓ Branch 1 taken 328 times.
526 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 408 times.
✗ Branch 2 not taken.
408 settings_key_path += "." + type_key;
181
2/2
✓ Branch 1 taken 398 times.
✓ Branch 2 taken 10 times.
408 const auto &type_node = get_required_node(*settings_node, type_key, settings_key_path);
182
3/6
✓ Branch 1 taken 388 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 388 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
796 return StatusOfTaskFromString(type_node.get<std::string>());
183 468 }
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
6/12
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 40 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 10 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 10 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 10 times.
✗ Branch 17 not taken.
283 Task() = default;
207 Task(const Task &) = delete;
208 Task(Task &&) = delete;
209 Task &operator=(const Task &) = delete;
210 Task &operator=(Task &&) = delete;
211
212 /// @brief Validates input data and task attributes before execution.
213 /// @return True if validation is successful.
214 418 virtual bool Validation() final {
215
2/2
✓ Branch 0 taken 283 times.
✓ Branch 1 taken 10 times.
418 if (stage_ == PipelineStage::kNone || stage_ == PipelineStage::kDone) {
216 398 stage_ = PipelineStage::kValidation;
217 } else {
218 20 stage_ = PipelineStage::kException;
219
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
20 throw std::runtime_error("Validation should be called before preprocessing");
220 }
221 398 return ValidationImpl();
222 }
223
224 /// @brief Performs preprocessing on the input data.
225 /// @return True if preprocessing is successful.
226 398 virtual bool PreProcessing() final {
227
2/2
✓ Branch 0 taken 263 times.
✓ Branch 1 taken 20 times.
398 if (stage_ == PipelineStage::kValidation) {
228 358 stage_ = PipelineStage::kPreProcessing;
229 } else {
230 40 stage_ = PipelineStage::kException;
231
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
40 throw std::runtime_error("Preprocessing should be called after validation");
232 }
233
1/2
✓ Branch 0 taken 263 times.
✗ Branch 1 not taken.
358 if (state_of_testing_ == StateOfTesting::kFunc) {
234 358 InternalTimeTest();
235 }
236 358 return PreProcessingImpl();
237 }
238
239 /// @brief Executes the main logic of the task.
240 /// @return True if execution is successful.
241 348 virtual bool Run() final {
242
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 10 times.
348 if (stage_ == PipelineStage::kPreProcessing || stage_ == PipelineStage::kRun) {
243 328 stage_ = PipelineStage::kRun;
244 } else {
245 20 stage_ = PipelineStage::kException;
246
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
20 throw std::runtime_error("Run should be called after preprocessing");
247 }
248 328 return RunImpl();
249 }
250
251 /// @brief Performs postprocessing on the output data.
252 /// @return True if postprocessing is successful.
253 398 virtual bool PostProcessing() final {
254
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 35 times.
398 if (stage_ == PipelineStage::kRun) {
255 328 stage_ = PipelineStage::kDone;
256 } else {
257 70 stage_ = PipelineStage::kException;
258
1/2
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
70 throw std::runtime_error("Postprocessing should be called after run");
259 }
260
1/2
✓ Branch 0 taken 248 times.
✗ Branch 1 not taken.
328 if (state_of_testing_ == StateOfTesting::kFunc) {
261 328 InternalTimeTest();
262 }
263 288 return PostProcessingImpl();
264 }
265
266 /// @brief Returns the current testing mode.
267 /// @return Reference to the current StateOfTesting.
268 StateOfTesting &GetStateOfTesting() {
269 return state_of_testing_;
270 }
271
272 /// @brief Sets the dynamic task type.
273 /// @param type_of_task Task type to set.
274 void SetTypeOfTask(TypeOfTask type_of_task) {
275
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
178 type_of_task_ = type_of_task;
276 }
277
278 /// @brief Returns the dynamic task type.
279 /// @return Current dynamic task type.
280 [[nodiscard]] TypeOfTask GetDynamicTypeOfTask() const {
281
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 return type_of_task_;
282 }
283
284 /// @brief Returns the current task status.
285 /// @return Task status (enabled or disabled).
286 [[nodiscard]] StatusOfTask GetStatusOfTask() const {
287 return status_of_task_;
288 }
289
290 /// @brief Returns the static task type.
291 /// @return Static task type (default: kUnknown).
292 static constexpr TypeOfTask GetStaticTypeOfTask() {
293 return TypeOfTask::kUnknown;
294 }
295
296 /// @brief Returns a reference to the input data.
297 /// @return Reference to the task's input data.
298 InType &GetInput() {
299
6/12
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 40 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 10 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 10 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 10 times.
✗ Branch 17 not taken.
115 return input_;
300 }
301
302 /// @brief Returns a reference to the output data.
303 /// @return Reference to the task's output data.
304 OutType &GetOutput() {
305 168 return output_;
306 }
307
308 /// @brief Destructor. Verifies that the pipeline was executed in the correct order.
309 /// @note Terminates the program if the pipeline order is incorrect or incomplete.
310 230 virtual ~Task() {
311 240 if (stage_ != PipelineStage::kDone && stage_ != PipelineStage::kException) {
312 ppc::util::DestructorFailureFlag::Set();
313 }
314 #if _OPENMP >= 201811
315 omp_pause_resource_all(omp_pause_soft);
316 #endif
317
2/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
240 }
318
319 protected:
320 /// @brief Measures execution time between preprocessing and postprocessing steps.
321 /// @throws std::runtime_error If execution exceeds the allowed time limit.
322 686 virtual void InternalTimeTest() final {
323
2/2
✓ Branch 0 taken 263 times.
✓ Branch 1 taken 248 times.
686 if (stage_ == PipelineStage::kPreProcessing) {
324 358 tmp_time_point_ = std::chrono::high_resolution_clock::now();
325 }
326
327
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 263 times.
686 if (stage_ == PipelineStage::kDone) {
328 328 auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() -
329 tmp_time_point_)
330 .count();
331 328 auto diff = static_cast<double>(duration) * 1e-9;
332
333 328 const auto max_time = ppc::util::GetTaskMaxTime();
334
2/2
✓ Branch 0 taken 228 times.
✓ Branch 1 taken 20 times.
328 if (diff < max_time) {
335 return;
336 }
337
338 40 std::stringstream err_msg;
339
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 err_msg << "\nTask execute time need to be: ";
340
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 err_msg << "time < " << max_time << " secs.\n";
341
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 err_msg << "Original time in secs: " << diff << '\n';
342
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
120 throw std::runtime_error(err_msg.str().c_str());
343 40 }
344 }
345
346 /// @brief User-defined validation logic.
347 /// @return True if validation is successful.
348 virtual bool ValidationImpl() = 0;
349
350 /// @brief User-defined preprocessing logic.
351 /// @return True if preprocessing is successful.
352 virtual bool PreProcessingImpl() = 0;
353
354 /// @brief User-defined task execution logic.
355 /// @return True if a run is successful.
356 virtual bool RunImpl() = 0;
357
358 /// @brief User-defined postprocessing logic.
359 /// @return True if postprocessing is successful.
360 virtual bool PostProcessingImpl() = 0;
361
362 private:
363 InType input_{};
364 OutType output_{};
365 StateOfTesting state_of_testing_ = StateOfTesting::kFunc;
366 TypeOfTask type_of_task_ = TypeOfTask::kUnknown;
367 StatusOfTask status_of_task_ = StatusOfTask::kEnabled;
368 std::chrono::high_resolution_clock::time_point tmp_time_point_;
369 enum class PipelineStage : uint8_t {
370 kNone,
371 kValidation,
372 kPreProcessing,
373 kRun,
374 kDone,
375 kException,
376 } stage_ = PipelineStage::kNone;
377 };
378
379 /// @brief Smart pointer alias for Task.
380 /// @tparam InType Input data type.
381 /// @tparam OutType Output data type.
382 template <typename InType, typename OutType>
383 using TaskPtr = std::unique_ptr<Task<InType, OutType>>;
384
385 /// @brief Constructs and returns a pointer to a task with the given input.
386 /// @tparam TaskType Type of the task to create.
387 /// @tparam InType Type of the input.
388 /// @param in Input to pass to the task constructor.
389 /// @return Unique pointer to the newly created task.
390 template <typename TaskType, typename InType>
391 336 std::unique_ptr<TaskType> TaskGetter(const InType &in) {
392 336 return std::make_unique<TaskType>(in);
393 }
394
395 } // namespace ppc::task
396