/** * @file lv_cache.c * */ /********************* * INCLUDES *********************/ #include "lv_cache.h" #include "../../stdlib/lv_sprintf.h" #include "../lv_assert.h" #include "lv_cache_entry_private.h" /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data); static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data); static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data); /********************** * GLOBAL VARIABLES **********************/ /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ lv_cache_t * lv_cache_create(const lv_cache_class_t * cache_class, size_t node_size, size_t max_size, lv_cache_ops_t ops) { lv_cache_t * cache = cache_class->alloc_cb(); LV_ASSERT_MALLOC(cache); cache->clz = cache_class; cache->node_size = node_size; cache->max_size = max_size; cache->size = 0; cache->ops = ops; if(cache->clz->init_cb(cache) == false) { LV_LOG_ERROR("Cache init failed"); lv_free(cache); return NULL; } lv_mutex_init(&cache->lock); return cache; } void lv_cache_destroy(lv_cache_t * cache, void * user_data) { LV_ASSERT_NULL(cache); lv_mutex_lock(&cache->lock); cache->clz->destroy_cb(cache, user_data); lv_mutex_unlock(&cache->lock); lv_mutex_delete(&cache->lock); lv_free(cache); } lv_cache_entry_t * lv_cache_acquire(lv_cache_t * cache, const void * key, void * user_data) { LV_ASSERT_NULL(cache); LV_ASSERT_NULL(key); LV_PROFILER_BEGIN; lv_mutex_lock(&cache->lock); if(cache->size == 0) { lv_mutex_unlock(&cache->lock); LV_PROFILER_END; return NULL; } lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data); if(entry != NULL) { lv_cache_entry_acquire_data(entry); } lv_mutex_unlock(&cache->lock); LV_PROFILER_END; return entry; } void lv_cache_release(lv_cache_t * cache, lv_cache_entry_t * entry, void * user_data) { LV_ASSERT_NULL(entry); LV_PROFILER_BEGIN; lv_mutex_lock(&cache->lock); lv_cache_entry_release_data(entry, user_data); if(lv_cache_entry_get_ref(entry) == 0 && lv_cache_entry_is_invalid(entry)) { cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data); lv_cache_entry_delete(entry); } lv_mutex_unlock(&cache->lock); LV_PROFILER_END; } lv_cache_entry_t * lv_cache_add(lv_cache_t * cache, const void * key, void * user_data) { LV_ASSERT_NULL(cache); LV_ASSERT_NULL(key); LV_PROFILER_BEGIN; lv_mutex_lock(&cache->lock); if(cache->max_size == 0) { lv_mutex_unlock(&cache->lock); LV_PROFILER_END; return NULL; } lv_cache_entry_t * entry = cache_add_internal_no_lock(cache, key, user_data); if(entry != NULL) { lv_cache_entry_acquire_data(entry); } lv_mutex_unlock(&cache->lock); LV_PROFILER_END; return entry; } lv_cache_entry_t * lv_cache_acquire_or_create(lv_cache_t * cache, const void * key, void * user_data) { LV_ASSERT_NULL(cache); LV_ASSERT_NULL(key); LV_PROFILER_BEGIN; lv_mutex_lock(&cache->lock); lv_cache_entry_t * entry = NULL; if(cache->size != 0) { entry = cache->clz->get_cb(cache, key, user_data); if(entry != NULL) { lv_cache_entry_acquire_data(entry); lv_mutex_unlock(&cache->lock); LV_PROFILER_END; return entry; } } if(cache->max_size == 0) { lv_mutex_unlock(&cache->lock); LV_PROFILER_END; return NULL; } entry = cache_add_internal_no_lock(cache, key, user_data); if(entry == NULL) { lv_mutex_unlock(&cache->lock); LV_PROFILER_END; return NULL; } bool create_res = cache->ops.create_cb(lv_cache_entry_get_data(entry), user_data); if(create_res == false) { cache->clz->remove_cb(cache, entry, user_data); lv_cache_entry_delete(entry); entry = NULL; } else { lv_cache_entry_acquire_data(entry); } lv_mutex_unlock(&cache->lock); LV_PROFILER_END; return entry; } void lv_cache_reserve(lv_cache_t * cache, uint32_t reserved_size, void * user_data) { LV_ASSERT_NULL(cache); LV_PROFILER_BEGIN; for(lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data); reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM; reserve_cond_res = cache->clz->reserve_cond_cb(cache, NULL, reserved_size, user_data)) cache_evict_one_internal_no_lock(cache, user_data); LV_PROFILER_END; } void lv_cache_drop(lv_cache_t * cache, const void * key, void * user_data) { LV_ASSERT_NULL(cache); LV_ASSERT_NULL(key); LV_PROFILER_BEGIN; lv_mutex_lock(&cache->lock); cache_drop_internal_no_lock(cache, key, user_data); lv_mutex_unlock(&cache->lock); LV_PROFILER_END; } bool lv_cache_evict_one(lv_cache_t * cache, void * user_data) { LV_ASSERT_NULL(cache); LV_PROFILER_BEGIN; lv_mutex_lock(&cache->lock); bool res = cache_evict_one_internal_no_lock(cache, user_data); lv_mutex_unlock(&cache->lock); LV_PROFILER_END; return res; } void lv_cache_drop_all(lv_cache_t * cache, void * user_data) { LV_ASSERT_NULL(cache); LV_PROFILER_BEGIN; lv_mutex_lock(&cache->lock); cache->clz->drop_all_cb(cache, user_data); lv_mutex_unlock(&cache->lock); LV_PROFILER_END; } void lv_cache_set_max_size(lv_cache_t * cache, size_t max_size, void * user_data) { LV_UNUSED(user_data); cache->max_size = max_size; } size_t lv_cache_get_max_size(lv_cache_t * cache, void * user_data) { LV_UNUSED(user_data); return cache->max_size; } size_t lv_cache_get_size(lv_cache_t * cache, void * user_data) { LV_UNUSED(user_data); return cache->size; } size_t lv_cache_get_free_size(lv_cache_t * cache, void * user_data) { LV_UNUSED(user_data); return cache->max_size - cache->size; } bool lv_cache_is_enabled(lv_cache_t * cache) { return cache->max_size > 0; } void lv_cache_set_compare_cb(lv_cache_t * cache, lv_cache_compare_cb_t compare_cb, void * user_data) { LV_UNUSED(user_data); cache->ops.compare_cb = compare_cb; } void lv_cache_set_create_cb(lv_cache_t * cache, lv_cache_create_cb_t alloc_cb, void * user_data) { LV_UNUSED(user_data); cache->ops.create_cb = alloc_cb; } void lv_cache_set_free_cb(lv_cache_t * cache, lv_cache_free_cb_t free_cb, void * user_data) { LV_UNUSED(user_data); cache->ops.free_cb = free_cb; } void lv_cache_set_name(lv_cache_t * cache, const char * name) { if(cache == NULL) return; cache->name = name; } const char * lv_cache_get_name(lv_cache_t * cache) { return cache->name; } /********************** * STATIC FUNCTIONS **********************/ static void cache_drop_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data) { lv_cache_entry_t * entry = cache->clz->get_cb(cache, key, user_data); if(entry == NULL) { return; } if(lv_cache_entry_get_ref(entry) == 0) { cache->clz->remove_cb(cache, entry, user_data); cache->ops.free_cb(lv_cache_entry_get_data(entry), user_data); lv_cache_entry_delete(entry); } else { lv_cache_entry_set_invalid(entry, true); cache->clz->remove_cb(cache, entry, user_data); } } static bool cache_evict_one_internal_no_lock(lv_cache_t * cache, void * user_data) { lv_cache_entry_t * victim = cache->clz->get_victim_cb(cache, user_data); if(victim == NULL) { LV_LOG_ERROR("No victim found"); return false; } cache->clz->remove_cb(cache, victim, user_data); cache->ops.free_cb(lv_cache_entry_get_data(victim), user_data); lv_cache_entry_delete(victim); return true; } static lv_cache_entry_t * cache_add_internal_no_lock(lv_cache_t * cache, const void * key, void * user_data) { lv_cache_reserve_cond_res_t reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data); if(reserve_cond_res == LV_CACHE_RESERVE_COND_TOO_LARGE) { LV_LOG_ERROR("data %p is too large that exceeds max size (%" LV_PRIu32 ")", key, cache->max_size); return NULL; } for(; reserve_cond_res == LV_CACHE_RESERVE_COND_NEED_VICTIM; reserve_cond_res = cache->clz->reserve_cond_cb(cache, key, 0, user_data)) if(cache_evict_one_internal_no_lock(cache, user_data) == false) return NULL; lv_cache_entry_t * entry = cache->clz->add_cb(cache, key, user_data); return entry; }