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