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