• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef LIBPANDABASE_PANDA_TASK_RUNNER_H
17 #define LIBPANDABASE_PANDA_TASK_RUNNER_H
18 
19 #include "libpandabase/macros.h"
20 
21 #include <functional>
22 #include <thread>
23 
24 // clang-format off
25 /**
26 *    ark::TaskRunner runner;
27 *
28 *    runner.AddFinalize(foo);
29 *    runner.AddCallbackOnSuccess(foo1);
30 *    runner.AddCallbackOnFail(foo2);
31 *    runner.SetTaskOnSuccess(next_task);
32 *    bool success = task();
33 *    TaskRunner::EndTask(runner, success); --> if success --
34 *                                              |           |
35 *                                              |           |
36 *                                              |           |
37 *                                              |           if has_task_on_success -->-- StartTask(runner, next_task);
38 *                                              |           |
39 *                                              |           |
40 *                                              |           |
41 *                                              |           else -->------ X ------ call foo1;
42 *                                              |                                   call foo;
43 *                                              |
44 *                                              |
45 *                                              |
46 *                                              else ------ if has_task_on_fail -->---- X
47 *                                                          |
48 *                                                          |
49 *                                                          |
50 *                                                          else -->---- call foo2;
51 *                                                                       call foo;
52 */
53 // clang-format on
54 
55 namespace ark {
56 
57 /**
58  * @brief TaskRunner class implements interface to add callbacks and set next tasks to be executed
59  * @tparam RunnerT - an inherited class that contains ContextT and specifies StartTask and GetContext methods
60  * @tparam ContexT - a class representing the context of the task
61  */
62 template <typename RunnerT, typename ContextT>
63 class TaskRunner {
64 public:
65     TaskRunner() = default;
66     NO_COPY_SEMANTIC(TaskRunner);
67     DEFAULT_MOVE_SEMANTIC(TaskRunner);
68     ~TaskRunner() = default;
69 
70     // We must use && to avoid slicing
71     using TaskFunc = std::function<void(RunnerT)>;
72     using Callback = std::function<void(ContextT &)>;
73 
74     virtual ContextT &GetContext() = 0;
75     static void StartTask(RunnerT taskRunner, TaskFunc taskFunc);
76 
77     class TaskCallback {
78     public:
79         TaskCallback() = default;
80         NO_COPY_SEMANTIC(TaskCallback);
81         NO_MOVE_OPERATOR(TaskCallback);
TaskCallback(TaskCallback && taskCb)82         TaskCallback(TaskCallback &&taskCb)
83             : callbackOnSuccess_(std::move(taskCb.callbackOnSuccess_)),
84               callbackOnFail_(std::move(taskCb.callbackOnFail_))
85         {
86             taskCb.SetNeedMakeCall(false);
87         }
88         // NOLINTNEXTLINE(modernize-use-equals-default)
~TaskCallback()89         ~TaskCallback()
90         {
91             ASSERT(!needMakeCall_);
92         }
93 
RunOnSuccess(ContextT & taskCtx)94         void RunOnSuccess(ContextT &taskCtx)
95         {
96             if (callbackOnSuccess_) {
97                 callbackOnSuccess_(taskCtx);
98             }
99             SetNeedMakeCall(false);
100         }
101 
RunOnFail(ContextT & taskCtx)102         void RunOnFail(ContextT &taskCtx)
103         {
104             if (callbackOnFail_) {
105                 callbackOnFail_(taskCtx);
106             }
107             SetNeedMakeCall(false);
108         }
109 
110         template <typename Foo>
AddOnSuccess(Foo foo)111         void AddOnSuccess(Foo foo)
112         {
113             if (LIKELY(callbackOnSuccess_)) {
114                 callbackOnSuccess_ = NextCallback(std::move(callbackOnSuccess_), std::move(foo));
115             } else {
116                 callbackOnSuccess_ = std::move(foo);
117             }
118             SetNeedMakeCall(true);
119         }
120 
121         template <typename Foo>
AddOnFail(Foo foo)122         void AddOnFail(Foo foo)
123         {
124             if (LIKELY(callbackOnFail_)) {
125                 callbackOnFail_ = NextCallback(std::move(callbackOnFail_), std::move(foo));
126             } else {
127                 callbackOnFail_ = std::move(foo);
128             }
129             SetNeedMakeCall(true);
130         }
131 
SetNeedMakeCall(bool needMakeCall)132         void SetNeedMakeCall([[maybe_unused]] bool needMakeCall)
133         {
134 #ifndef NDEBUG
135             needMakeCall_ = needMakeCall;
136 #endif
137         }
138 
139     private:
140         template <typename Foo>
NextCallback(Callback && cb,Foo foo)141         Callback NextCallback(Callback &&cb, Foo foo)
142         {
143             return [cb = std::move(cb), foo = std::move(foo)](ContextT &taskCtx) mutable {
144                 foo(taskCtx);
145                 cb(taskCtx);
146             };
147         }
148 
149         Callback callbackOnSuccess_;
150         Callback callbackOnFail_;
151 #ifndef NDEBUG
152         bool needMakeCall_ {false};
153 #endif
154     };
155 
156     class Task {
157     public:
158         Task() = default;
159         NO_COPY_SEMANTIC(Task);
160         NO_MOVE_OPERATOR(Task);
Task(Task && task)161         Task(Task &&task) : taskFunc_(std::move(task.taskFunc_)), hasTask_(task.hasTask_)
162         {
163             task.hasTask_ = false;
164         }
165         ~Task() = default;
166 
167         explicit operator bool() const
168         {
169             return hasTask_;
170         }
171 
172         template <typename Foo>
SetTaskFunc(Foo foo)173         void SetTaskFunc(Foo foo)
174         {
175             ASSERT(!hasTask_);
176             taskFunc_ = std::move(foo);
177             hasTask_ = true;
178         }
179 
GetTaskFunc()180         TaskFunc GetTaskFunc()
181         {
182             hasTask_ = false;
183             return std::move(taskFunc_);
184         }
185 
186     private:
187         TaskFunc taskFunc_;
188         bool hasTask_ {false};
189     };
190 
191     /// @brief Starts a chain of callbacks on success
RunCallbackOnSuccess()192     void RunCallbackOnSuccess()
193     {
194         auto &context = GetContext();
195         taskCb_.RunOnSuccess(context);
196     }
197 
198     /// @brief Starts a chain of callbacks on fail
RunCallbackOnFail()199     void RunCallbackOnFail()
200     {
201         auto &context = GetContext();
202         taskCb_.RunOnFail(context);
203     }
204 
205     /**
206      * @brief Adds callback to the chain on success
207      * @param foo - callable object that gets ContextT &
208      */
209     template <typename Foo>
AddCallbackOnSuccess(Foo foo)210     void AddCallbackOnSuccess(Foo foo)
211     {
212         ASSERT(!taskOnSuccess_);
213         taskCb_.AddOnSuccess(std::move(foo));
214     }
215 
216     /**
217      * @brief Adds callback to the chain on fail
218      * @param foo - callable object that gets ContextT &
219      */
220     template <typename Foo>
AddCallbackOnFail(Foo foo)221     void AddCallbackOnFail(Foo foo)
222     {
223         ASSERT(!taskOnFail_);
224         taskCb_.AddOnFail(std::move(foo));
225     }
226 
227     /**
228      * @brief Adds callback to the chain on success and on fail
229      * @param foo - callable object that gets ContextT &
230      */
231     template <typename Foo>
AddFinalize(Foo foo)232     void AddFinalize(Foo foo)
233     {
234         AddCallbackOnSuccess(foo);
235         AddCallbackOnFail(std::move(foo));
236     }
237 
238     /**
239      * @brief Sets next task on success. After this call you must not add callback on success
240      * @param foo - callable object that gets RunnerT &&
241      */
242     template <typename Foo>
SetTaskOnSuccess(Foo foo)243     void SetTaskOnSuccess(Foo foo)
244     {
245         ASSERT(!taskOnSuccess_);
246         taskOnSuccess_.SetTaskFunc(std::move(foo));
247     }
248 
249     /**
250      * @brief Sets next task on fail. After this call you must not add callback on fail
251      * @param foo - callable object that gets RunnerT &&
252      */
253     template <typename Foo>
SetTaskOnFail(Foo foo)254     void SetTaskOnFail(Foo foo)
255     {
256         ASSERT(!taskOnFail_);
257         taskOnFail_.SetTaskFunc(std::move(foo));
258     }
259 
260     /**
261      * @brief Completes the current task and starts the next one if it was set.
262      * Otherwise, it calls callbacks depending on @param success
263      * @param task_runner - Current TaskRunner
264      * @param success - result of current task
265      */
EndTask(RunnerT taskRunner,bool success)266     static void EndTask(RunnerT taskRunner, bool success)
267     {
268         auto &baseRunner = static_cast<TaskRunner &>(taskRunner);
269         auto taskOnSuccess = std::move(baseRunner.taskOnSuccess_);
270         auto taskOnFail = std::move(baseRunner.taskOnFail_);
271         ASSERT(!baseRunner.taskOnSuccess_ && !baseRunner.taskOnFail_);
272         ContextT &taskCtx = taskRunner.GetContext();
273         if (success) {
274             if (taskOnSuccess) {
275                 RunnerT::StartTask(std::move(taskRunner), taskOnSuccess.GetTaskFunc());
276                 return;
277             }
278             baseRunner.taskCb_.RunOnSuccess(taskCtx);
279         } else {
280             if (taskOnFail) {
281                 RunnerT::StartTask(std::move(taskRunner), taskOnFail.GetTaskFunc());
282                 return;
283             }
284             baseRunner.taskCb_.RunOnFail(taskCtx);
285         }
286     }
287 
288 private:
289     TaskCallback taskCb_;
290     Task taskOnSuccess_;
291     Task taskOnFail_;
292 };
293 
294 }  // namespace ark
295 
296 #endif  // LIBPANDABASE_PANDA_TASK_RUNNER_H
297