• 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 "base/thread/background_task_executor.h"
17 
18 #include <pthread.h>
19 #include <string>
20 #include <functional>
21 
22 #include "base/log/log.h"
23 #include "base/memory/memory_monitor.h"
24 #include "base/thread/frame_trace_adapter.h"
25 
26 namespace OHOS::Ace {
27 namespace {
28 
29 constexpr size_t MAX_BACKGROUND_THREADS = 8;
30 constexpr uint32_t PURGE_FLAG_MASK = (1 << MAX_BACKGROUND_THREADS) - 1;
31 
SetThreadName(uint32_t threadNo)32 void SetThreadName(uint32_t threadNo)
33 {
34     std::string name("ace.bg.");
35     name.append(std::to_string(threadNo));
36 #if defined(MAC_PLATFORM) || defined(IOS_PLATFORM)
37     pthread_setname_np(name.c_str());
38 #else
39     pthread_setname_np(pthread_self(), name.c_str());
40 #endif
41 }
42 
43 } // namespace
44 
GetInstance()45 BackgroundTaskExecutor& BackgroundTaskExecutor::GetInstance()
46 {
47     static BackgroundTaskExecutor instance;
48     return instance;
49 }
50 
BackgroundTaskExecutor()51 BackgroundTaskExecutor::BackgroundTaskExecutor() : maxThreadNum_(MAX_BACKGROUND_THREADS)
52 {
53     FrameTraceAdapter* ft = FrameTraceAdapter::GetInstance();
54     if (ft != nullptr && ft->IsEnabled()) {
55         LOGI("Use frame trace as bg threads pool.");
56     } else {
57         LOGI("Create ace bg threads pool.");
58         if (maxThreadNum_ > 1) {
59             // Start other threads in the first created thread.
60             PostTask([this, num = maxThreadNum_ - 1]() { StartNewThreads(num); });
61         }
62 
63         // Make sure there is at least 1 thread in background thread pool.
64         StartNewThreads(1);
65     }
66 }
67 
~BackgroundTaskExecutor()68 BackgroundTaskExecutor::~BackgroundTaskExecutor()
69 {
70     std::list<std::thread> threads;
71 
72     {
73         std::lock_guard<std::mutex> lock(mutex_);
74         running_ = false;
75         condition_.notify_all();
76         threads = std::move(threads_);
77     }
78 
79     for (auto& threadInPool : threads) {
80         threadInPool.join();
81     }
82 }
83 
PostTask(Task && task,BgTaskPriority priority)84 bool BackgroundTaskExecutor::PostTask(Task&& task, BgTaskPriority priority)
85 {
86     if (!task) {
87         return false;
88     }
89 
90     std::lock_guard<std::mutex> lock(mutex_);
91     if (!running_) {
92         return false;
93     }
94     FrameTraceAdapter* ft = FrameTraceAdapter::GetInstance();
95     if (ft != nullptr && ft->IsEnabled()) {
96         switch (priority) {
97             case BgTaskPriority::LOW:
98                 ft->QuickExecute(std::move(task));
99                 break;
100             default:
101                 ft->SlowExecute(std::move(task));
102                 break;
103         }
104         return true;
105     }
106     switch (priority) {
107         case BgTaskPriority::LOW:
108             lowPriorityTasks_.emplace_back(std::move(task));
109             break;
110         default:
111             tasks_.emplace_back(std::move(task));
112             break;
113     }
114     condition_.notify_one();
115     return true;
116 }
117 
PostTask(const Task & task,BgTaskPriority priority)118 bool BackgroundTaskExecutor::PostTask(const Task& task, BgTaskPriority priority)
119 {
120     if (!task) {
121         return false;
122     }
123 
124     std::lock_guard<std::mutex> lock(mutex_);
125     if (!running_) {
126         return false;
127     }
128     FrameTraceAdapter* ft = FrameTraceAdapter::GetInstance();
129     if (ft != nullptr && ft->IsEnabled()) {
130         Task variableTask = task;
131         switch (priority) {
132             case BgTaskPriority::LOW:
133                 ft->QuickExecute(std::move(variableTask));
134                 break;
135             default:
136                 ft->SlowExecute(std::move(variableTask));
137         }
138         return true;
139     }
140     switch (priority) {
141         case BgTaskPriority::LOW:
142             lowPriorityTasks_.emplace_back(task);
143             break;
144         default:
145             tasks_.emplace_back(task);
146             break;
147     }
148     condition_.notify_one();
149     return true;
150 }
151 
StartNewThreads(size_t num)152 void BackgroundTaskExecutor::StartNewThreads(size_t num)
153 {
154     uint32_t currentThreadNo = 0;
155 
156     {
157         std::lock_guard<std::mutex> lock(mutex_);
158         if (!running_ || currentThreadNum_ >= maxThreadNum_) {
159             return;
160         }
161         if (currentThreadNum_ + num > maxThreadNum_) {
162             num = maxThreadNum_ - currentThreadNum_;
163         }
164         currentThreadNo = currentThreadNum_ + 1;
165         currentThreadNum_ += num;
166     }
167 
168     // Start new threads.
169     std::list<std::thread> newThreads;
170     for (size_t idx = 0; idx < num; ++idx) {
171         newThreads.emplace_back(std::bind(&BackgroundTaskExecutor::ThreadLoop, this, currentThreadNo + idx));
172     }
173 
174     {
175         std::lock_guard<std::mutex> lock(mutex_);
176         if (running_) {
177             threads_.splice(threads_.end(), newThreads);
178         }
179     }
180 
181     for (auto& newThread : newThreads) {
182         // Join the new thread if stop running.
183         if (newThread.joinable()) {
184             newThread.join();
185         }
186     }
187 }
188 
ThreadLoop(uint32_t threadNo)189 void BackgroundTaskExecutor::ThreadLoop(uint32_t threadNo)
190 {
191     LOGD("Background thread is started");
192 
193     SetThreadName(threadNo);
194 
195     Task task;
196     const uint32_t purgeFlag = (1 << (threadNo - 1));
197     std::unique_lock<std::mutex> lock(mutex_);
198     while (running_) {
199         if (tasks_.empty() && lowPriorityTasks_.empty()) {
200             if ((purgeFlags_ & purgeFlag) != purgeFlag) {
201                 condition_.wait(lock);
202                 continue;
203             }
204 
205             lock.unlock();
206             LOGD("Purge malloc cache for background thread %{public}u", threadNo);
207             PurgeMallocCache();
208             lock.lock();
209             purgeFlags_ &= ~purgeFlag;
210             continue;
211         }
212         // deal with tasks_ first. do lowPriorityTasks_ only when all tasks_ done.
213         if (!tasks_.empty()) {
214             task = std::move(tasks_.front());
215             tasks_.pop_front();
216         } else {
217             task = std::move(lowPriorityTasks_.front());
218             lowPriorityTasks_.pop_front();
219         }
220 
221         lock.unlock();
222         // Execute the task and clear after execution.
223         task();
224         task = nullptr;
225         lock.lock();
226     }
227 
228     LOGD("Background thread is stopped");
229 }
230 
TriggerGarbageCollection()231 void BackgroundTaskExecutor::TriggerGarbageCollection()
232 {
233     std::lock_guard<std::mutex> lock(mutex_);
234     purgeFlags_ = PURGE_FLAG_MASK;
235     condition_.notify_all();
236 }
237 
238 } // namespace OHOS::Ace
239