1 /**
2 * @file lv_cache.c
3 *
4 */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_cache.h"
10 #include "../../stdlib/lv_sprintf.h"
11 #include "../lv_assert.h"
12 #include "lv_cache_entry_private.h"
13 #include "lv_cache_private.h"
14 
15 /*********************
16  *      DEFINES
17  *********************/
18 
19 /**********************
20  *      TYPEDEFS
21  **********************/
22 
23 /**********************
24  *  STATIC PROTOTYPES
25  **********************/
26 static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data);
27 static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data);
28 static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data);
29 
30 /**********************
31  *  GLOBAL VARIABLES
32  **********************/
33 
34 /**********************
35  *  STATIC VARIABLES
36  **********************/
37 
38 /**********************
39  *      MACROS
40  **********************/
41 
42 /**********************
43  *   GLOBAL FUNCTIONS
44  **********************/
45 
lv_cache_create(const lv_cache_class_t * cache_class,size_t node_size,size_t max_size,lv_cache_ops_t ops)46 lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class,
47                              size_t node_size, size_t max_size,
48                              lv_cache_ops_t ops)
49 {
50     lv_cache_t * cache = cache_class->alloc_cb();
51     LV_ASSERT_MALLOC(cache);
52 
53     cache->clz = cache_class;
54     cache->node_size = node_size;
55     cache->max_size = max_size;
56     cache->size = 0;
57     cache->ops = ops;
58 
59     if(cache->clz->init_cb(cache) == false) {
60         LV_LOG_ERROR("Cache init failed");
61         lv_free(cache);
62         return NULL;
63     }
64 
65     lv_mutex_init(&cache->lock);
66 
67     return cache;
68 }
69 
lv_cache_destroy(lv_cache_t * cache,void * user_data)70 void lv_cache_destroy(lv_cache_t * cache, void * user_data)
71 {
72     LV_ASSERT_NULL(cache);
73 
74     lv_mutex_lock(&cache->lock);
75     cache->clz->destroy_cb(cache, user_data);
76     lv_mutex_unlock(&cache->lock);
77     lv_mutex_delete(&cache->lock);
78     lv_free(cache);
79 }
80 
lv_cache_acquire(lv_cache_t * cache,const void * key,void * user_data)81 lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data)
82 {
83     LV_ASSERT_NULL(cache);
84     LV_ASSERT_NULL(key);
85 
86     LV_PROFILER_CACHE_BEGIN;
87 
88     lv_mutex_lock(&cache->lock);
89 
90     if(cache->size == 0) {
91         lv_mutex_unlock(&cache->lock);
92 
93         LV_PROFILER_CACHE_END;
94         return NULL;
95     }
96 
97     lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
98     if(entry != NULL) {
99         lv_cache_entry_acquire_data(entry);
100     }
101     lv_mutex_unlock(&cache->lock);
102 
103     LV_PROFILER_CACHE_END;
104     return entry;
105 }
lv_cache_release(lv_cache_t * cache,lv_cache_entry_t * entry,void * user_data)106 void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data)
107 {
108     LV_ASSERT_NULL(entry);
109 
110     LV_PROFILER_CACHE_BEGIN;
111 
112     lv_mutex_lock(&cache->lock);
113     lv_cache_entry_release_data(entry, user_data);
114 
115     if(lv_cache_entry_get_ref(entry) == 0 && lv_cache_entry_is_invalid(entry)) {
116         cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
117         lv_cache_entry_delete(entry);
118     }
119     lv_mutex_unlock(&cache->lock);
120 
121     LV_PROFILER_CACHE_END;
122 }
lv_cache_add(lv_cache_t * cache,const void * key,void * user_data)123 lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data)
124 {
125     LV_ASSERT_NULL(cache);
126     LV_ASSERT_NULL(key);
127 
128     LV_PROFILER_CACHE_BEGIN;
129 
130     lv_mutex_lock(&cache->lock);
131     if(cache->max_size == 0) {
132         lv_mutex_unlock(&cache->lock);
133 
134         LV_PROFILER_CACHE_END;
135         return NULL;
136     }
137 
138     lv_cache_entry_t * entry = cache_add_internal_no_lock(cache, key, user_data);
139     if(entry != NULL) {
140         lv_cache_entry_acquire_data(entry);
141     }
142     lv_mutex_unlock(&cache->lock);
143 
144     LV_PROFILER_CACHE_END;
145     return entry;
146 }
lv_cache_acquire_or_create(lv_cache_t * cache,const void * key,void * user_data)147 lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data)
148 {
149     LV_ASSERT_NULL(cache);
150     LV_ASSERT_NULL(key);
151 
152     LV_PROFILER_CACHE_BEGIN;
153 
154     lv_mutex_lock(&cache->lock);
155     lv_cache_entry_t * entry = NULL;
156 
157     if(cache->size != 0) {
158         entry = cache->clz->get_cb(cache, key, user_data);
159         if(entry != NULL) {
160             lv_cache_entry_acquire_data(entry);
161             lv_mutex_unlock(&cache->lock);
162 
163             LV_PROFILER_CACHE_END;
164             return entry;
165         }
166     }
167 
168     if(cache->max_size == 0) {
169         lv_mutex_unlock(&cache->lock);
170 
171         LV_PROFILER_CACHE_END;
172         return NULL;
173     }
174 
175     entry = cache_add_internal_no_lock(cache, key, user_data);
176     if(entry == NULL) {
177         lv_mutex_unlock(&cache->lock);
178 
179         LV_PROFILER_CACHE_END;
180         return NULL;
181     }
182     bool create_res = cache->ops.create_cb(lv_cache_entry_get_data(entry), user_data);
183     if(create_res == false) {
184         cache->clz->remove_cb(cache, entry, user_data);
185         lv_cache_entry_delete(entry);
186         entry = NULL;
187     }
188     else {
189         lv_cache_entry_acquire_data(entry);
190     }
191     lv_mutex_unlock(&cache->lock);
192 
193     LV_PROFILER_CACHE_END;
194     return entry;
195 }
lv_cache_reserve(lv_cache_t * cache,uint32_t reserved_size,void * user_data)196 void lv_cache_reserve(lv_cache_t * cache, uint32_t reserved_size, void * user_data)
197 {
198     LV_ASSERT_NULL(cache);
199 
200     LV_PROFILER_CACHE_BEGIN;
201 
202     for(lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data);
203         reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM;
204         reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data))
205         cache_evict_one_internal_no_lock(cache, user_data);
206 
207     LV_PROFILER_CACHE_END;
208 }
lv_cache_drop(lv_cache_t * cache,const void * key,void * user_data)209 void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data)
210 {
211     LV_ASSERT_NULL(cache);
212     LV_ASSERT_NULL(key);
213 
214     LV_PROFILER_CACHE_BEGIN;
215 
216     lv_mutex_lock(&cache->lock);
217     cache_drop_internal_no_lock(cache, key, user_data);
218     lv_mutex_unlock(&cache->lock);
219 
220     LV_PROFILER_CACHE_END;
221 }
lv_cache_evict_one(lv_cache_t * cache,void * user_data)222 bool lv_cache_evict_one(lv_cache_t * cache, void * user_data)
223 {
224     LV_ASSERT_NULL(cache);
225 
226     LV_PROFILER_CACHE_BEGIN;
227 
228     lv_mutex_lock(&cache->lock);
229     bool res = cache_evict_one_internal_no_lock(cache, user_data);
230     lv_mutex_unlock(&cache->lock);
231 
232     LV_PROFILER_CACHE_END;
233     return res;
234 }
lv_cache_drop_all(lv_cache_t * cache,void * user_data)235 void lv_cache_drop_all(lv_cache_t * cache, void * user_data)
236 {
237     LV_ASSERT_NULL(cache);
238 
239     LV_PROFILER_CACHE_BEGIN;
240 
241     lv_mutex_lock(&cache->lock);
242     cache->clz->drop_all_cb(cache, user_data);
243     lv_mutex_unlock(&cache->lock);
244 
245     LV_PROFILER_CACHE_END;
246 }
247 
lv_cache_set_max_size(lv_cache_t * cache,size_t max_size,void * user_data)248 void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data)
249 {
250     LV_UNUSED(user_data);
251     cache->max_size = max_size;
252 }
lv_cache_get_max_size(lv_cache_t * cache,void * user_data)253 size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data)
254 {
255     LV_UNUSED(user_data);
256     return cache->max_size;
257 }
lv_cache_get_size(lv_cache_t * cache,void * user_data)258 size_t lv_cache_get_size(lv_cache_t * cache, void * user_data)
259 {
260     LV_UNUSED(user_data);
261     return cache->size;
262 }
lv_cache_get_free_size(lv_cache_t * cache,void * user_data)263 size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data)
264 {
265     LV_UNUSED(user_data);
266     return cache->max_size - cache->size;
267 }
lv_cache_is_enabled(lv_cache_t * cache)268 bool lv_cache_is_enabled(lv_cache_t * cache)
269 {
270     return cache->max_size > 0;
271 }
lv_cache_set_compare_cb(lv_cache_t * cache,lv_cache_compare_cb_t compare_cb,void * user_data)272 void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data)
273 {
274     LV_UNUSED(user_data);
275     cache->ops.compare_cb = compare_cb;
276 }
lv_cache_set_create_cb(lv_cache_t * cache,lv_cache_create_cb_t alloc_cb,void * user_data)277 void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data)
278 {
279     LV_UNUSED(user_data);
280     cache->ops.create_cb = alloc_cb;
281 }
lv_cache_set_free_cb(lv_cache_t * cache,lv_cache_free_cb_t free_cb,void * user_data)282 void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data)
283 {
284     LV_UNUSED(user_data);
285     cache->ops.free_cb = free_cb;
286 }
lv_cache_set_name(lv_cache_t * cache,const char * name)287 void lv_cache_set_name(lv_cache_t * cache, const char * name)
288 {
289     if(cache == NULL) return;
290     cache->name = name;
291 }
lv_cache_get_name(lv_cache_t * cache)292 const char * lv_cache_get_name(lv_cache_t * cache)
293 {
294     return cache->name;
295 }
296 
lv_cache_iter_create(lv_cache_t * cache)297 lv_iter_t * lv_cache_iter_create(lv_cache_t * cache)
298 {
299     LV_ASSERT_NULL(cache);
300     if(cache == NULL || cache->clz->iter_create_cb == NULL) return NULL;
301     return cache->clz->iter_create_cb(cache);
302 }
303 
304 /**********************
305  *   STATIC FUNCTIONS
306  **********************/
307 
cache_drop_internal_no_lock(lv_cache_t * cache,const void * key,void * user_data)308 static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data)
309 {
310     lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data);
311     if(entry == NULL) {
312         return;
313     }
314 
315     if(lv_cache_entry_get_ref(entry) == 0) {
316         cache->clz->remove_cb(cache, entry, user_data);
317         cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data);
318         lv_cache_entry_delete(entry);
319     }
320     else {
321         lv_cache_entry_set_invalid(entry, true);
322         cache->clz->remove_cb(cache, entry, user_data);
323     }
324 }
325 
cache_evict_one_internal_no_lock(lv_cache_t * cache,void * user_data)326 static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data)
327 {
328     lv_cache_entry_t * victim = cache->clz->get_victim_cb(cache, user_data);
329 
330     if(victim == NULL) {
331         LV_LOG_ERROR("No victim found");
332         return false;
333     }
334 
335     cache->clz->remove_cb(cache, victim, user_data);
336     cache->ops.free_cb(lv_cache_entry_get_data(victim), user_data);
337     lv_cache_entry_delete(victim);
338     return true;
339 }
340 
cache_add_internal_no_lock(lv_cache_t * cache,const void * key,void * user_data)341 static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data)
342 {
343     lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data);
344     if(reserve_cond_res == LV_CACHE_RESERVE_COND_TOO_LARGE) {
345         LV_LOG_ERROR("data %p is too large that exceeds max size (%" LV_PRIu32 ")", key, cache->max_size);
346         return NULL;
347     }
348 
349     for(; reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM;
350         reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data))
351         if(cache_evict_one_internal_no_lock(cache, user_data) == false)
352             return NULL;
353 
354     lv_cache_entry_t * entry = cache->clz->add_cb(cache, key, user_data);
355 
356     return entry;
357 }
358