• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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/flutter/flutter_task_executor.h"
17 
18 #include <cerrno>
19 #include <functional>
20 #if !defined(PREVIEW)
21 #ifdef OHOS_STANDARD_SYSTEM
22 #include <sys/prctl.h>
23 #endif
24 #include <sys/resource.h>
25 #endif
26 #include <sys/time.h>
27 #include <unistd.h>
28 
29 #ifdef FML_EMBEDDER_ONLY
30 #undef FML_EMBEDDER_ONLY
31 #define FML_EMBEDDER_ONLY
32 #endif
33 #include "flutter/fml/message_loop.h"
34 #ifdef OHOS_STANDARD_SYSTEM
35 #include "flutter/shell/platform/ohos/platform_task_runner.h"
36 #endif
37 
38 #include "base/log/log.h"
39 #include "base/log/trace_id.h"
40 #include "base/thread/background_task_executor.h"
41 #include "base/utils/utils.h"
42 #include "core/common/container.h"
43 #include "core/common/container_scope.h"
44 
45 namespace OHOS::Ace {
46 namespace {
47 
48 constexpr int32_t GPU_THREAD_PRIORITY = -10;
49 constexpr int32_t UI_THREAD_PRIORITY = -15;
50 
GenJsThreadName()51 inline std::string GenJsThreadName()
52 {
53     static std::atomic<uint32_t> instanceCount { 1 };
54     return std::string("jsThread-") + std::to_string(instanceCount.fetch_add(1, std::memory_order_relaxed));
55 }
56 
WrapTaskWithContainer(TaskExecutor::Task && task,int32_t id,std::function<void ()> && traceIdFunc=nullptr)57 TaskExecutor::Task WrapTaskWithContainer(
58     TaskExecutor::Task&& task, int32_t id, std::function<void()>&& traceIdFunc = nullptr)
59 {
60     auto wrappedTask = [originTask = std::move(task), id, traceId = TraceId::CreateTraceId(),
61                            traceIdFunc = std::move(traceIdFunc)]() {
62         ContainerScope scope(id);
63         std::unique_ptr<TraceId> traceIdPtr(traceId);
64         if (originTask && traceIdPtr) {
65             traceIdPtr->SetTraceId();
66             originTask();
67             traceIdPtr->ClearTraceId();
68         } else {
69             LOGW("WrapTaskWithContainer: originTask or traceIdPtr is null.");
70         }
71         if (traceIdFunc) {
72             traceIdFunc();
73         }
74     };
75     return wrappedTask;
76 }
77 
PostTaskToTaskRunner(const fml::RefPtr<fml::TaskRunner> & taskRunner,TaskExecutor::Task && task,uint32_t delayTime)78 bool PostTaskToTaskRunner(const fml::RefPtr<fml::TaskRunner>& taskRunner, TaskExecutor::Task&& task, uint32_t delayTime)
79 {
80     CHECK_NULL_RETURN_NOLOG(taskRunner, false);
81     CHECK_NULL_RETURN_NOLOG(task, false);
82 
83     if (delayTime > 0) {
84         taskRunner->PostDelayedTask(std::move(task), fml::TimeDelta::FromMilliseconds(delayTime));
85     } else {
86         taskRunner->PostTask(std::move(task));
87     }
88     return true;
89 }
90 
SetThreadPriority(int32_t priority)91 void SetThreadPriority(int32_t priority)
92 {
93 #if !defined(PREVIEW) and !defined(IOS_PLATFORM)
94     if (setpriority(PRIO_PROCESS, gettid(), priority) < 0) {
95         LOGW("Failed to set thread priority, errno = %{private}d", errno);
96     }
97 #endif
98 }
99 
100 } // namespace
101 
FlutterTaskExecutor(const RefPtr<FlutterTaskExecutor> & taskExecutor)102 FlutterTaskExecutor::FlutterTaskExecutor(const RefPtr<FlutterTaskExecutor>& taskExecutor)
103 {
104     jsThread_ = std::make_unique<fml::Thread>(GenJsThreadName());
105     jsRunner_ = jsThread_->GetTaskRunner();
106 
107     platformRunner_ = taskExecutor->platformRunner_;
108     uiRunner_ = taskExecutor->uiRunner_;
109     ioRunner_ = taskExecutor->ioRunner_;
110     gpuRunner_ = taskExecutor->gpuRunner_;
111 }
112 
FlutterTaskExecutor(const flutter::TaskRunners & taskRunners)113 FlutterTaskExecutor::FlutterTaskExecutor(const flutter::TaskRunners& taskRunners)
114 {
115     jsThread_ = std::make_unique<fml::Thread>(GenJsThreadName());
116     jsRunner_ = jsThread_->GetTaskRunner();
117 
118     platformRunner_ = taskRunners.GetPlatformTaskRunner();
119     uiRunner_ = taskRunners.GetUITaskRunner();
120     ioRunner_ = taskRunners.GetIOTaskRunner();
121 #ifdef NG_BUILD
122     gpuRunner_ = taskRunners.GetRasterTaskRunner();
123 #else
124     gpuRunner_ = taskRunners.GetGPUTaskRunner();
125 #endif
126 }
127 
~FlutterTaskExecutor()128 FlutterTaskExecutor::~FlutterTaskExecutor()
129 {
130     // To guarantee the jsThread released in platform thread
131     auto rawPtr = jsThread_.release();
132     PostTaskToTaskRunner(
133         platformRunner_, [rawPtr] { std::unique_ptr<fml::Thread> jsThread(rawPtr); }, 0);
134 }
135 
InitPlatformThread(bool useCurrentEventRunner)136 void FlutterTaskExecutor::InitPlatformThread(bool useCurrentEventRunner)
137 {
138 #ifdef OHOS_STANDARD_SYSTEM
139     platformRunner_ = flutter::PlatformTaskRunner::CurrentTaskRunner(useCurrentEventRunner);
140 #else
141     fml::MessageLoop::EnsureInitializedForCurrentThread();
142     platformRunner_ = fml::MessageLoop::GetCurrent().GetTaskRunner();
143 #endif
144 
145     FillTaskTypeTable(TaskType::PLATFORM);
146 }
147 
InitJsThread(bool newThread)148 void FlutterTaskExecutor::InitJsThread(bool newThread)
149 {
150     if (newThread) {
151         jsThread_ = std::make_unique<fml::Thread>(GenJsThreadName());
152         jsRunner_ = jsThread_->GetTaskRunner();
153     } else {
154         jsRunner_ = uiRunner_;
155     }
156 
157     PostTaskToTaskRunner(
158         jsRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::JS); }, 0);
159 }
160 
InitOtherThreads(const flutter::TaskRunners & taskRunners)161 void FlutterTaskExecutor::InitOtherThreads(const flutter::TaskRunners& taskRunners)
162 {
163     uiRunner_ = taskRunners.GetUITaskRunner();
164     ioRunner_ = taskRunners.GetIOTaskRunner();
165 #ifdef NG_BUILD
166     gpuRunner_ = taskRunners.GetRasterTaskRunner();
167 #else
168     gpuRunner_ = taskRunners.GetGPUTaskRunner();
169 #endif
170 
171     PostTaskToTaskRunner(
172         uiRunner_, [] { SetThreadPriority(UI_THREAD_PRIORITY); }, 0);
173     PostTaskToTaskRunner(
174         gpuRunner_, [] { SetThreadPriority(GPU_THREAD_PRIORITY); }, 0);
175 
176     PostTaskToTaskRunner(
177         uiRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::UI); }, 0);
178     PostTaskToTaskRunner(
179         ioRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::IO); }, 0);
180     PostTaskToTaskRunner(
181         gpuRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::GPU); }, 0);
182 }
183 
OnPostTask(Task && task,TaskType type,uint32_t delayTime) const184 bool FlutterTaskExecutor::OnPostTask(Task&& task, TaskType type, uint32_t delayTime) const
185 {
186     int32_t currentId = Container::CurrentId();
187     auto traceIdFunc = [weak = WeakClaim(const_cast<FlutterTaskExecutor*>(this)), type]() {
188         auto sp = weak.Upgrade();
189         if (sp) {
190             sp->taskIdTable_[static_cast<uint32_t>(type)]++;
191         }
192     };
193     TaskExecutor::Task wrappedTask =
194         currentId >= 0 ? WrapTaskWithContainer(std::move(task), currentId, std::move(traceIdFunc)) : std::move(task);
195 
196     switch (type) {
197         case TaskType::PLATFORM:
198             return PostTaskToTaskRunner(platformRunner_, std::move(wrappedTask), delayTime);
199         case TaskType::UI:
200             return PostTaskToTaskRunner(uiRunner_, std::move(wrappedTask), delayTime);
201         case TaskType::IO:
202             return PostTaskToTaskRunner(ioRunner_, std::move(wrappedTask), delayTime);
203         case TaskType::GPU:
204             return PostTaskToTaskRunner(gpuRunner_, std::move(wrappedTask), delayTime);
205         case TaskType::JS:
206             return PostTaskToTaskRunner(jsRunner_, std::move(wrappedTask), delayTime);
207         case TaskType::BACKGROUND:
208             // Ignore delay time
209             return BackgroundTaskExecutor::GetInstance().PostTask(std::move(wrappedTask));
210         default:
211             return false;
212     }
213 }
214 
WrapTaskWithTraceId(Task && task,int32_t id) const215 TaskExecutor::Task FlutterTaskExecutor::WrapTaskWithTraceId(Task&& task, int32_t id) const
216 {
217     return WrapTaskWithContainer(std::move(task), id);
218 }
219 
WillRunOnCurrentThread(TaskType type) const220 bool FlutterTaskExecutor::WillRunOnCurrentThread(TaskType type) const
221 {
222     switch (type) {
223         case TaskType::PLATFORM:
224             return platformRunner_ ? platformRunner_->RunsTasksOnCurrentThread() : false;
225         case TaskType::UI:
226             return uiRunner_ ? uiRunner_->RunsTasksOnCurrentThread() : 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_ ? jsRunner_->RunsTasksOnCurrentThread() : false;
233         case TaskType::BACKGROUND:
234             // Always return false for background tasks.
235             return false;
236         default:
237             return false;
238     }
239 }
240 
AddTaskObserver(Task && callback)241 void FlutterTaskExecutor::AddTaskObserver(Task&& callback)
242 {
243     fml::MessageLoop::GetCurrent().AddTaskObserver(reinterpret_cast<intptr_t>(this), std::move(callback));
244 }
245 
RemoveTaskObserver()246 void FlutterTaskExecutor::RemoveTaskObserver()
247 {
248     if (!fml::MessageLoop::IsInitializedForCurrentThread()) {
249         return;
250     }
251     fml::MessageLoop::GetCurrent().RemoveTaskObserver(reinterpret_cast<intptr_t>(this));
252 }
253 
254 thread_local TaskExecutor::TaskType FlutterTaskExecutor::localTaskType = TaskExecutor::TaskType::UNKNOWN;
255 
256 #ifdef ACE_DEBUG
TaskTypeToString(TaskExecutor::TaskType type)257 static const char* TaskTypeToString(TaskExecutor::TaskType type)
258 {
259     switch (type) {
260         case TaskExecutor::TaskType::PLATFORM:
261             return "PLATFORM";
262         case TaskExecutor::TaskType::UI:
263             return "UI";
264         case TaskExecutor::TaskType::IO:
265             return "IO";
266         case TaskExecutor::TaskType::GPU:
267             return "GPU";
268         case TaskExecutor::TaskType::JS:
269             return "JS";
270         case TaskExecutor::TaskType::BACKGROUND:
271             return "BACKGROUND";
272         case TaskExecutor::TaskType::UNKNOWN:
273         default:
274             return "UNKNOWN";
275     }
276 }
277 
OnPreSyncTask(TaskType type) const278 bool FlutterTaskExecutor::OnPreSyncTask(TaskType type) const
279 {
280     std::lock_guard<std::mutex> lock(tableMutex_);
281     auto it = taskTypeTable_.find(type);
282     // when task type not filled, just skip
283     if (it == taskTypeTable_.end()) {
284         return true;
285     }
286 
287     auto itSync = syncTaskTable_.find(it->second.threadId);
288     while (itSync != syncTaskTable_.end()) {
289         if (itSync->second == std::this_thread::get_id()) {
290             DumpDeadSyncTask(localTaskType, type);
291             ACE_DCHECK(itSync->second != std::this_thread::get_id() && "DEAD LOCK HAPPENED !!!");
292             return false;
293         }
294 
295         itSync = syncTaskTable_.find(itSync->second);
296     }
297 
298     syncTaskTable_.emplace(std::this_thread::get_id(), it->second.threadId);
299     return true;
300 }
301 
OnPostSyncTask() const302 void FlutterTaskExecutor::OnPostSyncTask() const
303 {
304     std::lock_guard<std::mutex> lock(tableMutex_);
305     syncTaskTable_.erase(std::this_thread::get_id());
306 }
307 
DumpDeadSyncTask(TaskType from,TaskType to) const308 void FlutterTaskExecutor::DumpDeadSyncTask(TaskType from, TaskType to) const
309 {
310     auto itFrom = taskTypeTable_.find(from);
311     auto itTo = taskTypeTable_.find(to);
312 
313     ACE_DCHECK(itFrom != taskTypeTable_.end());
314     ACE_DCHECK(itTo != taskTypeTable_.end());
315 
316     LOGE("DEAD LOCK HAPPEN: %{public}s(%{public}d, %{public}s) -> %{public}s(%{public}d, %{public}s)",
317         TaskTypeToString(from), itFrom->second.tid, itFrom->second.threadName.c_str(), TaskTypeToString(to),
318         itTo->second.tid, itTo->second.threadName.c_str());
319 }
320 #endif
321 
FillTaskTypeTable(TaskType type)322 void FlutterTaskExecutor::FillTaskTypeTable(TaskType type)
323 {
324     constexpr size_t MAX_THREAD_NAME_SIZE = 32;
325     char threadNameBuf[MAX_THREAD_NAME_SIZE] = { 0 };
326     const char* threadName = threadNameBuf;
327 #if !defined(PREVIEW) and !defined(IOS_PLATFORM)
328 #ifdef OHOS_STANDARD_SYSTEM
329     if (prctl(PR_GET_NAME, threadNameBuf) < 0) {
330         threadName = "unknown";
331     }
332 #else
333     if (pthread_getname_np(pthread_self(), threadNameBuf, sizeof(threadNameBuf)) != 0) {
334         threadName = "unknown";
335     }
336 #endif
337 #endif
338 
339     localTaskType = type;
340     ThreadInfo info = {
341         .threadId = std::this_thread::get_id(),
342 #if !defined(PREVIEW) and !defined(IOS_PLATFORM)
343         .tid = gettid(),
344 #endif
345         .threadName = threadName,
346     };
347 
348     std::lock_guard<std::mutex> lock(tableMutex_);
349     taskTypeTable_.emplace(type, info);
350 }
351 
FillTaskTypeTable(const WeakPtr<FlutterTaskExecutor> & weak,TaskType type)352 void FlutterTaskExecutor::FillTaskTypeTable(const WeakPtr<FlutterTaskExecutor>& weak, TaskType type)
353 {
354     auto taskExecutor = weak.Upgrade();
355     if (taskExecutor) {
356         taskExecutor->FillTaskTypeTable(type);
357     }
358 }
359 
360 } // namespace OHOS::Ace
361