1 /* SPDX-License-Identifier: Apache-2.0 OR MIT */
2 /*
3 * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
4 */
5
6 #define MODULE_TAG "mpp_mem_pool"
7
8 #include <string.h>
9
10 #include "mpp_env.h"
11 #include "mpp_mem.h"
12 #include "mpp_list.h"
13 #include "mpp_debug.h"
14 #include "mpp_singleton.h"
15
16 #include "mpp_mem_pool.h"
17
18 #define MEM_POOL_DBG_FLOW (0x00000001)
19 #define MEM_POOL_DBG_EXIT (0x00000002)
20
21 #define mem_pool_dbg(flag, fmt, ...) _mpp_dbg(mpp_mem_pool_debug, flag, fmt, ## __VA_ARGS__)
22 #define mem_pool_dbg_f(flag, fmt, ...) _mpp_dbg_f(mpp_mem_pool_debug, flag, fmt, ## __VA_ARGS__)
23
24 #define mem_pool_dbg_flow(fmt, ...) mem_pool_dbg(MEM_POOL_DBG_FLOW, fmt, ## __VA_ARGS__)
25 #define mem_pool_dbg_exit(fmt, ...) mem_pool_dbg(MEM_POOL_DBG_EXIT, fmt, ## __VA_ARGS__)
26
27 #define get_srv_mem_pool(caller) \
28 ({ \
29 MppMemPoolSrv *__tmp; \
30 if (!srv_mem_pool) { \
31 mem_pool_srv_init(); \
32 } \
33 if (srv_mem_pool) { \
34 __tmp = srv_mem_pool; \
35 } else { \
36 mpp_err("mpp mem pool srv not init at %s : %s\n", __FUNCTION__, caller); \
37 __tmp = NULL; \
38 } \
39 __tmp; \
40 })
41
42 static rk_u32 mpp_mem_pool_debug = 0;
43
44 /*
45 * Aligning MppMemPoolNode to 8-byte
46 * Avoiding alignment traps caused by compiler optimizations
47 */
48 typedef struct MppMemPoolNode_t {
49 void *check;
50 struct list_head list;
51 void *ptr;
52 rk_u64 size;
53 } MppMemPoolNode;
54
55 typedef struct MppMemPoolImpl_t {
56 void *check;
57 const char *name;
58 size_t size;
59 pthread_mutex_t lock;
60 struct list_head service_link;
61
62 struct list_head used;
63 struct list_head unused;
64 rk_s32 used_count;
65 rk_s32 unused_count;
66
67 /* extra flag for C++ static destruction order error */
68 rk_s32 finalized;
69 } MppMemPoolImpl;
70
71 typedef struct MppMemPoolService_t {
72 struct list_head list;
73 pthread_mutex_t lock;
74 } MppMemPoolSrv;
75
76 static MppMemPoolSrv *srv_mem_pool = NULL;
77
mem_pool_srv_init()78 static void mem_pool_srv_init()
79 {
80 MppMemPoolSrv *srv = srv_mem_pool;
81
82 mpp_env_get_u32("mpp_mem_pool_debug", &mpp_mem_pool_debug, 0);
83
84 if (srv)
85 return;
86
87 srv = mpp_malloc(MppMemPoolSrv, 1);
88 if (!srv) {
89 mpp_err_f("failed to allocate pool service\n");
90 return;
91 }
92
93 srv_mem_pool = srv;
94
95 {
96 pthread_mutexattr_t attr;
97
98 pthread_mutexattr_init(&attr);
99 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
100 pthread_mutex_init(&srv->lock, &attr);
101 pthread_mutexattr_destroy(&attr);
102 }
103
104 INIT_LIST_HEAD(&srv->list);
105 }
106
put_pool(MppMemPoolSrv * srv,MppMemPoolImpl * impl,const char * caller)107 static void put_pool(MppMemPoolSrv *srv, MppMemPoolImpl *impl, const char *caller)
108 {
109 MppMemPoolNode *node, *m;
110
111 if (impl != impl->check) {
112 mpp_err_f("invalid mem impl %p check %p at %s\n", impl, impl->check, caller);
113 return;
114 }
115
116 if (impl->finalized)
117 return;
118
119 pthread_mutex_lock(&impl->lock);
120
121 if (!list_empty(&impl->unused)) {
122 list_for_each_entry_safe(node, m, &impl->unused, MppMemPoolNode, list) {
123 MPP_FREE(node);
124 impl->unused_count--;
125 }
126 }
127
128 if (!list_empty(&impl->used)) {
129 mpp_err_f("pool %-16s found %d used buffer size %4d at %s\n",
130 impl->name, impl->used_count, impl->size, caller);
131
132 list_for_each_entry_safe(node, m, &impl->used, MppMemPoolNode, list) {
133 MPP_FREE(node);
134 impl->used_count--;
135 }
136 }
137
138 if (impl->used_count || impl->unused_count)
139 mpp_err_f("pool %-16s size %4d found leaked buffer used:unused [%d:%d] at %s\n",
140 impl->name, impl->size, impl->used_count, impl->unused_count, caller);
141
142 pthread_mutex_unlock(&impl->lock);
143
144 if (srv) {
145 pthread_mutex_lock(&srv->lock);
146 list_del_init(&impl->service_link);
147 pthread_mutex_unlock(&srv->lock);
148 }
149
150 impl->finalized = 1;
151 mpp_free(impl);
152 }
153
mem_pool_srv_deinit()154 static void mem_pool_srv_deinit()
155 {
156 MppMemPoolSrv *srv = srv_mem_pool;
157
158 if (!srv)
159 return;
160
161 if (!list_empty(&srv->list)) {
162 MppMemPoolImpl *pos, *n;
163
164 list_for_each_entry_safe(pos, n, &srv->list, MppMemPoolImpl, service_link) {
165 mem_pool_dbg_exit("pool %-16s size %4d leaked\n", pos->name, pos->size);
166 put_pool(srv, pos, __FUNCTION__);
167 }
168 }
169
170 pthread_mutex_destroy(&srv->lock);
171
172 mpp_free(srv);
173 srv_mem_pool = NULL;
174 }
175
mpp_mem_pool_init(const char * name,size_t size,const char * caller)176 MppMemPool mpp_mem_pool_init(const char *name, size_t size, const char *caller)
177 {
178 MppMemPoolSrv *srv = get_srv_mem_pool(caller);
179 MppMemPoolImpl *pool;
180
181 if (!srv)
182 return NULL;
183
184 pool = mpp_calloc(MppMemPoolImpl, 1);
185 if (!pool)
186 return NULL;
187
188 {
189 pthread_mutexattr_t attr;
190
191 pthread_mutexattr_init(&attr);
192 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
193 pthread_mutex_init(&pool->lock, &attr);
194 pthread_mutexattr_destroy(&attr);
195 }
196
197 pool->check = pool;
198 pool->name = name;
199 pool->size = size;
200 pool->used_count = 0;
201 pool->unused_count = 0;
202 pool->finalized = 0;
203
204 INIT_LIST_HEAD(&pool->used);
205 INIT_LIST_HEAD(&pool->unused);
206 INIT_LIST_HEAD(&pool->service_link);
207
208 pthread_mutex_lock(&srv->lock);
209 list_add_tail(&pool->service_link, &srv->list);
210 pthread_mutex_unlock(&srv->lock);
211
212 mem_pool_dbg_flow("pool %-16s size %4d init at %s\n", pool->name, size, caller);
213
214 return pool;
215 }
216
mpp_mem_pool_deinit(MppMemPool pool,const char * caller)217 void mpp_mem_pool_deinit(MppMemPool pool, const char *caller)
218 {
219 MppMemPoolSrv *srv = get_srv_mem_pool(caller);
220 MppMemPoolImpl *impl = (MppMemPoolImpl *)pool;
221
222 mem_pool_dbg_flow("pool %-16s size %4d deinit at %s\n",
223 impl->name, impl->size, caller);
224
225 put_pool(srv, impl, caller);
226 }
227
mpp_mem_pool_get(MppMemPool pool,const char * caller)228 void *mpp_mem_pool_get(MppMemPool pool, const char *caller)
229 {
230 MppMemPoolImpl *impl = (MppMemPoolImpl *)pool;
231 MppMemPoolNode *node = NULL;
232 void* ptr = NULL;
233
234 pthread_mutex_lock(&impl->lock);
235
236 mem_pool_dbg_flow("pool %-16s size %4d get used:unused [%d:%d] at %s\n",
237 impl->name, impl->size, impl->used_count, impl->unused_count, caller);
238
239 if (!list_empty(&impl->unused)) {
240 node = list_first_entry(&impl->unused, MppMemPoolNode, list);
241 if (node) {
242 list_del_init(&node->list);
243 list_add_tail(&node->list, &impl->used);
244 impl->unused_count--;
245 impl->used_count++;
246 ptr = node->ptr;
247 node->check = node;
248 goto DONE;
249 }
250 }
251
252 node = mpp_malloc_size(MppMemPoolNode, sizeof(MppMemPoolNode) + impl->size);
253 if (!node) {
254 mpp_err_f("failed to create node from size %4d pool\n", impl->size);
255 goto DONE;
256 }
257
258 node->check = node;
259 node->ptr = (void *)(node + 1);
260 node->size = impl->size;
261 INIT_LIST_HEAD(&node->list);
262 list_add_tail(&node->list, &impl->used);
263 impl->used_count++;
264 ptr = node->ptr;
265
266 DONE:
267 pthread_mutex_unlock(&impl->lock);
268 if (node)
269 memset(node->ptr, 0 , node->size);
270 return ptr;
271 }
272
mpp_mem_pool_put(MppMemPool pool,void * p,const char * caller)273 void mpp_mem_pool_put(MppMemPool pool, void *p, const char *caller)
274 {
275 MppMemPoolImpl *impl = (MppMemPoolImpl *)pool;
276 MppMemPoolNode *node = (MppMemPoolNode *)((rk_u8 *)p - sizeof(MppMemPoolNode));
277
278 if (impl != impl->check) {
279 mpp_err_f("invalid mem pool %p check %p\n", impl, impl->check);
280 return ;
281 }
282
283 if (node != node->check) {
284 mpp_err_f("invalid mem pool ptr %p node %p check %p\n",
285 p, node, node->check);
286 return ;
287 }
288
289 pthread_mutex_lock(&impl->lock);
290
291 mem_pool_dbg_flow("pool %-16s size %4d put used:unused [%d:%d] at %s\n",
292 impl->name, impl->size, impl->used_count, impl->unused_count, caller);
293
294 list_del_init(&node->list);
295 list_add(&node->list, &impl->unused);
296 impl->used_count--;
297 impl->unused_count++;
298 node->check = NULL;
299
300 pthread_mutex_unlock(&impl->lock);
301 }
302
303 MPP_SINGLETON(MPP_SGLN_MEM_POOL, mpp_mem_pool, mem_pool_srv_init, mem_pool_srv_deinit)
304