1 /*
2 * Copyright 2021 Rockchip Electronics Co. LTD
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define MODULE_TAG "mpp_mem_pool"
18
19 #include <string.h>
20
21 #include "mpp_err.h"
22 #include "mpp_env.h"
23 #include "mpp_mem.h"
24 #include "mpp_list.h"
25 #include "mpp_debug.h"
26
27 #include "mpp_mem_pool.h"
28
29 #define MPP_MEM_POOL_DBG_FLOW (0x00000001)
30
31 #define mem_pool_dbg(flag, fmt, ...) _mpp_dbg(mpp_mem_pool_debug, flag, fmt, ## __VA_ARGS__)
32 #define mem_pool_dbg_f(flag, fmt, ...) _mpp_dbg_f(mpp_mem_pool_debug, flag, fmt, ## __VA_ARGS__)
33
34 #define mem_pool_dbg_flow(fmt, ...) mem_pool_dbg(MPP_MEM_POOL_DBG_FLOW, fmt, ## __VA_ARGS__)
35
36 RK_U32 mpp_mem_pool_debug = 0;
37
38 typedef struct MppMemPoolNode_t {
39 void *check;
40 struct list_head list;
41 void *ptr;
42 size_t size;
43 } MppMemPoolNode;
44
45 typedef struct MppMemPoolImpl_t {
46 void *check;
47 size_t size;
48 pthread_mutex_t lock;
49 struct list_head service_link;
50
51 struct list_head used;
52 struct list_head unused;
53 RK_S32 used_count;
54 RK_S32 unused_count;
55
56 /* extra flag for C++ static destruction order error */
57 RK_S32 finalized;
58 } MppMemPoolImpl;
59
60 class MppMemPoolService
61 {
62 public:
getInstance()63 static MppMemPoolService* getInstance() {
64 AutoMutex auto_lock(get_lock());
65 static MppMemPoolService pool_service;
66 return &pool_service;
67 }
get_lock()68 static Mutex *get_lock() {
69 static Mutex lock;
70 return &lock;
71 }
72
73 MppMemPoolImpl *get_pool(size_t size);
74 void put_pool(MppMemPoolImpl *impl);
75
76 private:
77 MppMemPoolService();
78 ~MppMemPoolService();
79 struct list_head mLink;
80 };
81
MppMemPoolService()82 MppMemPoolService::MppMemPoolService()
83 {
84 INIT_LIST_HEAD(&mLink);
85
86 mpp_env_get_u32("mpp_mem_pool_debug", &mpp_mem_pool_debug, 0);
87 }
88
~MppMemPoolService()89 MppMemPoolService::~MppMemPoolService()
90 {
91 if (!list_empty(&mLink)) {
92 MppMemPoolImpl *pos, *n;
93
94 list_for_each_entry_safe(pos, n, &mLink, MppMemPoolImpl, service_link) {
95 put_pool(pos);
96 }
97 }
98 }
99
get_pool(size_t size)100 MppMemPoolImpl *MppMemPoolService::get_pool(size_t size)
101 {
102 MppMemPoolImpl *pool = mpp_malloc(MppMemPoolImpl, 1);
103 if (NULL == pool)
104 return NULL;
105
106 pthread_mutexattr_t attr;
107 pthread_mutexattr_init(&attr);
108 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
109 pthread_mutex_init(&pool->lock, &attr);
110 pthread_mutexattr_destroy(&attr);
111
112 pool->check = pool;
113 pool->size = size;
114 pool->used_count = 0;
115 pool->unused_count = 0;
116 pool->finalized = 0;
117
118 INIT_LIST_HEAD(&pool->used);
119 INIT_LIST_HEAD(&pool->unused);
120 INIT_LIST_HEAD(&pool->service_link);
121 AutoMutex auto_lock(get_lock());
122 list_add_tail(&pool->service_link, &mLink);
123
124 return pool;
125 }
126
put_pool(MppMemPoolImpl * impl)127 void MppMemPoolService::put_pool(MppMemPoolImpl *impl)
128 {
129 MppMemPoolNode *node, *m;
130
131 if (impl != impl->check) {
132 mpp_err_f("invalid mem impl %p check %p\n", impl, impl->check);
133 return;
134 }
135
136 if (impl->finalized)
137 return;
138
139 pthread_mutex_lock(&impl->lock);
140
141 if (!list_empty(&impl->unused)) {
142 list_for_each_entry_safe(node, m, &impl->unused, MppMemPoolNode, list) {
143 MPP_FREE(node);
144 impl->unused_count--;
145 }
146 }
147
148 if (!list_empty(&impl->used)) {
149 mpp_err_f("found %d used buffer size %d\n",
150 impl->used_count, impl->size);
151
152 list_for_each_entry_safe(node, m, &impl->used, MppMemPoolNode, list) {
153 MPP_FREE(node);
154 impl->used_count--;
155 }
156 }
157
158 if (impl->used_count || impl->unused_count)
159 mpp_err_f("pool size %d found leaked buffer used:unused [%d:%d]\n",
160 impl->size, impl->used_count, impl->unused_count);
161
162 pthread_mutex_unlock(&impl->lock);
163
164 {
165 AutoMutex auto_lock(get_lock());
166 list_del_init(&impl->service_link);
167 }
168
169 impl->finalized = 1;
170 mpp_free(impl);
171 }
172
mpp_mem_pool_init_f(const char * caller,size_t size)173 MppMemPool mpp_mem_pool_init_f(const char *caller, size_t size)
174 {
175 mem_pool_dbg_flow("pool %d init from %s", size, caller);
176
177 return (MppMemPool)MppMemPoolService::getInstance()->get_pool(size);
178 }
179
mpp_mem_pool_deinit_f(const char * caller,MppMemPool pool)180 void mpp_mem_pool_deinit_f(const char *caller, MppMemPool pool)
181 {
182 MppMemPoolImpl *impl = (MppMemPoolImpl *)pool;
183
184 mem_pool_dbg_flow("pool %d deinit from %s", impl->size, caller);
185
186 MppMemPoolService::getInstance()->put_pool(impl);
187 }
188
mpp_mem_pool_get_f(const char * caller,MppMemPool pool)189 void *mpp_mem_pool_get_f(const char *caller, MppMemPool pool)
190 {
191 MppMemPoolImpl *impl = (MppMemPoolImpl *)pool;
192 MppMemPoolNode *node = NULL;
193 void* ptr = NULL;
194
195 pthread_mutex_lock(&impl->lock);
196
197 mem_pool_dbg_flow("pool %d get used:unused [%d:%d] from %s", impl->size,
198 impl->used_count, impl->unused_count, caller);
199
200 if (!list_empty(&impl->unused)) {
201 node = list_first_entry(&impl->unused, MppMemPoolNode, list);
202 if (node) {
203 list_del_init(&node->list);
204 list_add_tail(&node->list, &impl->used);
205 impl->unused_count--;
206 impl->used_count++;
207 ptr = node->ptr;
208 node->check = node;
209 goto DONE;
210 }
211 }
212
213 node = mpp_malloc_size(MppMemPoolNode, sizeof(MppMemPoolNode) + impl->size);
214 if (NULL == node) {
215 mpp_err_f("failed to create node from size %d pool\n", impl->size);
216 goto DONE;
217 }
218
219 node->check = node;
220 node->ptr = (void *)(node + 1);
221 node->size = impl->size;
222 INIT_LIST_HEAD(&node->list);
223 list_add_tail(&node->list, &impl->used);
224 impl->used_count++;
225 ptr = node->ptr;
226
227 DONE:
228 pthread_mutex_unlock(&impl->lock);
229 if (node)
230 memset(node->ptr, 0 , node->size);
231 return ptr;
232 }
233
mpp_mem_pool_put_f(const char * caller,MppMemPool pool,void * p)234 void mpp_mem_pool_put_f(const char *caller, MppMemPool pool, void *p)
235 {
236 MppMemPoolImpl *impl = (MppMemPoolImpl *)pool;
237 MppMemPoolNode *node = (MppMemPoolNode *)((RK_U8 *)p - sizeof(MppMemPoolNode));
238
239 if (impl != impl->check) {
240 mpp_err_f("invalid mem pool %p check %p\n", impl, impl->check);
241 return ;
242 }
243
244 if (node != node->check) {
245 mpp_err_f("invalid mem pool ptr %p node %p check %p\n",
246 p, node, node->check);
247 return ;
248 }
249
250 pthread_mutex_lock(&impl->lock);
251
252 mem_pool_dbg_flow("pool %d put used:unused [%d:%d] from %s", impl->size,
253 impl->used_count, impl->unused_count, caller);
254
255 list_del_init(&node->list);
256 list_add(&node->list, &impl->unused);
257 impl->used_count--;
258 impl->unused_count++;
259 node->check = NULL;
260
261 pthread_mutex_unlock(&impl->lock);
262 }
263