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