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