1 /* Copyright (C) 2013 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18 /**
19 * \defgroup utilpool Pool
20 *
21 * @{
22 */
23
24 /**
25 * \file
26 *
27 * \author Victor Julien <victor@inliniac.net>
28 *
29 * Pool utility functions
30 */
31
32 #include "suricata-common.h"
33 #include "util-pool.h"
34 #include "util-pool-thread.h"
35 #include "util-unittest.h"
36 #include "util-debug.h"
37
38 /**
39 * \brief per thread Pool, initialization function
40 * \param thread number of threads this is for. Can start with 1 and be expanded.
41 * Other params are as for PoolInit()
42 */
PoolThreadInit(int threads,uint32_t size,uint32_t prealloc_size,uint32_t elt_size,void * (* Alloc)(void),int (* Init)(void *,void *),void * InitData,void (* Cleanup)(void *),void (* Free)(void *))43 PoolThread *PoolThreadInit(int threads, uint32_t size, uint32_t prealloc_size,
44 uint32_t elt_size, void *(*Alloc)(void), int (*Init)(void *, void *),
45 void *InitData, void (*Cleanup)(void *), void (*Free)(void *))
46 {
47 if (threads <= 0) {
48 SCLogDebug("error");
49 return NULL;
50 }
51
52 PoolThread *pt = SCCalloc(1, sizeof(*pt));
53 if (unlikely(pt == NULL)) {
54 SCLogDebug("memory alloc error");
55 goto error;
56 }
57
58 SCLogDebug("size %d", threads);
59 pt->array = SCMalloc(threads * sizeof(PoolThreadElement));
60 if (pt->array == NULL) {
61 SCLogDebug("memory alloc error");
62 goto error;
63 }
64 pt->size = threads;
65
66 for (int i = 0; i < threads; i++) {
67 PoolThreadElement *e = &pt->array[i];
68
69 SCMutexInit(&e->lock, NULL);
70 SCMutexLock(&e->lock);
71 // SCLogDebug("size %u prealloc_size %u elt_size %u Alloc %p Init %p InitData %p Cleanup %p Free %p",
72 // size, prealloc_size, elt_size,
73 // Alloc, Init, InitData, Cleanup, Free);
74 e->pool = PoolInit(size, prealloc_size, elt_size, Alloc, Init, InitData, Cleanup, Free);
75 SCMutexUnlock(&e->lock);
76 if (e->pool == NULL) {
77 SCLogDebug("error");
78 goto error;
79 }
80 }
81
82 return pt;
83 error:
84 if (pt != NULL)
85 PoolThreadFree(pt);
86 return NULL;
87 }
88
89 /** \brief expand pool by one for a new thread
90 * \retval -1 or pool thread id
91 */
PoolThreadExpand(PoolThread * pt)92 int PoolThreadExpand(PoolThread *pt)
93 {
94 if (pt == NULL || pt->array == NULL || pt->size == 0) {
95 SCLogError(SC_ERR_POOL_INIT, "pool grow failed");
96 return -1;
97 }
98
99 size_t newsize = pt->size + 1;
100 SCLogDebug("newsize %"PRIuMAX, (uintmax_t)newsize);
101
102 void *ptmp = SCRealloc(pt->array, (newsize * sizeof(PoolThreadElement)));
103 if (ptmp == NULL) {
104 SCFree(pt->array);
105 pt->array = NULL;
106 SCLogError(SC_ERR_POOL_INIT, "pool grow failed");
107 return -1;
108 }
109 pt->array = ptmp;
110 pt->size = newsize;
111
112 /* copy settings from first thread that registered the pool */
113 Pool settings;
114 memset(&settings, 0x0, sizeof(settings));
115 PoolThreadElement *e = &pt->array[0];
116 SCMutexLock(&e->lock);
117 settings.max_buckets = e->pool->max_buckets;
118 settings.preallocated = e->pool->preallocated;
119 settings.elt_size = e->pool->elt_size;
120 settings.Alloc = e->pool->Alloc;
121 settings.Init = e->pool->Init;
122 settings.InitData = e->pool->InitData;
123 settings.Cleanup = e->pool->Cleanup;
124 settings.Free = e->pool->Free;
125 SCMutexUnlock(&e->lock);
126
127 e = &pt->array[newsize - 1];
128 memset(e, 0x00, sizeof(*e));
129 SCMutexInit(&e->lock, NULL);
130 SCMutexLock(&e->lock);
131 e->pool = PoolInit(settings.max_buckets, settings.preallocated,
132 settings.elt_size, settings.Alloc, settings.Init, settings.InitData,
133 settings.Cleanup, settings.Free);
134 SCMutexUnlock(&e->lock);
135 if (e->pool == NULL) {
136 SCLogError(SC_ERR_POOL_INIT, "pool grow failed");
137 return -1;
138 }
139
140 return (int)(newsize - 1);
141 }
142
PoolThreadSize(PoolThread * pt)143 int PoolThreadSize(PoolThread *pt)
144 {
145 if (pt == NULL)
146 return -1;
147 return (int)pt->size;
148 }
149
PoolThreadFree(PoolThread * pt)150 void PoolThreadFree(PoolThread *pt)
151 {
152 if (pt == NULL)
153 return;
154
155 if (pt->array != NULL) {
156 for (int i = 0; i < (int)pt->size; i++) {
157 PoolThreadElement *e = &pt->array[i];
158 SCMutexLock(&e->lock);
159 PoolFree(e->pool);
160 SCMutexUnlock(&e->lock);
161 SCMutexDestroy(&e->lock);
162 }
163 SCFree(pt->array);
164 }
165 SCFree(pt);
166 }
167
PoolThreadGetById(PoolThread * pt,uint16_t id)168 void *PoolThreadGetById(PoolThread *pt, uint16_t id)
169 {
170 void *data = NULL;
171
172 if (pt == NULL || id >= pt->size)
173 return NULL;
174
175 PoolThreadElement *e = &pt->array[id];
176 SCMutexLock(&e->lock);
177 data = PoolGet(e->pool);
178 SCMutexUnlock(&e->lock);
179 if (data) {
180 PoolThreadReserved *did = data;
181 *did = id;
182 }
183
184 return data;
185 }
186
PoolThreadReturn(PoolThread * pt,void * data)187 void PoolThreadReturn(PoolThread *pt, void *data)
188 {
189 PoolThreadReserved *id = data;
190
191 if (pt == NULL || *id >= pt->size)
192 return;
193
194 SCLogDebug("returning to id %u", *id);
195
196 PoolThreadElement *e = &pt->array[*id];
197 SCMutexLock(&e->lock);
198 PoolReturn(e->pool, data);
199 SCMutexUnlock(&e->lock);
200 }
201
202 #ifdef UNITTESTS
203 struct PoolThreadTestData {
204 PoolThreadReserved res;
205 int abc;
206 };
207
PoolThreadTestAlloc(void)208 static void *PoolThreadTestAlloc(void)
209 {
210 void *data = SCMalloc(sizeof(struct PoolThreadTestData));
211 return data;
212 }
213
214 static
PoolThreadTestInit(void * data,void * allocdata)215 int PoolThreadTestInit(void *data, void *allocdata)
216 {
217 if (!data)
218 return 0;
219
220 memset(data,0x00,sizeof(allocdata));
221 struct PoolThreadTestData *pdata = data;
222 pdata->abc = *(int *)allocdata;
223 return 1;
224 }
225
226 static
PoolThreadTestFree(void * data)227 void PoolThreadTestFree(void *data)
228 {
229 }
230
PoolThreadTestInit01(void)231 static int PoolThreadTestInit01(void)
232 {
233 PoolThread *pt = PoolThreadInit(4, /* threads */
234 10, 5, 10, PoolThreadTestAlloc,
235 NULL, NULL, NULL, NULL);
236 FAIL_IF(pt == NULL);
237 PoolThreadFree(pt);
238 PASS;
239 }
240
PoolThreadTestInit02(void)241 static int PoolThreadTestInit02(void)
242 {
243 int i = 123;
244
245 PoolThread *pt = PoolThreadInit(4, /* threads */
246 10, 5, 10,
247 PoolThreadTestAlloc, PoolThreadTestInit,
248 &i, PoolThreadTestFree, NULL);
249 FAIL_IF(pt == NULL);
250 PoolThreadFree(pt);
251 PASS;
252 }
253
PoolThreadTestGet01(void)254 static int PoolThreadTestGet01(void)
255 {
256 PoolThread *pt = PoolThreadInit(4, /* threads */
257 10, 5, 10, PoolThreadTestAlloc,
258 NULL, NULL, NULL, NULL);
259 FAIL_IF(pt == NULL);
260
261 void *data = PoolThreadGetById(pt, 3);
262 FAIL_IF_NULL(data);
263
264 struct PoolThreadTestData *pdata = data;
265 FAIL_IF(pdata->res != 3);
266
267 PoolThreadFree(pt);
268 PASS;
269 }
270
PoolThreadTestGet02(void)271 static int PoolThreadTestGet02(void)
272 {
273 int i = 123;
274
275 PoolThread *pt = PoolThreadInit(4, /* threads */
276 10, 5, 10, PoolThreadTestAlloc,
277 PoolThreadTestInit, &i, PoolThreadTestFree, NULL);
278 FAIL_IF_NULL(pt);
279
280 void *data = PoolThreadGetById(pt, 3);
281 FAIL_IF_NULL(data);
282
283 struct PoolThreadTestData *pdata = data;
284 FAIL_IF_NOT (pdata->res == 3);
285
286 FAIL_IF_NOT (pdata->abc == 123);
287
288 PoolThreadFree(pt);
289 PASS;
290 }
291
PoolThreadTestReturn01(void)292 static int PoolThreadTestReturn01(void)
293 {
294 int i = 123;
295
296 PoolThread *pt = PoolThreadInit(4, /* threads */
297 10, 5, 10, PoolThreadTestAlloc,
298 PoolThreadTestInit, &i, PoolThreadTestFree, NULL);
299 FAIL_IF_NULL(pt);
300
301 void *data = PoolThreadGetById(pt, 3);
302 FAIL_IF_NULL(data);
303
304 struct PoolThreadTestData *pdata = data;
305 FAIL_IF_NOT (pdata->res == 3);
306
307 FAIL_IF_NOT (pdata->abc == 123);
308
309 FAIL_IF_NOT (pt->array[3].pool->outstanding == 1);
310
311 PoolThreadReturn(pt, data);
312
313 FAIL_IF_NOT (pt->array[3].pool->outstanding == 0);
314
315 PoolThreadFree(pt);
316 PASS;
317 }
318
PoolThreadTestGrow01(void)319 static int PoolThreadTestGrow01(void)
320 {
321 PoolThread *pt = PoolThreadInit(4, /* threads */
322 10, 5, 10, PoolThreadTestAlloc,
323 NULL, NULL, NULL, NULL);
324 FAIL_IF_NULL(pt);
325 FAIL_IF(PoolThreadExpand(pt) < 0);
326
327 PoolThreadFree(pt);
328 PASS;
329 }
330
PoolThreadTestGrow02(void)331 static int PoolThreadTestGrow02(void)
332 {
333 int i = 123;
334
335 PoolThread *pt = PoolThreadInit(4, /* threads */
336 10, 5, 10, PoolThreadTestAlloc,
337 PoolThreadTestInit, &i, PoolThreadTestFree, NULL);
338 FAIL_IF_NULL(pt);
339 FAIL_IF(PoolThreadExpand(pt) < 0);
340
341 PoolThreadFree(pt);
342 PASS;
343 }
344
PoolThreadTestGrow03(void)345 static int PoolThreadTestGrow03(void)
346 {
347 int i = 123;
348
349 PoolThread *pt = PoolThreadInit(4, /* threads */
350 10, 5, 10, PoolThreadTestAlloc,
351 PoolThreadTestInit, &i, PoolThreadTestFree, NULL);
352 FAIL_IF_NULL(pt);
353 FAIL_IF(PoolThreadExpand(pt) < 0);
354
355 void *data = PoolThreadGetById(pt, 4);
356 FAIL_IF_NULL(data);
357
358 struct PoolThreadTestData *pdata = data;
359 FAIL_IF_NOT(pdata->res == 4);
360
361 FAIL_IF_NOT(pdata->abc == 123);
362
363 FAIL_IF_NOT(pt->array[4].pool->outstanding == 1);
364
365 PoolThreadReturn(pt, data);
366
367 FAIL_IF_NOT(pt->array[4].pool->outstanding == 0);
368
369 PoolThreadFree(pt);
370 PASS;
371 }
372
373 #endif
374
PoolThreadRegisterTests(void)375 void PoolThreadRegisterTests(void)
376 {
377 #ifdef UNITTESTS
378 UtRegisterTest("PoolThreadTestInit01", PoolThreadTestInit01);
379 UtRegisterTest("PoolThreadTestInit02", PoolThreadTestInit02);
380
381 UtRegisterTest("PoolThreadTestGet01", PoolThreadTestGet01);
382 UtRegisterTest("PoolThreadTestGet02", PoolThreadTestGet02);
383
384 UtRegisterTest("PoolThreadTestReturn01", PoolThreadTestReturn01);
385
386 UtRegisterTest("PoolThreadTestGrow01", PoolThreadTestGrow01);
387 UtRegisterTest("PoolThreadTestGrow02", PoolThreadTestGrow02);
388 UtRegisterTest("PoolThreadTestGrow03", PoolThreadTestGrow03);
389 #endif
390 }
391
392 /**
393 * @}
394 */
395