1 /*
2  * Copyright (c) 2021 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 FOUNDATION_ACE_FRAMEWORKS_BASE_THREAD_TASK_EXECUTOR_H
17 #define FOUNDATION_ACE_FRAMEWORKS_BASE_THREAD_TASK_EXECUTOR_H
18 
19 #include <cstdint>
20 #include <functional>
21 
22 #include "base/memory/ace_type.h"
23 #include "base/thread/cancelable_callback.h"
24 #include "base/utils/noncopyable.h"
25 #include "base/log/log.h"
26 
27 namespace OHOS::Ace {
28 
29 // 1. Must be consistent with AppExecFwk::EventQueue::Priority.
30 // 2. Do not use this ability arbitrarily.
31 enum class PriorityType : int32_t {
32     // The highest priority queue, should be distributed until the tasks in the queue are completed.
33     VIP = 0,
34     // Event that should be distributed at once if possible.
35     IMMEDIATE,
36     // High priority event, sorted by handle time, should be distributed before low priority event.
37     HIGH,
38     // Normal event, sorted by handle time.
39     LOW,
40     // Event that should be distributed only if no other event right now.
41     IDLE,
42 };
43 
44 class TaskExecutor : public AceType {
45     DECLARE_ACE_TYPE(TaskExecutor, AceType);
46     ACE_DISALLOW_COPY_AND_MOVE(TaskExecutor);
47 
48 public:
49     using Task = std::function<void()>;
50     using CancelableTask = CancelableCallback<void()>;
51 
52     static constexpr int32_t TASK_TYPE_SIZE = 7;
53     enum class TaskType : uint32_t {
54         PLATFORM = 0,
55         UI,
56         IO,
57         GPU,
58         JS,
59         BACKGROUND,
60         UNKNOWN,
61     };
62 
63     ~TaskExecutor() override = default;
64 
65     /**
66      * Post a task to the specified thread.
67      *
68      * @param task Task which need execution.
69      * @param type FrontendType of task, used to specify the thread.
70      * @param name Name of the task.
71      * @return Returns 'true' whether task has been post successfully.
72      */
73     bool PostTask(
74         Task&& task, TaskType type, const std::string& name, PriorityType priorityType = PriorityType::LOW) const
75     {
76         return PostDelayedTask(std::move(task), type, 0, name, priorityType);
77     }
78 
79     /**
80      * Post a task to the specified thread.
81      *
82      * @param task Task which need execution.
83      * @param type FrontendType of task, used to specify the thread.
84      * @param name Name of the task.
85      * @return Returns 'true' if task has been posted successfully.
86      */
87     bool PostTask(const Task& task, TaskType type, const std::string& name,
88         PriorityType priorityType = PriorityType::LOW) const
89     {
90         return PostDelayedTask(task, type, 0, name, priorityType);
91     }
92 
93     /**
94      * Post a task to the specified thread with a trace id.
95      *
96      * @param task Task which need execution.
97      * @param type FrontendType of task, used to specify the thread.
98      * @param id The id to trace the task.
99      * @param name Name of the task.
100      * @return Returns 'true' whether task has been post successfully.
101      */
PostTaskWithTraceId(Task && task,TaskType type,int32_t id,const std::string & name)102     bool PostTaskWithTraceId(Task&& task, TaskType type, int32_t id, const std::string& name) const
103     {
104         Task wrappedTask = WrapTaskWithTraceId(std::move(task), id);
105         return PostDelayedTask(std::move(wrappedTask), type, 0, name);
106     }
107 
108     /**
109      * Post a task to the specified thread.
110      *
111      * @param task Task which need execution.
112      * @param type FrontendType of task, used to specify the thread.
113      * @param id The id to trace the task.
114      * @param name Name of the task.
115      * @return Returns 'true' if task has been posted successfully.
116      */
PostTaskWithTraceId(const Task & task,TaskType type,int32_t id,const std::string & name)117     bool PostTaskWithTraceId(const Task& task, TaskType type, int32_t id, const std::string& name) const
118     {
119         Task wrappedTask = WrapTaskWithTraceId(Task(task), id);
120         return PostDelayedTask(std::move(wrappedTask), type, 0, name);
121     }
122 
123     /**
124      * Post a delayed task to the specified thread.
125      * Never allow to post a background delayed task.
126      *
127      * @param task Task which need execution.
128      * @param type FrontendType of task, used to specify the thread.
129      * @param delayTime Wait a period of time in milliseconds before execution.
130      * @param name Name of the task.
131      * @return Returns 'true' if task has been posted successfully.
132      */
133     bool PostDelayedTask(Task&& task, TaskType type, uint32_t delayTime, const std::string& name,
134         PriorityType priorityType = PriorityType::LOW) const
135     {
136         if (delayTime > 0 && type == TaskType::BACKGROUND) {
137             return false;
138         }
139         return OnPostTask(std::move(task), type, delayTime, name, priorityType);
140     }
141 
142     /**
143      * Post a delayed task to the specified thread.
144      * Never allow to post a background delayed task.
145      *
146      * @param task Task which need execution.
147      * @param type FrontendType of task, used to specify the thread.
148      * @param delayTime Wait a period of time in milliseconds before execution.
149      * @param name Name of the task.
150      * @return Returns 'true' if task has been posted successfully.
151      */
152     bool PostDelayedTask(const Task& task, TaskType type, uint32_t delayTime, const std::string& name,
153         PriorityType priorityType = PriorityType::LOW) const
154     {
155         return PostDelayedTask(Task(task), type, delayTime, name, priorityType);
156     }
157 
158     /**
159      * Post a task to the specified thread and wait until finished executing.
160      * Never allow to post a background synchronous task.
161      *
162      * @param task Task which need execution.
163      * @param type FrontendType of task, used to specify the thread.
164      * @param name Name of the task.
165      * @return Returns 'true' whether task has been executed.
166      */
167     bool PostSyncTask(
168         Task&& task, TaskType type, const std::string& name, PriorityType priorityType = PriorityType::IMMEDIATE) const
169     {
170         if (!task || type == TaskType::BACKGROUND) {
171             return false;
172         } else if (WillRunOnCurrentThread(type)) {
173             task();
174             return true;
175         }
176         return PostTaskAndWait(CancelableTask(std::move(task)), type, name, 0ms, priorityType);
177     }
178 
179     /**
180      * Post a task to the specified thread and wait until finished executing.
181      * Never allow to post a background synchronous task.
182      *
183      * @param task Task which need execution.
184      * @param type FrontendType of task, used to specify the thread.
185      * @param timeoutMs Timeout in milliseconds before task execution.
186      * @param name Name of the task.
187      * @return Returns 'true' whether task has been executed.
188      */
PostSyncTaskTimeout(const Task & task,TaskType type,uint32_t timeoutMs,const std::string & name)189     bool PostSyncTaskTimeout(const Task& task, TaskType type, uint32_t timeoutMs, const std::string& name) const
190     {
191         if (!task || type == TaskType::BACKGROUND) {
192             return false;
193         } else if (WillRunOnCurrentThread(type)) {
194             task();
195             return true;
196         }
197         return PostTaskAndWait(
198             CancelableTask(std::move(task)), type, name, std::chrono::milliseconds(timeoutMs));
199     }
200 
201     /**
202      * Post a task to the specified thread and wait until finished executing.
203      * Never allow to post a background synchronous task.
204      *
205      * @param task Task which need execution.
206      * @param type FrontendType of task, used to specify the thread.
207      * @param name Name of the task.
208      * @return Returns 'true' whether task has been executed.
209      */
PostSyncTask(const Task & task,TaskType type,const std::string & name)210     bool PostSyncTask(const Task& task, TaskType type, const std::string& name) const
211     {
212         return PostSyncTask(Task(task), type, name);
213     }
214 
215     /**
216      * Post a cancelable task to the specified thread and wait until finished executing.
217      * Never allow to post a background synchronous task.
218      *
219      * @param task Task which need execution.
220      * @param type FrontendType of task, used to specify the thread.
221      * @param name Name of the task.
222      * @return Returns 'true' whether task has been executed.
223      */
PostSyncTask(CancelableTask && task,TaskType type,const std::string & name)224     bool PostSyncTask(CancelableTask&& task, TaskType type, const std::string& name) const
225     {
226         if (!task || type == TaskType::BACKGROUND) {
227             return false;
228         } else if (WillRunOnCurrentThread(type)) {
229             CancelableTask avatar(task);
230             task();
231             return avatar.WaitUntilComplete();
232         }
233         return PostTaskAndWait(std::move(task), type, name, 0ms);
234     }
235 
236     /**
237      * Post a cancelable task to the specified thread and wait until finished executing.
238      * Never allow to post a background synchronous task.
239      *
240      * @param task Task which need execution.
241      * @param type FrontendType of task, used to specify the thread.
242      * @param name Name of the task.
243      * @return Returns 'true' whether task has been executed.
244      */
PostSyncTask(const CancelableTask & task,TaskType type,const std::string & name)245     bool PostSyncTask(const CancelableTask& task, TaskType type, const std::string& name) const
246     {
247         return PostSyncTask(CancelableTask(task), type, name);
248     }
249 
250     /**
251      * The task use PostDelayedTask will new an object TraceId, when use
252      * RemoveTask will cause memory overflow.
253      * Post a delayed task without traceId to the specified thread.
254      * Never allow to post a background delayed task.
255      *
256      * @param task Task which need execution.
257      * @param type FrontendType of task, used to specify the thread.
258      * @param delayTime Wait a period of time in milliseconds before execution.
259      * @param name Name of the task.
260      * @return Returns 'true' if task has been posted successfully.
261      */
262     bool PostDelayedTaskWithoutTraceId(Task&& task, TaskType type, uint32_t delayTime, const std::string& name,
263         PriorityType priorityType = PriorityType::LOW) const
264     {
265         if (delayTime > 0 && type == TaskType::BACKGROUND) {
266             return false;
267         }
268         return OnPostTaskWithoutTraceId(std::move(task), type, delayTime, name, priorityType);
269     }
270 
271     virtual void AddTaskObserver(Task&& callback) = 0;
272     virtual void RemoveTaskObserver() = 0;
273     virtual bool WillRunOnCurrentThread(TaskType type) const = 0;
274     virtual void RemoveTask(TaskType type, const std::string &name) = 0;
275 
GetTid(TaskType type)276     virtual int32_t GetTid(TaskType type)
277     {
278         return 0;
279     }
280 
GetTotalTaskNum(TaskType type)281     virtual uint32_t GetTotalTaskNum(TaskType type)
282     {
283         return 0;
284     }
285 
286 protected:
287     TaskExecutor() = default;
288 
289     virtual bool OnPostTask(Task&& task, TaskType type, uint32_t delayTime, const std::string& name,
290         PriorityType priorityType = PriorityType::LOW) const = 0;
291     virtual Task WrapTaskWithTraceId(Task&& task, int32_t id) const = 0;
292     virtual bool OnPostTaskWithoutTraceId(Task&& task, TaskType type, uint32_t delayTime, const std::string& name,
293         PriorityType priorityType = PriorityType::LOW) const = 0;
294 
295 #ifdef ACE_DEBUG
OnPreSyncTask(TaskType type)296     virtual bool OnPreSyncTask(TaskType type) const
297     {
298         return true;
299     }
OnPostSyncTask()300     virtual void OnPostSyncTask() const {}
301 #endif
302 
303 private:
304     bool PostTaskAndWait(CancelableTask&& task, TaskType type, const std::string& name,
305         std::chrono::milliseconds timeoutMs = 0ms, PriorityType priorityType = PriorityType::IMMEDIATE) const
306     {
307 #ifdef ACE_DEBUG
308         bool result = false;
309         if (OnPreSyncTask(type)) {
310             result = OnPostTask(Task(task), type, 0, name, priorityType) && task.WaitUntilComplete(timeoutMs);
311             OnPostSyncTask();
312         }
313         return result;
314 #else
315         return OnPostTask(Task(task), type, 0, name, priorityType) && task.WaitUntilComplete(timeoutMs);
316 #endif
317     }
318 };
319 
320 class TaskWrapper {
321 public:
322     virtual bool WillRunOnCurrentThread() = 0;
323     virtual void Call(const TaskExecutor::Task& task) = 0;
324 
325     virtual ~TaskWrapper() = default;
326 };
327 
328 class SingleTaskExecutor final {
329 public:
330     using Task = TaskExecutor::Task;
331     using CancelableTask = TaskExecutor::CancelableTask;
332     using TaskType = TaskExecutor::TaskType;
333 
SingleTaskExecutor(RefPtr<TaskExecutor> && taskExecutor,TaskType type)334     SingleTaskExecutor(RefPtr<TaskExecutor>&& taskExecutor, TaskType type)
335         : taskExecutor_(std::move(taskExecutor)), type_(type)
336     {}
SingleTaskExecutor(const RefPtr<TaskExecutor> & taskExecutor,TaskType type)337     SingleTaskExecutor(const RefPtr<TaskExecutor>& taskExecutor, TaskType type)
338         : taskExecutor_(taskExecutor), type_(type)
339     {}
340     ~SingleTaskExecutor() = default;
341 
Make(RefPtr<TaskExecutor> && taskExecutor,TaskType type)342     static SingleTaskExecutor Make(RefPtr<TaskExecutor>&& taskExecutor, TaskType type)
343     {
344         return SingleTaskExecutor(std::move(taskExecutor), type);
345     }
346 
Make(const RefPtr<TaskExecutor> & taskExecutor,TaskType type)347     static SingleTaskExecutor Make(const RefPtr<TaskExecutor>& taskExecutor, TaskType type)
348     {
349         return SingleTaskExecutor(taskExecutor, type);
350     }
351 
352     /**
353      * Post a task to the specified thread.
354      *
355      * @param task Task which need execution.
356      * @param name Name of the task.
357      * @return Returns 'true' whether task has been post successfully.
358      */
PostTask(Task && task,const std::string & name)359     bool PostTask(Task&& task, const std::string& name) const
360     {
361         return taskExecutor_ ? taskExecutor_->PostTask(std::move(task), type_, name) : false;
362     }
363 
364     /**
365      * Post a task to the specified thread.
366      *
367      * @param task Task which need execution.
368      * @param name Name of the task.
369      * @return Returns 'true' whether task has been post successfully.
370      */
PostTask(const Task & task,const std::string & name)371     bool PostTask(const Task& task, const std::string& name) const
372     {
373         return taskExecutor_ ? taskExecutor_->PostTask(task, type_, name) : false;
374     }
375 
376     /**
377      * Post a delayed task to the specified thread.
378      * Never allow to post a background delayed task.
379      *
380      * @param task Task which need execution.
381      * @param delayTime Wait a period of time in milliseconds before execution.
382      * @param name Name of the task.
383      * @return Returns 'true' if task has been posted successfully.
384      */
PostDelayedTask(Task && task,uint32_t delayTime,const std::string & name)385     bool PostDelayedTask(Task&& task, uint32_t delayTime, const std::string& name) const
386     {
387         return taskExecutor_ ? taskExecutor_->PostDelayedTask(std::move(task), type_, delayTime, name) : false;
388     }
389 
390     /**
391      * Post a delayed task to the specified thread.
392      * Never allow to post a background delayed task.
393      *
394      * @param task Task which need execution.
395      * @param delayTime Wait a period of time in milliseconds before execution.
396      * @param name Name of the task.
397      * @return Returns 'true' if task has been posted successfully.
398      */
PostDelayedTask(const Task & task,uint32_t delayTime,const std::string & name)399     bool PostDelayedTask(const Task& task, uint32_t delayTime, const std::string& name) const
400     {
401         return taskExecutor_ ? taskExecutor_->PostDelayedTask(task, type_, delayTime, name) : false;
402     }
403 
404     /**
405      * Post a task to the specified thread and wait until finished executing.
406      * Never allow to post a background synchronous task.
407      *
408      * @param task Task which need execution.
409      * @param name Name of the task.
410      * @return Returns 'true' whether task has been executed.
411      */
PostSyncTask(Task && task,const std::string & name)412     bool PostSyncTask(Task&& task, const std::string& name) const
413     {
414         return taskExecutor_ ? taskExecutor_->PostSyncTask(std::move(task), type_, name) : false;
415     }
416 
417     /**
418      * Post a task to the specified thread and wait until finished executing.
419      * Never allow to post a background synchronous task.
420      *
421      * @param task Task which need execution.
422      * @param name Name of the task.
423      * @return Returns 'true' whether task has been executed.
424      */
PostSyncTask(const Task & task,const std::string & name)425     bool PostSyncTask(const Task& task, const std::string& name) const
426     {
427         return taskExecutor_ ? taskExecutor_->PostSyncTask(task, type_, name) : false;
428     }
429 
430     /**
431      * Post a cancelable task to the specified thread and wait until finished executing.
432      * Never allow to post a background synchronous task.
433      *
434      * @param task Task which need execution.
435      * @param name Name of the task.
436      * @return Returns 'true' whether task has been executed.
437      */
PostSyncTask(CancelableTask && task,const std::string & name)438     bool PostSyncTask(CancelableTask&& task, const std::string& name) const
439     {
440         return taskExecutor_ ? taskExecutor_->PostSyncTask(std::move(task), type_, name) : false;
441     }
442 
443     /**
444      * Post a cancelable task to the specified thread and wait until finished executing.
445      * Never allow to post a background synchronous task.
446      *
447      * @param task Task which need execution.
448      * @param name Name of the task.
449      * @return Returns 'true' whether task has been executed.
450      */
PostSyncTask(const CancelableTask & task,const std::string & name)451     bool PostSyncTask(const CancelableTask& task, const std::string& name) const
452     {
453         return taskExecutor_ ? taskExecutor_->PostSyncTask(task, type_, name) : false;
454     }
455 
GetTaskExecutor()456     RefPtr<TaskExecutor> GetTaskExecutor() const
457     {
458         return taskExecutor_;
459     }
460 
IsRunOnCurrentThread()461     bool IsRunOnCurrentThread() const
462     {
463         return taskExecutor_ ? taskExecutor_->WillRunOnCurrentThread(type_) : false;
464     }
465 
466 private:
467     RefPtr<TaskExecutor> taskExecutor_;
468     TaskExecutor::TaskType type_;
469 };
470 
471 } // namespace OHOS::Ace
472 
473 #endif // FOUNDATION_ACE_FRAMEWORKS_BASE_THREAD_TASK_EXECUTOR_H
474