/* SPDX-License-Identifier: Apache-2.0 OR MIT */ /* * Copyright (c) 2021 Rockchip Electronics Co., Ltd. */ #define MODULE_TAG "mpp_mem_pool" #include #include "mpp_env.h" #include "mpp_mem.h" #include "mpp_list.h" #include "mpp_debug.h" #include "mpp_singleton.h" #include "mpp_mem_pool.h" #define MEM_POOL_DBG_FLOW (0x00000001) #define MEM_POOL_DBG_EXIT (0x00000002) #define mem_pool_dbg(flag, fmt, ...) _mpp_dbg(mpp_mem_pool_debug, flag, fmt, ## __VA_ARGS__) #define mem_pool_dbg_f(flag, fmt, ...) _mpp_dbg_f(mpp_mem_pool_debug, flag, fmt, ## __VA_ARGS__) #define mem_pool_dbg_flow(fmt, ...) mem_pool_dbg(MEM_POOL_DBG_FLOW, fmt, ## __VA_ARGS__) #define mem_pool_dbg_exit(fmt, ...) mem_pool_dbg(MEM_POOL_DBG_EXIT, fmt, ## __VA_ARGS__) #define get_srv_mem_pool(caller) \ ({ \ MppMemPoolSrv *__tmp; \ if (!srv_mem_pool) { \ mem_pool_srv_init(); \ } \ if (srv_mem_pool) { \ __tmp = srv_mem_pool; \ } else { \ mpp_err("mpp mem pool srv not init at %s : %s\n", __FUNCTION__, caller); \ __tmp = NULL; \ } \ __tmp; \ }) static rk_u32 mpp_mem_pool_debug = 0; typedef struct MppMemPoolNode_t { void *check; struct list_head list; void *ptr; size_t size; } MppMemPoolNode; typedef struct MppMemPoolImpl_t { void *check; const char *name; size_t size; pthread_mutex_t lock; struct list_head service_link; struct list_head used; struct list_head unused; rk_s32 used_count; rk_s32 unused_count; /* extra flag for C++ static destruction order error */ rk_s32 finalized; } MppMemPoolImpl; typedef struct MppMemPoolService_t { struct list_head list; pthread_mutex_t lock; } MppMemPoolSrv; static MppMemPoolSrv *srv_mem_pool = NULL; static void mem_pool_srv_init() { MppMemPoolSrv *srv = srv_mem_pool; mpp_env_get_u32("mpp_mem_pool_debug", &mpp_mem_pool_debug, 0); if (srv) return; srv = mpp_malloc(MppMemPoolSrv, 1); if (!srv) { mpp_err_f("failed to allocate pool service\n"); return; } srv_mem_pool = srv; { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&srv->lock, &attr); pthread_mutexattr_destroy(&attr); } INIT_LIST_HEAD(&srv->list); } static void put_pool(MppMemPoolSrv *srv, MppMemPoolImpl *impl, const char *caller) { MppMemPoolNode *node, *m; if (impl != impl->check) { mpp_err_f("invalid mem impl %p check %p at %s\n", impl, impl->check, caller); return; } if (impl->finalized) return; pthread_mutex_lock(&impl->lock); if (!list_empty(&impl->unused)) { list_for_each_entry_safe(node, m, &impl->unused, MppMemPoolNode, list) { MPP_FREE(node); impl->unused_count--; } } if (!list_empty(&impl->used)) { mpp_err_f("pool %-16s found %d used buffer size %4d at %s\n", impl->name, impl->used_count, impl->size, caller); list_for_each_entry_safe(node, m, &impl->used, MppMemPoolNode, list) { MPP_FREE(node); impl->used_count--; } } if (impl->used_count || impl->unused_count) mpp_err_f("pool %-16s size %4d found leaked buffer used:unused [%d:%d] at %s\n", impl->name, impl->size, impl->used_count, impl->unused_count, caller); pthread_mutex_unlock(&impl->lock); if (srv) { pthread_mutex_lock(&srv->lock); list_del_init(&impl->service_link); pthread_mutex_unlock(&srv->lock); } impl->finalized = 1; mpp_free(impl); } static void mem_pool_srv_deinit() { MppMemPoolSrv *srv = srv_mem_pool; if (!srv) return; if (!list_empty(&srv->list)) { MppMemPoolImpl *pos, *n; list_for_each_entry_safe(pos, n, &srv->list, MppMemPoolImpl, service_link) { mem_pool_dbg_exit("pool %-16s size %4d leaked\n", pos->name, pos->size); put_pool(srv, pos, __FUNCTION__); } } pthread_mutex_destroy(&srv->lock); mpp_free(srv); srv_mem_pool = NULL; } MppMemPool mpp_mem_pool_init(const char *name, size_t size, const char *caller) { MppMemPoolSrv *srv = get_srv_mem_pool(caller); MppMemPoolImpl *pool; if (!srv) return NULL; pool = mpp_calloc(MppMemPoolImpl, 1); if (!pool) return NULL; { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&pool->lock, &attr); pthread_mutexattr_destroy(&attr); } pool->check = pool; pool->name = name; pool->size = size; pool->used_count = 0; pool->unused_count = 0; pool->finalized = 0; INIT_LIST_HEAD(&pool->used); INIT_LIST_HEAD(&pool->unused); INIT_LIST_HEAD(&pool->service_link); pthread_mutex_lock(&srv->lock); list_add_tail(&pool->service_link, &srv->list); pthread_mutex_unlock(&srv->lock); mem_pool_dbg_flow("pool %-16s size %4d init at %s\n", pool->name, size, caller); return pool; } void mpp_mem_pool_deinit(MppMemPool pool, const char *caller) { MppMemPoolSrv *srv = get_srv_mem_pool(caller); MppMemPoolImpl *impl = (MppMemPoolImpl *)pool; mem_pool_dbg_flow("pool %-16s size %4d deinit at %s\n", impl->name, impl->size, caller); put_pool(srv, impl, caller); } void *mpp_mem_pool_get(MppMemPool pool, const char *caller) { MppMemPoolImpl *impl = (MppMemPoolImpl *)pool; MppMemPoolNode *node = NULL; void* ptr = NULL; pthread_mutex_lock(&impl->lock); mem_pool_dbg_flow("pool %-16s size %4d get used:unused [%d:%d] at %s\n", impl->name, impl->size, impl->used_count, impl->unused_count, caller); if (!list_empty(&impl->unused)) { node = list_first_entry(&impl->unused, MppMemPoolNode, list); if (node) { list_del_init(&node->list); list_add_tail(&node->list, &impl->used); impl->unused_count--; impl->used_count++; ptr = node->ptr; node->check = node; goto DONE; } } node = mpp_malloc_size(MppMemPoolNode, sizeof(MppMemPoolNode) + impl->size); if (!node) { mpp_err_f("failed to create node from size %4d pool\n", impl->size); goto DONE; } node->check = node; node->ptr = (void *)(node + 1); node->size = impl->size; INIT_LIST_HEAD(&node->list); list_add_tail(&node->list, &impl->used); impl->used_count++; ptr = node->ptr; DONE: pthread_mutex_unlock(&impl->lock); if (node) memset(node->ptr, 0 , node->size); return ptr; } void mpp_mem_pool_put(MppMemPool pool, void *p, const char *caller) { MppMemPoolImpl *impl = (MppMemPoolImpl *)pool; MppMemPoolNode *node = (MppMemPoolNode *)((rk_u8 *)p - sizeof(MppMemPoolNode)); if (impl != impl->check) { mpp_err_f("invalid mem pool %p check %p\n", impl, impl->check); return ; } if (node != node->check) { mpp_err_f("invalid mem pool ptr %p node %p check %p\n", p, node, node->check); return ; } pthread_mutex_lock(&impl->lock); mem_pool_dbg_flow("pool %-16s size %4d put used:unused [%d:%d] at %s\n", impl->name, impl->size, impl->used_count, impl->unused_count, caller); list_del_init(&node->list); list_add(&node->list, &impl->unused); impl->used_count--; impl->unused_count++; node->check = NULL; pthread_mutex_unlock(&impl->lock); } MPP_SINGLETON(MPP_SGLN_MEM_POOL, mpp_mem_pool, mem_pool_srv_init, mem_pool_srv_deinit)