• 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 #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