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