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