1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 #include "dnscore/dnscore-config.h"
36 #include "dnscore/pool.h"
37 #include "dnscore/logger.h"
38
39 extern logger_handle *g_system_logger;
40 #define MODULE_MSG_HANDLE g_system_logger
41
42 static mutex_t pool_chain_mtx = MUTEX_INITIALIZER;
43 static pool_s *pool_chain = NULL;
44
pool_reset_nop(void * ptr,void * args)45 static void pool_reset_nop(void *ptr, void *args)
46 {
47 (void)ptr;
48 (void)args;
49 }
50
pool_init_ex(pool_s * pool,pool_allocate_callback * allocate_,pool_free_callback * free_,pool_reset_callback * reset_,void * allocate_args,const char * name)51 void pool_init_ex(pool_s *pool, pool_allocate_callback *allocate_, pool_free_callback *free_, pool_reset_callback *reset_, void *allocate_args, const char* name)
52 {
53 #if DEBUG
54
55 // ensure there are no double initialisations
56
57 pool_s *first = pool_chain;
58 while(first != NULL)
59 {
60 if(first == pool)
61 {
62 abort();
63 }
64 first = first->next;
65 }
66 #endif
67 ptr_vector_init(&pool->pool);
68 pool->allocate_method = allocate_;
69 pool->free_method = free_;
70 pool->reset_method = reset_;
71 pool->allocate_args = allocate_args;
72 mutex_init(&pool->mtx);
73 pool->allocated_count = 0;
74 pool->released_count = 0;
75 pool->name = name;
76 pool->max_size = 0;
77 pool->current_count = 0;
78 pool->peak_count = 0;
79 pool->hard_limit = FALSE;
80 pool->maxed = FALSE;
81
82 pool_set_size(pool, 0x10000);
83
84 mutex_lock(&pool_chain_mtx);
85 pool->next = pool_chain;
86 pool_chain = pool;
87 mutex_unlock(&pool_chain_mtx);
88 }
89
90 void
pool_init(pool_s * pool,pool_allocate_callback * allocate_,pool_free_callback * free_,void * allocate_args,const char * name)91 pool_init(pool_s *pool, pool_allocate_callback *allocate_, pool_free_callback *free_, void *allocate_args, const char *name)
92 {
93 pool_init_ex(pool, allocate_, free_, pool_reset_nop, allocate_args, name);
94 }
95
96 void
pool_log_stats_ex(pool_s * pool,logger_handle * handle,u32 level)97 pool_log_stats_ex(pool_s *pool, logger_handle* handle, u32 level)
98 {
99 if(pool != NULL)
100 {
101 logger_handle_msg(handle, level, "pool '%s' handled %llu allocations and %llu releases; pooled %i maxed at %i; using %u peaked at %u",
102 pool->name, pool->allocated_count, pool->released_count,
103 pool->pool.offset + 1, pool->max_size,
104 pool->current_count, pool->peak_count);
105 }
106 else
107 {
108 logger_handle_msg(handle, MSG_ERR, "pool is NULL");
109 }
110 }
111
112 void
pool_log_stats(pool_s * pool)113 pool_log_stats(pool_s *pool)
114 {
115 pool_log_stats_ex(pool, MODULE_MSG_HANDLE, MSG_DEBUG);
116 }
117
118 void
pool_log_all_stats_ex(logger_handle * handle,u32 level)119 pool_log_all_stats_ex(logger_handle* handle, u32 level)
120 {
121 mutex_lock(&pool_chain_mtx);
122 pool_s *p = pool_chain;
123 while(p != NULL)
124 {
125 pool_log_stats_ex(p, handle, level);
126 p = p->next;
127 }
128 mutex_unlock(&pool_chain_mtx);
129 }
130
131 void
pool_log_all_stats()132 pool_log_all_stats()
133 {
134 pool_log_all_stats_ex(MODULE_MSG_HANDLE, MSG_DEBUG);
135 }
136
137 void
pool_finalize(pool_s * pool)138 pool_finalize(pool_s *pool)
139 {
140 #if DEBUG
141 pool_log_stats(pool);
142 #endif
143
144 mutex_lock(&pool_chain_mtx);
145 pool_s **pp = &pool_chain;
146 while(*pp != NULL)
147 {
148 if(*pp == pool)
149 {
150 *pp = pool->next;
151 break;
152 }
153 pp = &(*pp)->next;
154 }
155 mutex_unlock(&pool_chain_mtx);
156
157 u64 delta;
158 mutex_lock(&pool->mtx);
159 delta = pool->allocated_count - pool->released_count;
160 for(s32 i = 0; i <= pool->pool.offset; i++)
161 {
162 pool->free_method(pool->pool.data[i], pool->allocate_args);
163 pool->pool.data[i] = NULL;
164 }
165 ptr_vector_destroy(&pool->pool);
166 mutex_unlock(&pool->mtx);
167 mutex_destroy(&pool->mtx);
168
169 pool_log_stats(pool);
170
171 if(delta != 0)
172 {
173 log_warn("pool '%s' leaked: %d items", pool->name, delta);
174 }
175
176 #if DEBUG
177 memset(pool, 0xe0, sizeof(pool_s));
178 #endif
179 }
180
181 void*
pool_alloc(pool_s * pool)182 pool_alloc(pool_s *pool)
183 {
184 void *p;
185 mutex_lock(&pool->mtx);
186
187 if(pool->hard_limit)
188 {
189 if(pool->current_count >= pool->max_size + 1)
190 {
191 if(!pool->maxed) // the maxed flag helps to only complain once the limit is reached
192 {
193 log_warn("pool '%s' : pool usage reached maximum %i > %i", pool->name, pool->peak_count, pool->max_size);
194 pool->maxed = TRUE;
195 }
196
197 mutex_unlock(&pool->mtx);
198
199 return NULL;
200 }
201
202 pool->maxed = FALSE;
203 }
204
205 pool->allocated_count++;
206
207 if(++pool->current_count > pool->peak_count)
208 {
209 pool->peak_count = pool->current_count;
210 }
211
212 if(pool->pool.offset >= 0)
213 {
214 p = ptr_vector_pop(&pool->pool);
215 mutex_unlock(&pool->mtx);
216 pool->reset_method(p, pool->allocate_args);
217 }
218 else
219 {
220 mutex_unlock(&pool->mtx);
221 p = pool->allocate_method(pool->allocate_args);
222 }
223
224 log_debug7("pool '%s': alloc %p", pool->name, p);
225
226 return p;
227 }
228
229 void
pool_release(pool_s * pool,void * p)230 pool_release(pool_s *pool, void *p)
231 {
232 log_debug7("pool '%s': release %p", pool->name, p);
233
234 mutex_lock(&pool->mtx);
235
236 if((--pool->current_count) < 0)
237 {
238 log_err("pool '%s': <0: %d", pool->name, pool->current_count);
239 }
240
241 if(pool->pool.offset < pool->max_size)
242 {
243 ptr_vector_append(&pool->pool, p);
244 }
245 else
246 {
247 pool->free_method(p, pool->allocate_args);
248 }
249 pool->released_count++;
250 mutex_unlock(&pool->mtx);
251 }
252
253 void
pool_set_size(pool_s * pool,s32 max_size)254 pool_set_size(pool_s *pool, s32 max_size)
255 {
256 yassert(ptr_vector_size(&pool->pool) <= max_size);
257
258 ptr_vector_resize(&pool->pool, max_size);
259 pool->max_size = max_size - 1;
260 }
261