GCC Code Coverage Report


Directory: ./
File: modules/task/include/task.hpp
Date: 2025-08-13 00:58:25
Exec Total Coverage
Lines: 70 70 100.0%
Functions: 35 37 94.6%
Branches: 67 111 60.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 <iomanip>
11 #include <iostream>
12 #include <memory>
13 #include <sstream>
14 #include <stdexcept>
15 #include <string>
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 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>;
41 using TaskMappingArray = std::array<TaskMapping, 6>;
42
43 const 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 768 inline std::string TypeOfTaskToString(TypeOfTask type) {
51
2/2
✓ Branch 0 taken 2850 times.
✓ Branch 1 taken 40 times.
2890 for (const auto &[key, value] : kTaskTypeMappings) {
52
2/2
✓ Branch 0 taken 728 times.
✓ Branch 1 taken 2122 times.
2850 if (key == type) {
53 return value;
54 }
55 }
56 40 return "unknown";
57 }
58
59 /// @brief Indicates whether a task is enabled or disabled.
60 enum StatusOfTask : uint8_t {
61 /// Task is enabled and should be executed
62 kEnabled,
63 /// Task is disabled and will be skipped
64 kDisabled
65 };
66
67 /// @brief Returns a string representation of the task status.
68 /// @param status_of_task Task status (enabled or disabled).
69 /// @return "enabled" if the task is enabled, otherwise "disabled".
70 inline std::string GetStringTaskStatus(StatusOfTask status_of_task) {
71 if (status_of_task == kDisabled) {
72
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 return "disabled";
73 }
74
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 return "enabled";
75 }
76
77 /// @brief Returns a string representation of the task type based on the JSON settings file.
78 /// @param type_of_task Type of the task.
79 /// @param settings_file_path Path to the JSON file containing task type strings.
80 /// @return Formatted string combining the task type and its corresponding value from the file.
81 /// @throws std::runtime_error If the file cannot be opened.
82 828 inline std::string GetStringTaskType(TypeOfTask type_of_task, const std::string &settings_file_path) {
83 828 std::ifstream file(settings_file_path);
84
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 788 times.
828 if (!file.is_open()) {
85
2/4
✓ Branch 2 taken 40 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 40 times.
✗ Branch 6 not taken.
80 throw std::runtime_error("Failed to open " + settings_file_path);
86 }
87
88 auto list_settings = ppc::util::InitJSONPtr();
89
2/2
✓ Branch 1 taken 768 times.
✓ Branch 2 taken 20 times.
788 file >> *list_settings;
90
91
1/2
✓ Branch 1 taken 768 times.
✗ Branch 2 not taken.
768 std::string type_str = TypeOfTaskToString(type_of_task);
92
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 728 times.
768 if (type_str == "unknown") {
93 40 return type_str;
94 }
95
96
4/8
✓ Branch 1 taken 728 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 728 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 708 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 708 times.
✗ Branch 11 not taken.
3600 return type_str + "_" + std::string((*list_settings)["tasks"][type_str]);
97 828 }
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
6/12
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 60 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.
511 class Task {
106 public:
107 /// @brief Validates input data and task attributes before execution.
108 /// @return True if validation is successful.
109 1006 virtual bool Validation() final {
110
2/2
✓ Branch 0 taken 661 times.
✓ Branch 1 taken 10 times.
1006 if (stage_ == PipelineStage::kNone || stage_ == PipelineStage::kDone) {
111 986 stage_ = PipelineStage::kValidation;
112 } else {
113 20 stage_ = PipelineStage::kException;
114
1/2
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
20 throw std::runtime_error("Validation should be called before preprocessing");
115 }
116 986 return ValidationImpl();
117 }
118
119 /// @brief Performs preprocessing on the input data.
120 /// @return True if preprocessing is successful.
121 966 virtual bool PreProcessing() final {
122
2/2
✓ Branch 0 taken 631 times.
✓ Branch 1 taken 20 times.
966 if (stage_ == PipelineStage::kValidation) {
123 926 stage_ = PipelineStage::kPreProcessing;
124 } else {
125 40 stage_ = PipelineStage::kException;
126
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
40 throw std::runtime_error("Preprocessing should be called after validation");
127 }
128
2/2
✓ Branch 0 taken 431 times.
✓ Branch 1 taken 200 times.
926 if (state_of_testing_ == StateOfTesting::kFunc) {
129 526 InternalTimeTest();
130 }
131 926 return PreProcessingImpl();
132 }
133
134 /// @brief Executes the main logic of the task.
135 /// @return True if execution is successful.
136 1116 virtual bool Run() final {
137
2/2
✓ Branch 0 taken 706 times.
✓ Branch 1 taken 20 times.
1116 if (stage_ == PipelineStage::kPreProcessing || stage_ == PipelineStage::kRun) {
138 1076 stage_ = PipelineStage::kRun;
139 } else {
140 40 stage_ = PipelineStage::kException;
141
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
40 throw std::runtime_error("Run should be called after preprocessing");
142 }
143 1076 return RunImpl();
144 }
145
146 /// @brief Performs postprocessing on the output data.
147 /// @return True if postprocessing is successful.
148 966 virtual bool PostProcessing() final {
149
2/2
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 35 times.
966 if (stage_ == PipelineStage::kRun) {
150 896 stage_ = PipelineStage::kDone;
151 } else {
152 70 stage_ = PipelineStage::kException;
153
1/2
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
70 throw std::runtime_error("Postprocessing should be called after run");
154 }
155
2/2
✓ Branch 0 taken 416 times.
✓ Branch 1 taken 200 times.
896 if (state_of_testing_ == StateOfTesting::kFunc) {
156 496 InternalTimeTest();
157 }
158 856 return PostProcessingImpl();
159 }
160
161 /// @brief Returns the current testing mode.
162 /// @return Reference to the current StateOfTesting.
163 StateOfTesting &GetStateOfTesting() {
164 return state_of_testing_;
165 }
166
167 /// @brief Sets the dynamic task type.
168 /// @param type_of_task Task type to set.
169 void SetTypeOfTask(TypeOfTask type_of_task) {
170
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
346 type_of_task_ = type_of_task;
171 }
172
173 /// @brief Returns the dynamic task type.
174 /// @return Current dynamic task type.
175 [[nodiscard]] TypeOfTask GetDynamicTypeOfTask() const {
176
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 return type_of_task_;
177 }
178
179 /// @brief Returns the current task status.
180 /// @return Task status (enabled or disabled).
181 [[nodiscard]] StatusOfTask GetStatusOfTask() const {
182 return status_of_task_;
183 }
184
185 /// @brief Returns the static task type.
186 /// @return Static task type (default: kUnknown).
187 static constexpr TypeOfTask GetStaticTypeOfTask() {
188 return TypeOfTask::kUnknown;
189 }
190
191 /// @brief Returns a reference to the input data.
192 /// @return Reference to the task's input data.
193 InType &GetInput() {
194
6/12
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 60 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.
175 return input_;
195 }
196
197 /// @brief Returns a reference to the output data.
198 /// @return Reference to the task's output data.
199 OutType &GetOutput() {
200
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 return output_;
201 }
202
203 /// @brief Destructor. Verifies that the pipeline was executed in the correct order.
204 /// @note Terminates the program if the pipeline order is incorrect or incomplete.
205 350 virtual ~Task() {
206 380 if (stage_ != PipelineStage::kDone && stage_ != PipelineStage::kException) {
207 ppc::util::DestructorFailureFlag::Set();
208 }
209 #if _OPENMP >= 201811
210 omp_pause_resource_all(omp_pause_soft);
211 #endif
212
4/12
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 10 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
380 }
213
214 protected:
215 /// @brief Measures execution time between preprocessing and postprocessing steps.
216 /// @throws std::runtime_error If execution exceeds the allowed time limit.
217 1022 virtual void InternalTimeTest() final {
218
2/2
✓ Branch 0 taken 431 times.
✓ Branch 1 taken 416 times.
1022 if (stage_ == PipelineStage::kPreProcessing) {
219 526 tmp_time_point_ = std::chrono::high_resolution_clock::now();
220 }
221
222
2/2
✓ Branch 0 taken 416 times.
✓ Branch 1 taken 431 times.
1022 if (stage_ == PipelineStage::kDone) {
223 496 auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() -
224 tmp_time_point_)
225 .count();
226 496 auto diff = static_cast<double>(duration) * 1e-9;
227
228 496 const auto max_time = ppc::util::GetTaskMaxTime();
229 496 std::stringstream err_msg;
230
2/2
✓ Branch 0 taken 396 times.
✓ Branch 1 taken 20 times.
496 if (diff < max_time) {
231
1/2
✓ Branch 1 taken 396 times.
✗ Branch 2 not taken.
456 err_msg << "Test time:" << std::fixed << std::setprecision(10) << diff << '\n';
232 } else {
233
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 err_msg << "\nTask execute time need to be: ";
234
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 err_msg << "time < " << max_time << " secs.\n";
235
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 err_msg << "Original time in secs: " << diff << '\n';
236
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
120 throw std::runtime_error(err_msg.str().c_str());
237 }
238 496 }
239 982 }
240
241 /// @brief User-defined validation logic.
242 /// @return True if validation is successful.
243 virtual bool ValidationImpl() = 0;
244
245 /// @brief User-defined preprocessing logic.
246 /// @return True if preprocessing is successful.
247 virtual bool PreProcessingImpl() = 0;
248
249 /// @brief User-defined task execution logic.
250 /// @return True if a run is successful.
251 virtual bool RunImpl() = 0;
252
253 /// @brief User-defined postprocessing logic.
254 /// @return True if postprocessing is successful.
255 virtual bool PostProcessingImpl() = 0;
256
257 private:
258 InType input_{};
259 OutType output_{};
260 StateOfTesting state_of_testing_ = kFunc;
261 TypeOfTask type_of_task_ = kUnknown;
262 StatusOfTask status_of_task_ = kEnabled;
263 std::chrono::high_resolution_clock::time_point tmp_time_point_;
264 enum class PipelineStage : uint8_t {
265 kNone,
266 kValidation,
267 kPreProcessing,
268 kRun,
269 kDone,
270 kException
271 } stage_ = PipelineStage::kNone;
272 };
273
274 /// @brief Smart pointer alias for Task.
275 /// @tparam InType Input data type.
276 /// @tparam OutType Output data type.
277 template <typename InType, typename OutType>
278 using TaskPtr = std::shared_ptr<Task<InType, OutType>>;
279
280 /// @brief Constructs and returns a shared pointer to a task with the given input.
281 /// @tparam TaskType Type of the task to create.
282 /// @tparam InType Type of the input.
283 /// @param in Input to pass to the task constructor.
284 /// @return Shared a pointer to the newly created task.
285 template <typename TaskType, typename InType>
286 336 std::shared_ptr<TaskType> TaskGetter(InType in) {
287 336 return std::make_shared<TaskType>(in);
288 }
289
290 } // namespace ppc::task
291