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