• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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