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