1 /*
2  * Copyright (c) 2023 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 #include "core/common/task_executor_impl.h"
17 
18 #if !defined(PREVIEW)
19 #ifdef OHOS_STANDARD_SYSTEM
20 #include <sys/prctl.h>
21 #endif
22 #include <sys/resource.h>
23 #endif
24 #include <unistd.h>
25 #include "base/log/trace_id.h"
26 #include "base/thread/background_task_executor.h"
27 #include "core/common/container.h"
28 #include "core/common/task_runner_adapter_factory.h"
29 
30 namespace OHOS::Ace {
31 namespace {
32 constexpr int32_t GPU_THREAD_PRIORITY = -10;
33 constexpr int32_t UI_THREAD_PRIORITY = -15;
34 
GenJsThreadName()35 inline std::string GenJsThreadName()
36 {
37     static std::atomic<uint32_t> instanceCount { 1 };
38     return std::string("jsThread-") + std::to_string(instanceCount.fetch_add(1, std::memory_order_relaxed));
39 }
40 } // namespace
41 
WrapTaskWithContainer(TaskExecutor::Task && task,int32_t id,std::function<void ()> && traceIdFunc) const42 TaskExecutor::Task TaskExecutorImpl::WrapTaskWithContainer(
43     TaskExecutor::Task&& task, int32_t id, std::function<void()>&& traceIdFunc) const
44 {
45     auto wrappedTask = [originTask = std::move(task), id, traceIdPtr = TraceId::CreateTraceId(),
46                            traceIdFunc = std::move(traceIdFunc)]() {
47         ContainerScope scope(id);
48         if (originTask && traceIdPtr) {
49             traceIdPtr->SetTraceId();
50             originTask();
51             traceIdPtr->ClearTraceId();
52         } else {
53             LOGW("WrapTaskWithContainer: originTask or traceIdPtr is null.");
54         }
55         if (traceIdFunc) {
56             traceIdFunc();
57         }
58     };
59     return wrappedTask;
60 }
61 
WrapTaskWithCustomWrapper(TaskExecutor::Task && task,int32_t id,std::function<void ()> && traceIdFunc) const62 TaskExecutor::Task TaskExecutorImpl::WrapTaskWithCustomWrapper(
63     TaskExecutor::Task&& task, int32_t id, std::function<void()>&& traceIdFunc) const
64 {
65     auto wrappedTask = [taskWrapper = taskWrapper_, originTask = std::move(task), id,
66                            traceIdPtr = TraceId::CreateTraceId(), traceIdFunc = std::move(traceIdFunc)]() {
67         ContainerScope scope(id);
68         if (originTask && traceIdPtr) {
69             traceIdPtr->SetTraceId();
70             taskWrapper->Call(originTask);
71             traceIdPtr->ClearTraceId();
72         } else {
73             LOGW("WrapTaskWithContainer: originTask or traceIdPtr is null.");
74         }
75         if (traceIdFunc) {
76             traceIdFunc();
77         }
78     };
79     return wrappedTask;
80 }
81 
PostTaskToTaskRunner(const RefPtr<TaskRunnerAdapter> & taskRunner,TaskExecutor::Task && task,uint32_t delayTime,const std::string & name,PriorityType priorityType) const82 bool TaskExecutorImpl::PostTaskToTaskRunner(const RefPtr<TaskRunnerAdapter>& taskRunner, TaskExecutor::Task&& task,
83     uint32_t delayTime, const std::string& name, PriorityType priorityType) const
84 {
85     CHECK_NULL_RETURN(taskRunner, false);
86     CHECK_NULL_RETURN(task, false);
87 
88     if (delayTime > 0) {
89         taskRunner->PostDelayedTask(std::move(task), delayTime, name, priorityType);
90     } else {
91         taskRunner->PostTask(std::move(task), name, priorityType);
92     }
93     return true;
94 }
95 
SetThreadPriority(int32_t priority) const96 void TaskExecutorImpl::SetThreadPriority(int32_t priority) const
97 {
98 #if !defined(PREVIEW) and !defined(IOS_PLATFORM)
99     if (setpriority(PRIO_PROCESS, gettid(), priority) < 0) {
100         LOGW("Failed to set thread priority, errno = %{private}d", errno);
101     }
102 #endif
103 }
104 
TaskExecutorImpl(const RefPtr<TaskExecutorImpl> & taskExecutor)105 TaskExecutorImpl::TaskExecutorImpl(const RefPtr<TaskExecutorImpl>& taskExecutor)
106 {
107     jsRunner_ = TaskRunnerAdapterFactory::Create(false, GenJsThreadName());
108     platformRunner_ = taskExecutor->platformRunner_;
109     uiRunner_ = taskExecutor->uiRunner_;
110     ioRunner_ = taskExecutor->ioRunner_;
111     gpuRunner_ = taskExecutor->gpuRunner_;
112 }
113 
TaskExecutorImpl(const OHOS::Ace::TaskRunners & taskRunners)114 TaskExecutorImpl::TaskExecutorImpl(const OHOS::Ace::TaskRunners& taskRunners)
115 {
116     jsRunner_ = TaskRunnerAdapterFactory::Create(false, GenJsThreadName());
117 
118     platformRunner_ = taskRunners.GetPlatformTaskRunner();
119     uiRunner_ = taskRunners.GetUITaskRunner();
120     ioRunner_ = taskRunners.GetIOTaskRunner();
121     gpuRunner_ = taskRunners.GetGPUTaskRunner();
122 }
123 
InitPlatformThread(bool useCurrentEventRunner,bool isStageModel)124 void TaskExecutorImpl::InitPlatformThread(bool useCurrentEventRunner, bool isStageModel)
125 {
126     platformRunner_ = TaskRunnerAdapterFactory::Create(useCurrentEventRunner, "");
127     FillTaskTypeTable(TaskType::PLATFORM);
128 }
129 
InitJsThread(bool newThread)130 void TaskExecutorImpl::InitJsThread(bool newThread)
131 {
132     if (newThread) {
133         jsRunner_ = TaskRunnerAdapterFactory::Create(false, GenJsThreadName());
134     } else {
135         jsRunner_ = uiRunner_;
136     }
137 
138     PostTaskToTaskRunner(
139         jsRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::JS); }, 0,
140         "ArkUIFillTaskTypeTable");
141 }
142 
InitOtherThreads(ThreadModelImpl * threadModel)143 void TaskExecutorImpl::InitOtherThreads(ThreadModelImpl* threadModel)
144 {
145     if (threadModel) {
146         InitOtherThreads(threadModel->GetTaskRunners());
147     }
148 }
149 
InitOtherThreads(const OHOS::Ace::TaskRunners & taskRunners)150 void TaskExecutorImpl::InitOtherThreads(const OHOS::Ace::TaskRunners& taskRunners)
151 {
152     uiRunner_ = taskRunners.GetUITaskRunner();
153     ioRunner_ = taskRunners.GetIOTaskRunner();
154     gpuRunner_ = taskRunners.GetGPUTaskRunner();
155 
156     PostTaskToTaskRunner(
157         uiRunner_, [this] { SetThreadPriority(UI_THREAD_PRIORITY); }, 0, "ArkUISetThreadPriority");
158     PostTaskToTaskRunner(
159         gpuRunner_, [this] { SetThreadPriority(GPU_THREAD_PRIORITY); }, 0, "ArkUISetThreadPriority");
160 
161     PostTaskToTaskRunner(
162         uiRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::UI); }, 0,
163         "ArkUIFillTaskTypeTable");
164     PostTaskToTaskRunner(
165         ioRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::IO); }, 0,
166         "ArkUIFillTaskTypeTable");
167     PostTaskToTaskRunner(
168         gpuRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::GPU); }, 0,
169         "ArkUIFillTaskTypeTable");
170 }
171 
OnPostTask(Task && task,TaskType type,uint32_t delayTime,const std::string & name,PriorityType priorityType) const172 bool TaskExecutorImpl::OnPostTask(
173     Task&& task, TaskType type, uint32_t delayTime, const std::string& name, PriorityType priorityType) const
174 {
175     int32_t currentId = Container::CurrentId();
176     auto traceIdFunc = [weak = WeakClaim(const_cast<TaskExecutorImpl*>(this)), type]() {
177         auto sp = weak.Upgrade();
178         if (sp) {
179             sp->taskIdTable_[static_cast<uint32_t>(type)]++;
180         }
181     };
182 
183     if (taskWrapper_ != nullptr && (type == TaskType::PLATFORM || type == TaskType::UI || type == TaskType::JS)) {
184         TaskExecutor::Task wrappedTask = WrapTaskWithCustomWrapper(std::move(task), currentId, std::move(traceIdFunc));
185         taskWrapper_->Call(std::move(wrappedTask));
186         return true;
187     }
188 
189     TaskExecutor::Task wrappedTask =
190         currentId >= 0 ? WrapTaskWithContainer(std::move(task), currentId, std::move(traceIdFunc)) : std::move(task);
191 
192     switch (type) {
193         case TaskType::PLATFORM:
194             return PostTaskToTaskRunner(platformRunner_, std::move(wrappedTask), delayTime, name);
195         case TaskType::UI:
196             return PostTaskToTaskRunner(uiRunner_, std::move(wrappedTask), delayTime, name, priorityType);
197         case TaskType::IO:
198             return PostTaskToTaskRunner(ioRunner_, std::move(wrappedTask), delayTime, name);
199         case TaskType::GPU:
200             return PostTaskToTaskRunner(gpuRunner_, std::move(wrappedTask), delayTime, name);
201         case TaskType::JS:
202             return PostTaskToTaskRunner(jsRunner_, std::move(wrappedTask), delayTime, name);
203         case TaskType::BACKGROUND:
204             // Ignore delay time
205             return BackgroundTaskExecutor::GetInstance().PostTask(std::move(wrappedTask));
206         default:
207             return false;
208     }
209 }
210 
WrapTaskWithTraceId(Task && task,int32_t id) const211 TaskExecutor::Task TaskExecutorImpl::WrapTaskWithTraceId(Task&& task, int32_t id) const
212 {
213     return WrapTaskWithContainer(std::move(task), id);
214 }
215 
WillRunOnCurrentThread(TaskType type) const216 bool TaskExecutorImpl::WillRunOnCurrentThread(TaskType type) const
217 {
218     switch (type) {
219         case TaskType::PLATFORM:
220             return platformRunner_ ? (taskWrapper_ != nullptr ? taskWrapper_->WillRunOnCurrentThread()
221                                                               : platformRunner_->RunsTasksOnCurrentThread())
222                                    : false;
223         case TaskType::UI:
224             return uiRunner_ ? (taskWrapper_ != nullptr ? taskWrapper_->WillRunOnCurrentThread()
225                                                         : uiRunner_->RunsTasksOnCurrentThread())
226                              : false;
227         case TaskType::IO:
228             return ioRunner_ ? ioRunner_->RunsTasksOnCurrentThread() : false;
229         case TaskType::GPU:
230             return gpuRunner_ ? gpuRunner_->RunsTasksOnCurrentThread() : false;
231         case TaskType::JS:
232             return jsRunner_ ? (taskWrapper_ != nullptr ? taskWrapper_->WillRunOnCurrentThread()
233                                                         : jsRunner_->RunsTasksOnCurrentThread())
234                              : false;
235         case TaskType::BACKGROUND:
236             // Always return false for background tasks.
237             return false;
238         default:
239             return false;
240     }
241 }
242 
243 thread_local TaskExecutor::TaskType TaskExecutorImpl::localTaskType = TaskExecutor::TaskType::UNKNOWN;
244 
245 #ifdef ACE_DEBUG
TaskTypeToString(TaskExecutor::TaskType type)246 static const char* TaskTypeToString(TaskExecutor::TaskType type)
247 {
248     switch (type) {
249         case TaskExecutor::TaskType::PLATFORM:
250             return "PLATFORM";
251         case TaskExecutor::TaskType::UI:
252             return "UI";
253         case TaskExecutor::TaskType::IO:
254             return "IO";
255         case TaskExecutor::TaskType::GPU:
256             return "GPU";
257         case TaskExecutor::TaskType::JS:
258             return "JS";
259         case TaskExecutor::TaskType::BACKGROUND:
260             return "BACKGROUND";
261         case TaskExecutor::TaskType::UNKNOWN:
262         default:
263             return "UNKNOWN";
264     }
265 }
266 
OnPreSyncTask(TaskType type) const267 bool TaskExecutorImpl::OnPreSyncTask(TaskType type) const
268 {
269     std::lock_guard<std::mutex> lock(tableMutex_);
270     auto it = taskTypeTable_.find(type);
271     // when task type not filled, just skip
272     if (it == taskTypeTable_.end()) {
273         return true;
274     }
275 
276     auto itSync = syncTaskTable_.find(it->second.threadId);
277     while (itSync != syncTaskTable_.end()) {
278         if (itSync->second == std::this_thread::get_id()) {
279             DumpDeadSyncTask(localTaskType, type);
280             ACE_DCHECK(itSync->second != std::this_thread::get_id() && "DEAD LOCK HAPPENED !!!");
281             return false;
282         }
283 
284         itSync = syncTaskTable_.find(itSync->second);
285     }
286 
287     syncTaskTable_.emplace(std::this_thread::get_id(), it->second.threadId);
288     return true;
289 }
290 
OnPostSyncTask() const291 void TaskExecutorImpl::OnPostSyncTask() const
292 {
293     std::lock_guard<std::mutex> lock(tableMutex_);
294     syncTaskTable_.erase(std::this_thread::get_id());
295 }
296 
DumpDeadSyncTask(TaskType from,TaskType to) const297 void TaskExecutorImpl::DumpDeadSyncTask(TaskType from, TaskType to) const
298 {
299     auto itFrom = taskTypeTable_.find(from);
300     auto itTo = taskTypeTable_.find(to);
301 
302     ACE_DCHECK(itFrom != taskTypeTable_.end());
303     ACE_DCHECK(itTo != taskTypeTable_.end());
304 
305     LOGE("DEAD LOCK HAPPEN: %{public}s(%{public}d, %{public}s) -> %{public}s(%{public}d, %{public}s)",
306         TaskTypeToString(from), itFrom->second.tid, itFrom->second.threadName.c_str(), TaskTypeToString(to),
307         itTo->second.tid, itTo->second.threadName.c_str());
308 }
309 #endif
310 
FillTaskTypeTable(TaskType type)311 void TaskExecutorImpl::FillTaskTypeTable(TaskType type)
312 {
313     constexpr size_t MAX_THREAD_NAME_SIZE = 32;
314     char threadNameBuf[MAX_THREAD_NAME_SIZE] = { 0 };
315     const char* threadName = threadNameBuf;
316 #if !defined(PREVIEW) and !defined(IOS_PLATFORM)
317 #ifdef OHOS_STANDARD_SYSTEM
318     if (prctl(PR_GET_NAME, threadNameBuf) < 0) {
319         threadName = "unknown";
320     }
321 #else
322     if (pthread_getname_np(pthread_self(), threadNameBuf, sizeof(threadNameBuf)) != 0) {
323         threadName = "unknown";
324     }
325 #endif
326 #endif
327 
328     localTaskType = type;
329     ThreadInfo info = {
330         .threadId = std::this_thread::get_id(),
331 #if !defined(PREVIEW) and !defined(IOS_PLATFORM)
332         .tid = gettid(),
333 #endif
334         .threadName = threadName,
335     };
336 
337     std::lock_guard<std::mutex> lock(tableMutex_);
338     taskTypeTable_.emplace(type, info);
339 }
340 
FillTaskTypeTable(const WeakPtr<TaskExecutorImpl> & weak,TaskType type)341 void TaskExecutorImpl::FillTaskTypeTable(const WeakPtr<TaskExecutorImpl>& weak, TaskType type)
342 {
343     auto taskExecutor = weak.Upgrade();
344     if (taskExecutor) {
345         taskExecutor->FillTaskTypeTable(type);
346     }
347 }
348 
OnPostTaskWithoutTraceId(Task && task,TaskType type,uint32_t delayTime,const std::string & name,PriorityType priorityType) const349 bool TaskExecutorImpl::OnPostTaskWithoutTraceId(
350     Task&& task, TaskType type, uint32_t delayTime, const std::string& name, PriorityType priorityType) const
351 {
352     TaskExecutor::Task wrappedTask = std::move(task);
353 
354     switch (type) {
355         case TaskType::PLATFORM:
356             return PostTaskToTaskRunner(platformRunner_, std::move(wrappedTask), delayTime, name);
357         case TaskType::UI:
358             return PostTaskToTaskRunner(uiRunner_, std::move(wrappedTask), delayTime, name, priorityType);
359         case TaskType::IO:
360             return PostTaskToTaskRunner(ioRunner_, std::move(wrappedTask), delayTime, name);
361         case TaskType::GPU:
362             return PostTaskToTaskRunner(gpuRunner_, std::move(wrappedTask), delayTime, name);
363         case TaskType::JS:
364             return PostTaskToTaskRunner(jsRunner_, std::move(wrappedTask), delayTime, name);
365         case TaskType::BACKGROUND:
366             // Ignore delay time
367             return BackgroundTaskExecutor::GetInstance().PostTask(std::move(wrappedTask));
368         default:
369             return false;
370     }
371 }
372 
RemoveTask(TaskType type,const std::string & name)373 void TaskExecutorImpl::RemoveTask(TaskType type, const std::string &name)
374 {
375     switch (type) {
376         case TaskType::PLATFORM:
377             RemoveTaskFromTaskRunner(platformRunner_, name);
378             break;
379         case TaskType::UI:
380             RemoveTaskFromTaskRunner(uiRunner_, name);
381             break;
382         case TaskType::IO:
383             RemoveTaskFromTaskRunner(ioRunner_, name);
384             break;
385         case TaskType::GPU:
386             RemoveTaskFromTaskRunner(gpuRunner_, name);
387             break;
388         case TaskType::JS:
389             RemoveTaskFromTaskRunner(jsRunner_, name);
390             break;
391         case TaskType::BACKGROUND:
392             // background task cannot remove,use ffrt not eventhander
393             break;
394         default:
395             break;
396     }
397 }
398 
RemoveTaskFromTaskRunner(const RefPtr<TaskRunnerAdapter> & taskRunner,const std::string & name)399 void TaskExecutorImpl::RemoveTaskFromTaskRunner(const RefPtr<TaskRunnerAdapter>& taskRunner, const std::string& name)
400 {
401     CHECK_NULL_VOID(taskRunner);
402     taskRunner->RemoveTask(name);
403 }
404 } // namespace OHOS::Ace
405