1 /*
2 * Copyright (c) 2021-2023 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 "core/image/image_cache.h"
17
18 #include "base/log/dump_log.h"
19 #include "core/components_ng/image_provider/image_object.h"
20 #include "core/image/image_object.h"
21
22 namespace OHOS::Ace {
23 namespace {
24 constexpr uint64_t MAX_WAITING_TIME = 1000; // 1000ms
25 }
Create()26 RefPtr<ImageCache> ImageCache::Create()
27 {
28 return MakeRefPtr<ImageCache>();
29 }
30
31 // TODO: Create a real ImageCache later
32 #ifdef FLUTTER_2_5
33 class MockImageCache : public ImageCache {
Clear()34 void Clear() override {};
GetDataFromCacheFile(const std::string & filePath)35 RefPtr<NG::ImageData> GetDataFromCacheFile(const std::string& filePath) override
36 {
37 return nullptr;
38 }
39 };
40
Create()41 RefPtr<ImageCache> ImageCache::Create()
42 {
43 return AceType::MakeRefPtr<MockImageCache>();
44 }
45
Purge()46 void ImageCache::Purge() {}
47 #endif
48
CacheImage(const std::string & key,const std::shared_ptr<CachedImage> & image)49 void ImageCache::CacheImage(const std::string& key, const std::shared_ptr<CachedImage>& image)
50 {
51 if (key.empty() || capacity_ == 0) {
52 return;
53 }
54 std::scoped_lock lock(imageCacheMutex_);
55 CountLimitLRU::CacheWithCountLimitLRU<std::shared_ptr<CachedImage>>(key, image, cacheList_, imageCache_, capacity_);
56 }
57
GetCacheImage(const std::string & key)58 std::shared_ptr<CachedImage> ImageCache::GetCacheImage(const std::string& key)
59 {
60 std::scoped_lock lock(imageCacheMutex_);
61 return CountLimitLRU::GetCacheObjWithCountLimitLRU<std::shared_ptr<CachedImage>>(key, cacheList_, imageCache_);
62 }
63
CacheImgObjNG(const std::string & key,const RefPtr<NG::ImageObject> & imgObj)64 void ImageCache::CacheImgObjNG(const std::string& key, const RefPtr<NG::ImageObject>& imgObj)
65 {
66 if (key.empty() || imgObjCapacity_ == 0) {
67 return;
68 }
69 std::scoped_lock lock(imgObjMutex_);
70 CountLimitLRU::CacheWithCountLimitLRU<RefPtr<NG::ImageObject>>(
71 key, imgObj, cacheImgObjListNG_, imgObjCacheNG_, imgObjCapacity_);
72 }
73
GetCacheImgObjNG(const std::string & key)74 RefPtr<NG::ImageObject> ImageCache::GetCacheImgObjNG(const std::string& key)
75 {
76 std::scoped_lock lock(imgObjMutex_);
77 return CountLimitLRU::GetCacheObjWithCountLimitLRU<RefPtr<NG::ImageObject>>(
78 key, cacheImgObjListNG_, imgObjCacheNG_);
79 }
80
CacheImgObj(const std::string & key,const RefPtr<ImageObject> & imgObj)81 void ImageCache::CacheImgObj(const std::string& key, const RefPtr<ImageObject>& imgObj)
82 {
83 if (key.empty() || imgObjCapacity_ == 0) {
84 return;
85 }
86 std::scoped_lock lock(imgObjMutex_);
87 CountLimitLRU::CacheWithCountLimitLRU<RefPtr<ImageObject>>(
88 key, imgObj, cacheImgObjList_, imgObjCache_, imgObjCapacity_);
89 }
90
GetCacheImgObj(const std::string & key)91 RefPtr<ImageObject> ImageCache::GetCacheImgObj(const std::string& key)
92 {
93 std::scoped_lock lock(imgObjMutex_);
94 return CountLimitLRU::GetCacheObjWithCountLimitLRU<RefPtr<ImageObject>>(key, cacheImgObjList_, imgObjCache_);
95 }
96
CacheImageData(const std::string & key,const RefPtr<NG::ImageData> & imageData)97 void ImageCache::CacheImageData(const std::string& key, const RefPtr<NG::ImageData>& imageData)
98 {
99 if (key.empty() || !imageData || dataSizeLimit_ == 0) {
100 return;
101 }
102 ACE_SCOPED_TRACE("CacheImageData key:%s", key.c_str());
103 auto dataSize = imageData->GetSize();
104 std::vector<CacheNode<RefPtr<NG::ImageData>>> needErase;
105
106 // Try to acquire the dataCacheMutex_ lock within MAX_WAITING_TIME milliseconds to avoid long blocking.
107 if (!dataCacheMutex_.try_lock_for(std::chrono::milliseconds(MAX_WAITING_TIME))) {
108 TAG_LOGW(AceLogTag::ACE_IMAGE,
109 "Failed to acquire mutex within %{public}" PRIu64 "milliseconds, proceeding without cache access.",
110 MAX_WAITING_TIME);
111 return;
112 }
113 // Adopt the already acquired lock
114 std::scoped_lock lock(std::adopt_lock, dataCacheMutex_);
115 auto iter = imageDataCache_.find(key);
116 bool inCache = (iter != imageDataCache_.end());
117 bool largerHalfSize = dataSize > (dataSizeLimit_ >> 1);
118 size_t oldSize = !inCache ? 0 : iter->second->cacheObj->GetSize();
119 if (largerHalfSize && inCache) {
120 // if data is longer than half limit, do not cache it.
121 // and if the key is in Cache, erase it.
122 curDataSize_ -= oldSize;
123 needErase.push_back(*(iter->second));
124 dataCacheList_.erase(iter->second);
125 imageDataCache_.erase(key);
126 TAG_LOGW(AceLogTag::ACE_IMAGE, "data is %{public}d, bigger than half limit %{public}d, do not cache it",
127 static_cast<int32_t>(dataSize), static_cast<int32_t>(dataSizeLimit_ >> 1));
128 } else if (!largerHalfSize && !inCache && ProcessImageDataCacheInner(dataSize, needErase)) {
129 dataCacheList_.emplace_front(key, imageData);
130 imageDataCache_.emplace(key, dataCacheList_.begin());
131 } else if (!largerHalfSize && inCache && oldSize >= dataSize) {
132 // if the image is in the cache, and dataSize <= oldSize, we can replace the imageData in cache.
133 curDataSize_ = curDataSize_ + dataSize - oldSize;
134 iter->second->cacheObj = imageData;
135 dataCacheList_.splice(dataCacheList_.begin(), dataCacheList_, iter->second);
136 iter->second = dataCacheList_.begin();
137 } else if (!largerHalfSize && inCache && oldSize < dataSize) {
138 // if the image is in the cache, and dataSize > oldSize, we erase the old one, the try to cache the new image.
139 curDataSize_ -= oldSize;
140 needErase.push_back(*(iter->second));
141 dataCacheList_.erase(iter->second);
142 imageDataCache_.erase(key);
143 if (ProcessImageDataCacheInner(dataSize, needErase)) {
144 dataCacheList_.emplace_front(key, imageData);
145 imageDataCache_.emplace(key, dataCacheList_.begin());
146 }
147 }
148 }
149
ProcessImageDataCacheInner(size_t dataSize,std::vector<CacheNode<RefPtr<NG::ImageData>>> & needErase)150 bool ImageCache::ProcessImageDataCacheInner(size_t dataSize, std::vector<CacheNode<RefPtr<NG::ImageData>>>& needErase)
151 {
152 while (dataSize + curDataSize_ > dataSizeLimit_ && !dataCacheList_.empty()) {
153 curDataSize_ -= dataCacheList_.back().cacheObj->GetSize();
154 needErase.push_back(dataCacheList_.back());
155 imageDataCache_.erase(dataCacheList_.back().cacheKey);
156 dataCacheList_.pop_back();
157 }
158 if (dataSize + curDataSize_ > dataSizeLimit_) {
159 return false;
160 }
161 curDataSize_ += dataSize;
162 return true;
163 }
164
GetCacheImageData(const std::string & key)165 RefPtr<NG::ImageData> ImageCache::GetCacheImageData(const std::string& key)
166 {
167 ACE_SCOPED_TRACE("GetCacheImageData key:%s", key.c_str());
168 // Try to acquire the dataCacheMutex_ lock within MAX_WAITING_TIME milliseconds to avoid long blocking.
169 if (!dataCacheMutex_.try_lock_for(std::chrono::milliseconds(MAX_WAITING_TIME))) {
170 TAG_LOGW(AceLogTag::ACE_IMAGE,
171 "Failed to acquire mutex within %{public}" PRIu64 "milliseconds, proceeding without cache access.",
172 MAX_WAITING_TIME);
173 return nullptr;
174 }
175 // Adopt the already acquired lock
176 std::scoped_lock lock(std::adopt_lock, dataCacheMutex_);
177 auto iter = imageDataCache_.find(key);
178 if (iter != imageDataCache_.end()) {
179 dataCacheList_.splice(dataCacheList_.begin(), dataCacheList_, iter->second);
180 iter->second = dataCacheList_.begin();
181 return iter->second->cacheObj;
182 }
183 return nullptr;
184 }
185
ClearCacheImage(const std::string & key)186 void ImageCache::ClearCacheImage(const std::string& key)
187 {
188 ACE_SCOPED_TRACE("ClearCacheImage key:%s", key.c_str());
189 {
190 std::scoped_lock lock(imageCacheMutex_);
191 auto iter = imageCache_.find(key);
192 if (iter != imageCache_.end()) {
193 cacheList_.erase(iter->second);
194 imageCache_.erase(iter);
195 }
196 }
197
198 {
199 // Try to acquire the dataCacheMutex_ lock within MAX_WAITING_TIME milliseconds to avoid long blocking.
200 if (!dataCacheMutex_.try_lock_for(std::chrono::milliseconds(MAX_WAITING_TIME))) {
201 TAG_LOGW(AceLogTag::ACE_IMAGE,
202 "Failed to acquire mutex within %{public}" PRIu64 "milliseconds, proceeding without cache access.",
203 MAX_WAITING_TIME);
204 return;
205 }
206 // Adopt the already acquired lock
207 std::scoped_lock lock(std::adopt_lock, dataCacheMutex_);
208 auto iter = imageDataCache_.find(key);
209 if (iter != imageDataCache_.end()) {
210 dataCacheList_.erase(iter->second);
211 imageDataCache_.erase(iter);
212 }
213 }
214 }
215
Clear()216 void ImageCache::Clear()
217 {
218 ACE_SCOPED_TRACE("ImageCache Clear");
219 {
220 std::scoped_lock lock(imageCacheMutex_);
221 cacheList_.clear();
222 imageCache_.clear();
223 }
224 {
225 // Try to acquire the dataCacheMutex_ lock within MAX_WAITING_TIME milliseconds to avoid long blocking.
226 if (!dataCacheMutex_.try_lock_for(std::chrono::milliseconds(MAX_WAITING_TIME))) {
227 TAG_LOGW(AceLogTag::ACE_IMAGE,
228 "Failed to acquire mutex within %{public}" PRIu64 "milliseconds, proceeding without cache access.",
229 MAX_WAITING_TIME);
230 return;
231 }
232 // Adopt the already acquired lock
233 std::scoped_lock lock(std::adopt_lock, dataCacheMutex_);
234 dataCacheList_.clear();
235 imageDataCache_.clear();
236 }
237 {
238 std::scoped_lock lock(imgObjMutex_);
239 cacheImgObjListNG_.clear();
240 imgObjCacheNG_.clear();
241 cacheImgObjList_.clear();
242 imgObjCache_.clear();
243 }
244 }
245
DumpCacheInfo()246 void ImageCache::DumpCacheInfo()
247 {
248 auto cacheSize = dataCacheList_.size();
249 auto capacity = static_cast<int32_t>(capacity_);
250 auto dataSizeLimit = static_cast<int32_t>(dataSizeLimit_);
251 DumpLog::GetInstance().Print("------------ImageCacheInfo------------");
252 DumpLog::GetInstance().Print("User set ImageRawDataCacheSize : " + std::to_string(dataSizeLimit) + "(B)" +
253 ", ImageCacheCount :" + std::to_string(capacity) + "(number)");
254 DumpLog::GetInstance().Print("Cache count: " + std::to_string(cacheSize));
255 if (cacheSize == 0) {
256 return;
257 }
258 auto totalCount = 0;
259 for (const auto& item : dataCacheList_) {
260 auto imageObj = item.cacheObj;
261 auto key = item.cacheKey;
262 std::string srcStr = "NA";
263 for (const auto& cacheImageObj : cacheImgObjListNG_) {
264 if (cacheImageObj.cacheKey == key) {
265 srcStr = cacheImageObj.cacheObj->GetSourceInfo().ToString();
266 break;
267 }
268 }
269 totalCount += static_cast<int32_t>(imageObj->GetSize());
270 DumpLog::GetInstance().Print("Cache Obj of key: " + key + ", src:" + srcStr + "," + imageObj->ToString());
271 }
272 DumpLog::GetInstance().Print("Cache total size: " + std::to_string(totalCount));
273 }
274 } // namespace OHOS::Ace
275