GCC Code Coverage Report


Directory: ./
File: modules/core/task/include/task.hpp
Date: 2025-06-27 00:53:24
Exec Total Coverage
Lines: 71 73 97.3%
Functions: 58 59 98.3%
Branches: 80 134 59.7%

Line Branch Exec Source
1 #pragma once
2
3 #include <omp.h>
4
5 #include <algorithm>
6 #include <chrono>
7 #include <core/util/include/util.hpp>
8 #include <cstdint>
9 #include <cstdlib>
10 #include <exception>
11 #include <fstream>
12 #include <functional>
13 #include <iomanip>
14 #include <iostream>
15 #include <memory>
16 #include <sstream>
17 #include <stdexcept>
18 #include <string>
19 #include <vector>
20
21 namespace ppc::core {
22
23 /// @brief Represents the type of task (parallelization technology).
24 /// @details Used to select the implementation type in tests and execution logic.
25 enum TypeOfTask : uint8_t {
26 /// Use all available implementations
27 kALL,
28 /// MPI (Message Passing Interface)
29 kMPI,
30 /// OpenMP (Open Multi-Processing)
31 kOMP,
32 /// Sequential implementation
33 kSEQ,
34 /// Standard Thread Library (STL threads)
35 kSTL,
36 /// Intel Threading Building Blocks (TBB)
37 kTBB,
38 /// Unknown task type
39 kUnknown
40 };
41
42 /// @brief Indicates whether a task is enabled or disabled.
43 enum StatusOfTask : uint8_t {
44 /// Task is enabled and should be executed
45 kEnabled,
46 /// Task is disabled and will be skipped
47 kDisabled
48 };
49
50 /// @brief Returns a string representation of the task status.
51 /// @param status_of_task Task status (enabled or disabled).
52 /// @return "enabled" if the task is enabled, otherwise "disabled".
53 inline std::string GetStringTaskStatus(StatusOfTask status_of_task) {
54 if (status_of_task == kDisabled) {
55
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 return "disabled";
56 }
57
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 return "enabled";
58 }
59
60 /// @brief Returns a string representation of the task type based on the JSON settings file.
61 /// @param type_of_task Type of the task.
62 /// @param settings_file_path Path to the JSON file containing task type strings.
63 /// @return Formatted string combining the task type and its corresponding value from the file.
64 /// @throws std::runtime_error If the file cannot be opened.
65 718 inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string &settings_file_path) {
66 718 std::ifstream file(settings_file_path);
67
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 688 times.
718 if (!file.is_open()) {
68
2/4
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
60 throw std::runtime_error("Failed to open " + settings_file_path);
69 }
70
71 auto list_settings = ppc::util::InitJSONPtr();
72
2/2
✓ Branch 1 taken 678 times.
✓ Branch 2 taken 10 times.
688 file >> *list_settings;
73
74 658 auto to_type_str = [&](const std::string &type) -> std::string {
75
3/6
✓ Branch 2 taken 658 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 648 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 648 times.
✗ Branch 9 not taken.
3260 return type + "_" + std::string((*list_settings)["tasks"][type]);
76
7/7
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 94 times.
✓ Branch 3 taken 94 times.
✓ Branch 4 taken 94 times.
✓ Branch 5 taken 188 times.
✓ Branch 6 taken 20 times.
678 };
77
78 if (type_of_task == TypeOfTask::kALL) {
79
2/4
✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
188 return to_type_str("all");
80 }
81 if (type_of_task == TypeOfTask::kSTL) {
82
2/4
✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
188 return to_type_str("stl");
83 }
84 if (type_of_task == TypeOfTask::kOMP) {
85
2/4
✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
188 return to_type_str("omp");
86 }
87 if (type_of_task == TypeOfTask::kMPI) {
88
2/4
✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
188 return to_type_str("mpi");
89 }
90 if (type_of_task == TypeOfTask::kTBB) {
91
2/4
✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 94 times.
✗ Branch 5 not taken.
188 return to_type_str("tbb");
92 }
93 if (type_of_task == TypeOfTask::kSEQ) {
94
3/4
✓ Branch 1 taken 188 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✓ Branch 5 taken 10 times.
366 return to_type_str("seq");
95 }
96
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
40 return "unknown";
97 718 }
98
99 enum StateOfTesting : uint8_t { kFunc, kPerf };
100
101 template <typename InType, typename OutType>
102 /// @brief Base abstract class representing a generic task with a defined pipeline.
103 /// @tparam InType Input data type.
104 /// @tparam OutType Output data type.
105 class Task {
106 public:
107 /// @brief Constructs a new Task object.
108
2/4
✓ Branch 1 taken 466 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 466 times.
596 explicit Task(StateOfTesting /*state_of_testing*/ = StateOfTesting::kFunc) { functions_order_.clear(); }
109
110 /// @brief Validates input data and task attributes before execution.
111 /// @return True if validation is successful.
112 796 virtual bool Validation() final {
113
1/2
✓ Branch 2 taken 566 times.
✗ Branch 3 not taken.
796 InternalOrderTest(ppc::util::FuncName());
114 796 return ValidationImpl();
115 }
116
117 /// @brief Performs preprocessing on the input data.
118 /// @return True if preprocessing is successful.
119 796 virtual bool PreProcessing() final {
120
1/2
✓ Branch 2 taken 566 times.
✗ Branch 3 not taken.
796 InternalOrderTest(ppc::util::FuncName());
121
2/2
✓ Branch 0 taken 416 times.
✓ Branch 1 taken 150 times.
796 if (state_of_testing_ == StateOfTesting::kFunc) {
122
1/2
✓ Branch 2 taken 416 times.
✗ Branch 3 not taken.
992 InternalTimeTest(ppc::util::FuncName());
123 }
124 796 return PreProcessingImpl();
125 }
126
127 /// @brief Executes the main logic of the task.
128 /// @return True if execution is successful.
129 956 virtual bool Run() final {
130
1/2
✓ Branch 2 taken 646 times.
✗ Branch 3 not taken.
956 InternalOrderTest(ppc::util::FuncName());
131 956 return RunImpl();
132 }
133
134 /// @brief Performs postprocessing on the output data.
135 /// @return True if postprocessing is successful.
136 796 virtual bool PostProcessing() final {
137
1/2
✓ Branch 2 taken 566 times.
✗ Branch 3 not taken.
796 InternalOrderTest(ppc::util::FuncName());
138
2/2
✓ Branch 0 taken 416 times.
✓ Branch 1 taken 150 times.
796 if (state_of_testing_ == StateOfTesting::kFunc) {
139
2/2
✓ Branch 2 taken 406 times.
✓ Branch 3 taken 10 times.
972 InternalTimeTest(ppc::util::FuncName());
140 }
141 776 return PostProcessingImpl();
142 }
143
144 /// @brief Returns the current testing mode.
145 /// @return Reference to the current StateOfTesting.
146 StateOfTesting &GetStateOfTesting() { return state_of_testing_; }
147
148 /// @brief Sets the dynamic task type.
149 /// @param type_of_task Task type to set.
150
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
346 void SetTypeOfTask(TypeOfTask type_of_task) { type_of_task_ = type_of_task; }
151
152 /// @brief Returns the dynamic task type.
153 /// @return Current dynamic task type.
154
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 [[nodiscard]] TypeOfTask GetDynamicTypeOfTask() const { return type_of_task_; }
155
156 /// @brief Returns the current task status.
157 /// @return Task status (enabled or disabled).
158 [[nodiscard]] StatusOfTask GetStatusOfTask() const { return status_of_task_; }
159
160 /// @brief Returns the static task type.
161 /// @return Static task type (default: kUnknown).
162 static constexpr TypeOfTask GetStaticTypeOfTask() { return TypeOfTask::kUnknown; }
163
164 /// @brief Returns a reference to the input data.
165 /// @return Reference to the task's input data.
166
5/10
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 10 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 30 times.
✗ Branch 14 not taken.
120 InType &GetInput() { return input_; }
167
168 /// @brief Returns a reference to the output data.
169 /// @return Reference to the task's output data.
170
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
366 OutType &GetOutput() { return output_; }
171
172 /// @brief Destructor. Verifies that the pipeline was executed in the correct order.
173 /// @note Terminates the program if pipeline order is incorrect or incomplete.
174 932 virtual ~Task() {
175
2/4
✓ Branch 0 taken 466 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 466 times.
✗ Branch 3 not taken.
932 if (!functions_order_.empty() || !was_worked_) {
176 std::cerr << "ORDER OF FUNCTIONS IS NOT RIGHT! \n Expected - \"Validation\", \"PreProcessing\", \"Run\", "
177 "\"PostProcessing\" \n";
178 std::terminate();
179 } else {
180 functions_order_.clear();
181 }
182 #if _OPENMP >= 201811
183 omp_pause_resource_all(omp_pause_soft);
184 #endif
185
1/2
✓ Branch 0 taken 466 times.
✗ Branch 1 not taken.
1864 }
186
187 protected:
188 /// @brief Verifies the correct order of pipeline method calls.
189 /// @param str Name of the method being called.
190 3344 virtual void InternalOrderTest(const std::string &str) final {
191 3344 functions_order_.push_back(str);
192
3/4
✓ Branch 0 taken 566 times.
✓ Branch 1 taken 1778 times.
✓ Branch 2 taken 566 times.
✗ Branch 3 not taken.
3344 if (str == "PostProcessing" && IsFullPipelineStage()) {
193 functions_order_.clear();
194 } else {
195 2548 was_worked_ = true;
196 }
197 3344 }
198
199 /// @brief Measures execution time between preprocessing and postprocessing steps.
200 /// @param str Name of the method being timed.
201 /// @throws std::runtime_error If execution exceeds the allowed time limit.
202 992 virtual void InternalTimeTest(const std::string &str) final {
203
2/2
✓ Branch 0 taken 416 times.
✓ Branch 1 taken 416 times.
992 if (str == "PreProcessing") {
204 496 tmp_time_point_ = std::chrono::high_resolution_clock::now();
205 }
206
207
2/2
✓ Branch 0 taken 416 times.
✓ Branch 1 taken 416 times.
992 if (str == "PostProcessing") {
208 496 auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() -
209 tmp_time_point_)
210 .count();
211 496 auto diff = static_cast<double>(duration) * 1e-9;
212
213 496 std::stringstream err_msg;
214
2/2
✓ Branch 0 taken 406 times.
✓ Branch 1 taken 10 times.
496 if (diff < kMaxTestTime) {
215
1/2
✓ Branch 1 taken 406 times.
✗ Branch 2 not taken.
476 err_msg << "Test time:" << std::fixed << std::setprecision(10) << diff << '\n';
216 } else {
217
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 err_msg << "\nTask execute time need to be: ";
218
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 err_msg << "time < " << kMaxTestTime << " secs.\n";
219
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 err_msg << "Original time in secs: " << diff << '\n';
220
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
60 throw std::runtime_error(err_msg.str().c_str());
221 }
222 496 }
223 972 }
224
225 /// @brief User-defined validation logic.
226 /// @return True if validation is successful.
227 virtual bool ValidationImpl() = 0;
228
229 /// @brief User-defined preprocessing logic.
230 /// @return True if preprocessing is successful.
231 virtual bool PreProcessingImpl() = 0;
232
233 /// @brief User-defined task execution logic.
234 /// @return True if run is successful.
235 virtual bool RunImpl() = 0;
236
237 /// @brief User-defined postprocessing logic.
238 /// @return True if postprocessing is successful.
239 virtual bool PostProcessingImpl() = 0;
240
241 private:
242 InType input_;
243 OutType output_;
244 StateOfTesting state_of_testing_ = kFunc;
245 TypeOfTask type_of_task_ = kUnknown;
246 StatusOfTask status_of_task_ = kEnabled;
247 std::vector<std::string> functions_order_;
248 std::vector<std::string> right_functions_order_ = {"Validation", "PreProcessing", "Run", "PostProcessing"};
249 static constexpr double kMaxTestTime = 1.0;
250 std::chrono::high_resolution_clock::time_point tmp_time_point_;
251 bool was_worked_ = false;
252
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
566 bool IsFullPipelineStage() {
254
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
566 if (functions_order_.size() < 4) {
255 return false;
256 }
257
258 566 auto it = std::adjacent_find(functions_order_.begin() + 2,
259 functions_order_.begin() + static_cast<long>(functions_order_.size() - 2),
260 std::not_equal_to<>());
261
262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
566 return (functions_order_[0] == "Validation" && functions_order_[1] == "PreProcessing" &&
263
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 336 times.
566 functions_order_[2] == "Run" &&
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
566 it == (functions_order_.begin() + static_cast<long>(functions_order_.size() - 2)) &&
265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
566 functions_order_[functions_order_.size() - 1] == "PostProcessing");
266 }
267 };
268
269 /// @brief Smart pointer alias for Task.
270 /// @tparam InType Input data type.
271 /// @tparam OutType Output data type.
272 template <typename InType, typename OutType>
273 using TaskPtr = std::shared_ptr<Task<InType, OutType>>;
274
275 /// @brief Constructs and returns a shared pointer to a task with the given input.
276 /// @tparam TaskType Type of the task to create.
277 /// @tparam InType Type of the input.
278 /// @param in Input to pass to the task constructor.
279 /// @return Shared pointer to the newly created task.
280 template <typename TaskType, typename InType>
281 336 std::shared_ptr<TaskType> TaskGetter(InType in) {
282 336 return std::make_shared<TaskType>(in);
283 }
284
285 } // namespace ppc::core
286