1 /*
2 * Copyright (C) 2024 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 "thumbnail_generate_worker.h"
17
18 #include <pthread.h>
19
20 #ifdef HAS_BATTERY_MANAGER_PART
21 #include "battery_srv_client.h"
22 #endif
23 #include "medialibrary_errno.h"
24 #include "medialibrary_notify.h"
25 #include "media_log.h"
26 #ifdef HAS_THERMAL_MANAGER_PART
27 #include "thermal_mgr_client.h"
28 #endif
29
30 namespace OHOS {
31 namespace Media {
32 static constexpr int32_t THREAD_NUM_FOREGROUND = 4;
33 static constexpr int32_t THREAD_NUM_BACKGROUND = 2;
34 constexpr size_t TASK_INSERT_COUNT = 15;
35 constexpr size_t CLOSE_THUMBNAIL_WORKER_TIME_INTERVAL = 270000;
36
BeforeExecute()37 void ThumbnailGeneratorWrapper::BeforeExecute()
38 {
39 if (executeParam_ == nullptr) {
40 return;
41 }
42 if (executeParam_->affinity_ != CpuAffinityType::CPU_IDX_DEFAULT) {
43 CpuUtils::SetSelfThreadAffinity(executeParam_->affinity_);
44 }
45 }
46
IsPreconditionFullfilled()47 bool ThumbnailGeneratorWrapper::IsPreconditionFullfilled()
48 {
49 if (executeParam_ == nullptr) {
50 return true;
51 }
52 if (executeParam_->tempLimit_ != -1) {
53 #ifdef HAS_THERMAL_MANAGER_PART
54 auto& thermalMgrClient = PowerMgr::ThermalMgrClient::GetInstance();
55 if (executeParam_->tempLimit_ < static_cast<int32_t>(thermalMgrClient.GetThermalLevel())) {
56 MEDIA_INFO_LOG("task stop because of temp limit %{public}d", executeParam_->tempLimit_);
57 return false;
58 }
59 #endif
60 }
61 if (executeParam_->batteryLimit_ != -1) {
62 #ifdef HAS_BATTERY_MANAGER_PART
63 auto& batteryClient = PowerMgr::BatterySrvClient::GetInstance();
64 if (batteryClient.GetCapacity() < executeParam_->batteryLimit_) {
65 MEDIA_INFO_LOG("task stop because of battery limit %{public}d", executeParam_->batteryLimit_);
66 return false;
67 }
68 #endif
69 }
70 return true;
71 }
72
operator ()(std::shared_ptr<ThumbnailTaskData> & data)73 void ThumbnailGeneratorWrapper::operator() (std::shared_ptr<ThumbnailTaskData> &data)
74 {
75 if (!IsPreconditionFullfilled()) {
76 return;
77 }
78 BeforeExecute();
79 executor_(data);
80 }
81
~ThumbnailGenerateWorker()82 ThumbnailGenerateWorker::~ThumbnailGenerateWorker()
83 {
84 ClearWorkerThreads();
85
86 if (timerId_ != 0) {
87 timer_.Unregister(timerId_);
88 timer_.Shutdown();
89 timerId_ = 0;
90 }
91 }
92
Init(const ThumbnailTaskType & taskType)93 int32_t ThumbnailGenerateWorker::Init(const ThumbnailTaskType &taskType)
94 {
95 std::unique_lock<std::mutex> lock(taskMutex_);
96 if (!threads_.empty()) {
97 return E_OK;
98 }
99
100 MEDIA_INFO_LOG("threads empty, need to init, taskType:%{public}d", taskType);
101 int32_t threadNum;
102 std::string threadName;
103 taskType_ = taskType;
104 CpuAffinityType cpuAffinityType;
105 CpuAffinityType cpuAffinityTypeLowPriority;
106 if (taskType == ThumbnailTaskType::FOREGROUND) {
107 threadNum = THREAD_NUM_FOREGROUND;
108 threadName = THREAD_NAME_FOREGROUND;
109 cpuAffinityType = CpuAffinityType::CPU_IDX_9;
110 cpuAffinityTypeLowPriority = CpuAffinityType::CPU_IDX_3;
111 } else if (taskType == ThumbnailTaskType::BACKGROUND) {
112 threadNum = THREAD_NUM_BACKGROUND;
113 threadName = THREAD_NAME_BACKGROUND;
114 cpuAffinityType = CpuAffinityType::CPU_IDX_9;
115 cpuAffinityTypeLowPriority = CpuAffinityType::CPU_IDX_9;
116 } else {
117 MEDIA_ERR_LOG("invalid task type");
118 return E_ERR;
119 }
120
121 isThreadRunning_ = true;
122 for (auto i = 0; i < threadNum; i++) {
123 std::shared_ptr<ThumbnailGenerateThreadStatus> threadStatus =
124 std::make_shared<ThumbnailGenerateThreadStatus>(i);
125 threadStatus->cpuAffinityType = cpuAffinityType;
126 threadStatus->cpuAffinityTypeLowPriority = cpuAffinityTypeLowPriority;
127 std::thread thread([this, threadStatus] { this->StartWorker(threadStatus); });
128 pthread_setname_np(thread.native_handle(), threadName.c_str());
129 threads_.emplace_back(std::move(thread));
130 threadsStatus_.emplace_back(threadStatus);
131 }
132 lock.unlock();
133 RegisterWorkerTimer();
134 return E_OK;
135 }
136
ReleaseTaskQueue(const ThumbnailTaskPriority & taskPriority)137 int32_t ThumbnailGenerateWorker::ReleaseTaskQueue(const ThumbnailTaskPriority &taskPriority)
138 {
139 if (taskPriority == ThumbnailTaskPriority::HIGH) {
140 highPriorityTaskQueue_.Clear();
141 } else if (taskPriority == ThumbnailTaskPriority::MID) {
142 midPriorityTaskQueue_.Clear();
143 } else if (taskPriority == ThumbnailTaskPriority::LOW) {
144 lowPriorityTaskQueue_.Clear();
145 } else {
146 MEDIA_ERR_LOG("invalid task priority");
147 return E_ERR;
148 }
149 return E_OK;
150 }
151
AddTask(const std::shared_ptr<ThumbnailGenerateTask> & task,const ThumbnailTaskPriority & taskPriority)152 int32_t ThumbnailGenerateWorker::AddTask(
153 const std::shared_ptr<ThumbnailGenerateTask> &task, const ThumbnailTaskPriority &taskPriority)
154 {
155 IncreaseRequestIdTaskNum(task);
156 if (taskPriority == ThumbnailTaskPriority::HIGH) {
157 highPriorityTaskQueue_.Push(task);
158 } else if (taskPriority == ThumbnailTaskPriority::MID) {
159 midPriorityTaskQueue_.Push(task);
160 } else if (taskPriority == ThumbnailTaskPriority::LOW) {
161 lowPriorityTaskQueue_.Push(task);
162 } else {
163 MEDIA_ERR_LOG("invalid task priority");
164 return E_ERR;
165 }
166
167 Init(taskType_);
168 workerCv_.notify_one();
169 return E_OK;
170 }
171
IgnoreTaskByRequestId(int32_t requestId)172 void ThumbnailGenerateWorker::IgnoreTaskByRequestId(int32_t requestId)
173 {
174 if (highPriorityTaskQueue_.Empty() && midPriorityTaskQueue_.Empty() && lowPriorityTaskQueue_.Empty()) {
175 MEDIA_INFO_LOG("task queue empty, no need to ignore task requestId: %{public}d", requestId);
176 return;
177 }
178 ignoreRequestId_.store(requestId);
179
180 std::unique_lock<std::mutex> lock(requestIdMapLock_);
181 requestIdTaskMap_.erase(requestId);
182 MEDIA_INFO_LOG("IgnoreTaskByRequestId, requestId: %{public}d", requestId);
183 }
184
WaitForTask(std::shared_ptr<ThumbnailGenerateThreadStatus> threadStatus)185 bool ThumbnailGenerateWorker::WaitForTask(std::shared_ptr<ThumbnailGenerateThreadStatus> threadStatus)
186 {
187 std::unique_lock<std::mutex> lock(workerLock_);
188 if (highPriorityTaskQueue_.Empty() && midPriorityTaskQueue_.Empty() && lowPriorityTaskQueue_.Empty() &&
189 isThreadRunning_) {
190 ignoreRequestId_ = 0;
191 threadStatus->isThreadWaiting_ = true;
192 bool ret = workerCv_.wait_for(lock, std::chrono::milliseconds(CLOSE_THUMBNAIL_WORKER_TIME_INTERVAL), [this]() {
193 return !isThreadRunning_ || !highPriorityTaskQueue_.Empty() || !midPriorityTaskQueue_.Empty() ||
194 !lowPriorityTaskQueue_.Empty();
195 });
196 if (!ret) {
197 MEDIA_INFO_LOG("Wait for task timeout");
198 return false;
199 }
200 }
201 threadStatus->isThreadWaiting_ = false;
202 return isThreadRunning_;
203 }
204
StartWorker(std::shared_ptr<ThumbnailGenerateThreadStatus> threadStatus)205 void ThumbnailGenerateWorker::StartWorker(std::shared_ptr<ThumbnailGenerateThreadStatus> threadStatus)
206 {
207 std::string name("ThumbnailGenerateWorker");
208 pthread_setname_np(pthread_self(), name.c_str());
209 MEDIA_INFO_LOG("ThumbnailGenerateWorker thread start, taskType:%{public}d, id:%{public}d, "
210 "cpuAffinityType:%{public}d", taskType_, threadStatus->threadId_, threadStatus->cpuAffinityType);
211 while (isThreadRunning_) {
212 if (!WaitForTask(threadStatus)) {
213 continue;
214 }
215 std::shared_ptr<ThumbnailGenerateTask> task;
216 if (!highPriorityTaskQueue_.Empty() && highPriorityTaskQueue_.Pop(task) && task != nullptr) {
217 CpuUtils::SetSelfThreadAffinity(threadStatus->cpuAffinityType);
218 if (NeedIgnoreTask(task->data_->requestId_)) {
219 continue;
220 }
221 task->executor_(task->data_);
222 ++(threadStatus->taskNum_);
223 DecreaseRequestIdTaskNum(task);
224 continue;
225 }
226
227 if (!midPriorityTaskQueue_.Empty() && midPriorityTaskQueue_.Pop(task) && task != nullptr) {
228 CpuUtils::SetSelfThreadAffinity(threadStatus->cpuAffinityType);
229 if (NeedIgnoreTask(task->data_->requestId_)) {
230 continue;
231 }
232 task->executor_(task->data_);
233 ++(threadStatus->taskNum_);
234 DecreaseRequestIdTaskNum(task);
235 continue;
236 }
237
238 if (!lowPriorityTaskQueue_.Empty() && lowPriorityTaskQueue_.Pop(task) && task != nullptr) {
239 CpuUtils::SetSelfThreadAffinity(threadStatus->cpuAffinityTypeLowPriority);
240 if (NeedIgnoreTask(task->data_->requestId_)) {
241 continue;
242 }
243 task->executor_(task->data_);
244 ++(threadStatus->taskNum_);
245 DecreaseRequestIdTaskNum(task);
246 }
247 }
248 MEDIA_INFO_LOG("ThumbnailGenerateWorker thread finish, taskType:%{public}d, id:%{public}d",
249 taskType_, threadStatus->threadId_);
250 }
251
NeedIgnoreTask(int32_t requestId)252 bool ThumbnailGenerateWorker::NeedIgnoreTask(int32_t requestId)
253 {
254 return ignoreRequestId_ != 0 && ignoreRequestId_ == requestId;
255 }
256
IncreaseRequestIdTaskNum(const std::shared_ptr<ThumbnailGenerateTask> & task)257 void ThumbnailGenerateWorker::IncreaseRequestIdTaskNum(const std::shared_ptr<ThumbnailGenerateTask> &task)
258 {
259 if (task == nullptr || task->data_->requestId_ == 0) {
260 // If requestId is 0, no need to manager this task.
261 return;
262 }
263
264 std::unique_lock<std::mutex> lock(requestIdMapLock_);
265 int32_t requestId = task->data_->requestId_;
266 auto pos = requestIdTaskMap_.find(requestId);
267 if (pos == requestIdTaskMap_.end()) {
268 requestIdTaskMap_.insert(std::make_pair(requestId, 1));
269 return;
270 }
271 ++(pos->second);
272 }
273
DecreaseRequestIdTaskNum(const std::shared_ptr<ThumbnailGenerateTask> & task)274 void ThumbnailGenerateWorker::DecreaseRequestIdTaskNum(const std::shared_ptr<ThumbnailGenerateTask> &task)
275 {
276 if (task == nullptr || task->data_->requestId_ == 0) {
277 // If requestId is 0, no need to manager this task.
278 return;
279 }
280
281 std::unique_lock<std::mutex> lock(requestIdMapLock_);
282 int32_t requestId = task->data_->requestId_;
283 auto pos = requestIdTaskMap_.find(requestId);
284 if (pos == requestIdTaskMap_.end()) {
285 return;
286 }
287
288 if (--(pos->second) == 0) {
289 requestIdTaskMap_.erase(requestId);
290 NotifyTaskFinished(requestId);
291 }
292 }
293
NotifyTaskFinished(int32_t requestId)294 void ThumbnailGenerateWorker::NotifyTaskFinished(int32_t requestId)
295 {
296 auto watch = MediaLibraryNotify::GetInstance();
297 if (watch == nullptr) {
298 MEDIA_ERR_LOG("watch is nullptr");
299 return;
300 }
301 std::string notifyUri = PhotoColumn::PHOTO_URI_PREFIX + std::to_string(requestId);
302 watch->Notify(notifyUri, NotifyType::NOTIFY_THUMB_UPDATE);
303 }
304
ClearWorkerThreads()305 void ThumbnailGenerateWorker::ClearWorkerThreads()
306 {
307 {
308 std::unique_lock<std::mutex> lock(workerLock_);
309 isThreadRunning_ = false;
310 }
311 ignoreRequestId_ = 0;
312 workerCv_.notify_all();
313 for (auto &thread : threads_) {
314 if (!thread.joinable()) {
315 continue;
316 }
317 thread.join();
318 }
319 threadsStatus_.clear();
320 threads_.clear();
321 MEDIA_INFO_LOG("Clear ThumbnailGenerateWorker threads successfully, taskType:%{public}d", taskType_);
322 }
323
IsAllThreadWaiting()324 bool ThumbnailGenerateWorker::IsAllThreadWaiting()
325 {
326 std::unique_lock<std::mutex> lock(workerLock_);
327 if (!highPriorityTaskQueue_.Empty() || !midPriorityTaskQueue_.Empty() || !lowPriorityTaskQueue_.Empty()) {
328 MEDIA_INFO_LOG("task queue is not empty, no need to clear worker threads, taskType:%{public}d", taskType_);
329 return false;
330 }
331
332 for (auto &threadStatus : threadsStatus_) {
333 if (!threadStatus->isThreadWaiting_) {
334 MEDIA_INFO_LOG("thread is running, taskType:%{public}d, id:%{public}d, taskNum:%{public}d",
335 taskType_, threadStatus->threadId_, threadStatus->taskNum_);
336 return false;
337 }
338 }
339 isThreadRunning_ = false;
340 return true;
341 }
342
TryClearWorkerThreads()343 void ThumbnailGenerateWorker::TryClearWorkerThreads()
344 {
345 std::unique_lock<std::mutex> lock(taskMutex_);
346 if (!IsAllThreadWaiting()) {
347 return;
348 }
349
350 ClearWorkerThreads();
351 }
352
RegisterWorkerTimer()353 void ThumbnailGenerateWorker::RegisterWorkerTimer()
354 {
355 Utils::Timer::TimerCallback timerCallback = [this]() {
356 MEDIA_INFO_LOG("ThumbnailGenerateWorker timerCallback, ClearWorkerThreads, taskType:%{public}d", taskType_);
357 TryClearWorkerThreads();
358 };
359
360 std::lock_guard<std::mutex> lock(timerMutex_);
361 if (timerId_ == 0) {
362 MEDIA_INFO_LOG("ThumbnailGenerateWorker timer Setup, taskType:%{public}d", taskType_);
363 timer_.Setup();
364 } else {
365 timer_.Unregister(timerId_);
366 }
367
368 timerId_ = timer_.Register(timerCallback, CLOSE_THUMBNAIL_WORKER_TIME_INTERVAL, false);
369 MEDIA_INFO_LOG("ThumbnailGenerateWorker timer Restart, taskType:%{public}d, timeId:%{public}u",
370 taskType_, timerId_);
371 }
372
TryCloseTimer()373 void ThumbnailGenerateWorker::TryCloseTimer()
374 {
375 std::lock_guard<std::mutex> lock(timerMutex_);
376 if (threads_.empty() && timerId_ != 0) {
377 timer_.Unregister(timerId_);
378 timer_.Shutdown();
379 timerId_ = 0;
380 MEDIA_INFO_LOG("ThumbnailGenerateWorker timer Shutdown, taskType:%{public}d", taskType_);
381 }
382 }
383
IsLowerQueueEmpty()384 bool ThumbnailGenerateWorker::IsLowerQueueEmpty()
385 {
386 MEDIA_DEBUG_LOG("lower queue size %{public}d", lowPriorityTaskQueue_.Size());
387 return lowPriorityTaskQueue_.Empty();
388 }
389 } // namespace Media
390 } // namespace OHOS
391