• 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 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