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