• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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