/* * Copyright 2021 Rockchip Electronics Co. LTD * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define MODULE_TAG "mpp_mem_pool" #include #include "mpp_err.h" #include "mpp_env.h" #include "mpp_mem.h" #include "mpp_list.h" #include "mpp_debug.h" #include "mpp_mem_pool.h" #define MPP_MEM_POOL_DBG_FLOW (0x00000001) #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(MPP_MEM_POOL_DBG_FLOW, fmt, ## __VA_ARGS__) 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; 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; } MppMemPoolImpl; class MppMemPoolService { public: static MppMemPoolService* getInstance() { AutoMutex auto_lock(get_lock()); static MppMemPoolService pool_service; return &pool_service; } static Mutex *get_lock() { static Mutex lock; return &lock; } MppMemPoolImpl *get_pool(size_t size); void put_pool(MppMemPoolImpl *impl); private: MppMemPoolService(); ~MppMemPoolService(); struct list_head mLink; }; MppMemPoolService::MppMemPoolService() { INIT_LIST_HEAD(&mLink); mpp_env_get_u32("mpp_mem_pool_debug", &mpp_mem_pool_debug, 0); } MppMemPoolService::~MppMemPoolService() { if (!list_empty(&mLink)) { MppMemPoolImpl *pos, *n; list_for_each_entry_safe(pos, n, &mLink, MppMemPoolImpl, service_link) { put_pool(pos); } } } MppMemPoolImpl *MppMemPoolService::get_pool(size_t size) { MppMemPoolImpl *pool = mpp_malloc(MppMemPoolImpl, 1); if (NULL == 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->size = size; pool->used_count = 0; pool->unused_count = 0; INIT_LIST_HEAD(&pool->used); INIT_LIST_HEAD(&pool->unused); INIT_LIST_HEAD(&pool->service_link); AutoMutex auto_lock(get_lock()); list_add_tail(&pool->service_link, &mLink); return pool; } void MppMemPoolService::put_pool(MppMemPoolImpl *impl) { MppMemPoolNode *node, *m; if (impl != impl->check) { mpp_err_f("invalid mem impl %p check %p\n", impl, impl->check); return ; } 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("found %d used buffer size %d\n", impl->used_count, impl->size); list_for_each_entry_safe(node, m, &impl->used, MppMemPoolNode, list) { MPP_FREE(node); impl->used_count--; } } mpp_assert(!impl->used_count); mpp_assert(!impl->unused_count); { AutoMutex auto_lock(get_lock()); list_del_init(&impl->service_link); } mpp_free(impl); } MppMemPool mpp_mem_pool_init_f(const char *caller, size_t size) { mem_pool_dbg_flow("pool %d init from %s", size, caller); return (MppMemPool)MppMemPoolService::getInstance()->get_pool(size); } void mpp_mem_pool_deinit_f(const char *caller, MppMemPool pool) { MppMemPoolImpl *impl = (MppMemPoolImpl *)pool; mem_pool_dbg_flow("pool %d deinit from %s", impl->size, caller); MppMemPoolService::getInstance()->put_pool(impl); } void *mpp_mem_pool_get_f(const char *caller, MppMemPool pool) { MppMemPoolImpl *impl = (MppMemPoolImpl *)pool; MppMemPoolNode *node = NULL; void* ptr = NULL; pthread_mutex_lock(&impl->lock); mem_pool_dbg_flow("pool %d get used:unused [%d:%d] from %s", 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; goto DONE; } } node = mpp_malloc_size(MppMemPoolNode, sizeof(MppMemPoolNode) + impl->size); if (NULL == node) { mpp_err_f("failed to create node from size %d 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_f(const char *caller, MppMemPool pool, void *p) { 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 %d put used:unused [%d:%d] from %s", 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++; pthread_mutex_unlock(&impl->lock); }