suricata
util-pool.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2010 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 * ::Pool are an effective way to maintain a set of ready to use
22 * structures.
23 *
24 * To create a ::Pool, you need to use PoolInit(). You can
25 * get an item from the ::Pool by using PoolGet(). When you're
26 * done with it call PoolReturn().
27 * To destroy the ::Pool, call PoolFree(), it will free all used
28 * memory.
29 *
30 * @{
31 */
32
33/**
34 * \file
35 *
36 * \author Victor Julien <victor@inliniac.net>
37 *
38 * Pool utility functions
39 */
40
41#include "suricata-common.h"
42#include "util-pool.h"
43#include "util-pool-thread.h"
44#include "util-unittest.h"
45#include "util-debug.h"
46
47static int PoolMemset(void *pitem, void *initdata)
48{
49 Pool *p = (Pool *) initdata;
50
51 memset(pitem, 0, p->elt_size);
52 return 1;
53}
54
55/**
56 * \brief Check if data is preallocated
57 * \retval false if not inside the prealloc'd block, true if inside */
58static bool PoolDataPreAllocated(Pool *p, void *data)
59{
60 ptrdiff_t delta = data - p->data_buffer;
61 if ((delta < 0) || (delta > p->data_buffer_size)) {
62 return false;
63 }
64 return true;
65}
66
67/** \brief Init a Pool
68 *
69 * PoolInit() creates a ::Pool. The Alloc function must only do
70 * allocation stuff. The Cleanup function must not try to free
71 * the PoolBucket::data. This is done by the ::Pool management
72 * system.
73 *
74 * \param size
75 * \param prealloc_size
76 * \param elt_size Memory size of an element
77 * \param Alloc An allocation function or NULL to use a standard SCMalloc
78 * \param Init An init function or NULL to use a standard memset to 0
79 * \param InitData Init data
80 * \param Cleanup a free function or NULL if no special treatment is needed
81 * \param Free free func
82 * \retval the allocated Pool
83 */
84Pool *PoolInit(uint32_t size, uint32_t prealloc_size, uint32_t elt_size,
85 void *(*Alloc)(void), int (*Init)(void *, void *), void *InitData,
86 void (*Cleanup)(void *), void (*Free)(void *))
87{
89
90 Pool *p = NULL;
91
92 if (size != 0 && prealloc_size > size) {
94 goto error;
95 }
96 if (size != 0 && elt_size == 0) {
98 goto error;
99 }
100 if (elt_size && Free) {
102 goto error;
103 }
104 if (elt_size == 0 && Alloc == NULL) {
106 goto error;
107 }
108
109 /* setup the filter */
110 p = SCCalloc(1, sizeof(Pool));
111 if (unlikely(p == NULL)) {
113 goto error;
114 }
115
116 p->max_buckets = size;
117 p->preallocated = prealloc_size;
118 p->elt_size = elt_size;
119 p->data_buffer_size = prealloc_size * elt_size;
120 p->Alloc = Alloc;
121 p->Init = Init;
122 p->InitData = InitData;
123 p->Cleanup = Cleanup;
124 p->Free = Free;
125 if (p->Init == NULL) {
126 p->Init = PoolMemset;
127 p->InitData = p;
128 }
129
130 /* alloc the buckets and place them in the empty list */
131 uint32_t u32 = 0;
132 if (size > 0) {
133 PoolBucket *pb = SCCalloc(size, sizeof(PoolBucket));
134 if (unlikely(pb == NULL)) {
136 goto error;
137 }
138 memset(pb, 0, size * sizeof(PoolBucket));
139 p->pb_buffer = pb;
140 for (u32 = 0; u32 < size; u32++) {
141 /* populate pool */
142 pb->next = p->empty_stack;
144 p->empty_stack = pb;
145 p->empty_stack_size++;
146 pb++;
147 }
148
149 p->data_buffer = SCCalloc(prealloc_size, elt_size);
150 /* FIXME better goto */
151 if (p->data_buffer == NULL) {
153 goto error;
154 }
155 }
156 /* prealloc the buckets and requeue them to the alloc list */
157 for (u32 = 0; u32 < prealloc_size; u32++) {
158 if (size == 0) { /* unlimited */
159 PoolBucket *pb = SCCalloc(1, sizeof(PoolBucket));
160 if (unlikely(pb == NULL)) {
162 goto error;
163 }
164
165 if (p->Alloc) {
166 pb->data = p->Alloc();
167 } else {
168 pb->data = SCMalloc(p->elt_size);
169 }
170 if (pb->data == NULL) {
171 SCFree(pb);
173 goto error;
174 }
175 if (p->Init(pb->data, p->InitData) != 1) {
176 if (p->Free)
177 p->Free(pb->data);
178 else
179 SCFree(pb->data);
180 SCFree(pb);
182 goto error;
183 }
184 p->allocated++;
185
186 pb->next = p->alloc_stack;
187 p->alloc_stack = pb;
188 p->alloc_stack_size++;
189 } else {
190 PoolBucket *pb = p->empty_stack;
191 if (pb == NULL) {
193 goto error;
194 }
195
196 pb->data = (char *)p->data_buffer + u32 * elt_size;
197 if (p->Init(pb->data, p->InitData) != 1) {
198 pb->data = NULL;
200 goto error;
201 }
202
203 p->empty_stack = pb->next;
204 p->empty_stack_size--;
205
206 p->allocated++;
207
208 pb->next = p->alloc_stack;
209 p->alloc_stack = pb;
210 p->alloc_stack_size++;
211 }
212 }
213
214 return p;
215
216error:
217 if (p != NULL) {
218 PoolFree(p);
219 }
220 return NULL;
221}
222
224{
225 if (p == NULL)
226 return;
227
228 while (p->alloc_stack != NULL) {
229 PoolBucket *pb = p->alloc_stack;
230 p->alloc_stack = pb->next;
231 if (p->Cleanup)
232 p->Cleanup(pb->data);
233 if (!PoolDataPreAllocated(p, pb->data)) {
234 if (p->Free)
235 p->Free(pb->data);
236 else
237 SCFree(pb->data);
238 }
239 pb->data = NULL;
240 if (!(pb->flags & POOL_BUCKET_PREALLOCATED)) {
241 SCFree(pb);
242 }
243 }
244
245 while (p->empty_stack != NULL) {
246 PoolBucket *pb = p->empty_stack;
247 p->empty_stack = pb->next;
248 if (pb->data!= NULL) {
249 if (p->Cleanup)
250 p->Cleanup(pb->data);
251 if (!PoolDataPreAllocated(p, pb->data)) {
252 if (p->Free)
253 p->Free(pb->data);
254 else
255 SCFree(pb->data);
256 }
257 pb->data = NULL;
258 }
259 if (!(pb->flags & POOL_BUCKET_PREALLOCATED)) {
260 SCFree(pb);
261 }
262 }
263
264 if (p->pb_buffer)
265 SCFree(p->pb_buffer);
266 if (p->data_buffer)
268 SCFree(p);
269}
270
271void *PoolGet(Pool *p)
272{
273 SCEnter();
274
275 PoolBucket *pb = p->alloc_stack;
276 if (pb != NULL) {
277 /* pull from the alloc list */
278 p->alloc_stack = pb->next;
279 p->alloc_stack_size--;
280
281 /* put in the empty list */
282 pb->next = p->empty_stack;
283 p->empty_stack = pb;
284 p->empty_stack_size++;
285 } else {
286 if (p->max_buckets == 0 || p->allocated < p->max_buckets) {
287 void *pitem;
288 SCLogDebug("max_buckets %"PRIu32"", p->max_buckets);
289
290 if (p->Alloc != NULL) {
291 pitem = p->Alloc();
292 } else {
293 pitem = SCMalloc(p->elt_size);
294 }
295
296 if (pitem != NULL) {
297 if (p->Init(pitem, p->InitData) != 1) {
298 if (p->Free != NULL)
299 p->Free(pitem);
300 else
301 SCFree(pitem);
302 SCReturnPtr(NULL, "void");
303 }
304
305 p->allocated++;
306 p->outstanding++;
307#ifdef DEBUG
308 if (p->outstanding > p->max_outstanding)
309 p->max_outstanding = p->outstanding;
310#endif
311 }
312
313 SCReturnPtr(pitem, "void");
314 } else {
315 SCReturnPtr(NULL, "void");
316 }
317 }
318
319 void *ptr = pb->data;
320 pb->data = NULL;
321 p->outstanding++;
322#ifdef DEBUG
323 if (p->outstanding > p->max_outstanding)
324 p->max_outstanding = p->outstanding;
325#endif
326 SCReturnPtr(ptr,"void");
327}
328
329void PoolReturn(Pool *p, void *data)
330{
331 SCEnter();
332
333 PoolBucket *pb = p->empty_stack;
334
335 SCLogDebug("pb %p", pb);
336
337 if (pb == NULL) {
338 p->allocated--;
339 p->outstanding--;
340 if (p->Cleanup != NULL) {
341 p->Cleanup(data);
342 }
343 if (!PoolDataPreAllocated(p, data)) {
344 if (p->Free)
345 p->Free(data);
346 else
347 SCFree(data);
348 }
349
350 SCLogDebug("tried to return data %p to the pool %p, but no more "
351 "buckets available. Just freeing the data.", data, p);
352 SCReturn;
353 }
354
355 /* pull from the alloc list */
356 p->empty_stack = pb->next;
357 p->empty_stack_size--;
358
359 /* put in the alloc list */
360 pb->next = p->alloc_stack;
361 p->alloc_stack = pb;
362 p->alloc_stack_size++;
363
364 pb->data = data;
365 p->outstanding--;
366 SCReturn;
367}
368
369/*
370 * ONLY TESTS BELOW THIS COMMENT
371 */
372
373#ifdef UNITTESTS
374static void *PoolTestAlloc(void)
375{
376 void *ptr = SCMalloc(10);
377 if (unlikely(ptr == NULL))
378 return NULL;
379 return ptr;
380}
381static int PoolTestInitArg(void *data, void *allocdata)
382{
383 size_t len = strlen((char *)allocdata) + 1;
384 char *str = data;
385 if (str != NULL)
386 strlcpy(str,(char *)allocdata,len);
387 return 1;
388}
389
390static void PoolTestFree(void *ptr)
391{
392}
393
394static int PoolTestInit01 (void)
395{
396 Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
397 if (p == NULL)
398 return 0;
399
400 PoolFree(p);
401 return 1;
402}
403
404static int PoolTestInit02 (void)
405{
406 int retval = 0;
407
408 Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
409 if (p == NULL)
410 goto end;
411
412 if (p->alloc_stack == NULL || p->empty_stack == NULL) {
413 printf("list(s) not properly initialized (a:%p e:%p): ",
414 p->alloc_stack, p->empty_stack);
415 retval = 0;
416 goto end;
417 }
418
419 if (p->Alloc != PoolTestAlloc) {
420 printf("Alloc func ptr %p != %p: ",
421 p->Alloc, PoolTestAlloc);
422 retval = 0;
423 goto end;
424 }
425
426 if (p->Cleanup != PoolTestFree) {
427 printf("Free func ptr %p != %p: ",
428 p->Cleanup, PoolTestFree);
429 retval = 0;
430 goto end;
431 }
432
433 retval = 1;
434end:
435 if (p != NULL)
436 PoolFree(p);
437 return retval;
438}
439
440static int PoolTestInit03 (void)
441{
442 int retval = 0;
443 void *data = NULL;
444
445 Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
446 if (p == NULL)
447 goto end;
448
449 data = PoolGet(p);
450 if (data == NULL) {
451 printf("PoolGet returned NULL: ");
452 retval = 0;
453 goto end;
454 }
455
456 if (p->alloc_stack_size != 4) {
457 printf("p->alloc_stack_size 4 != %" PRIu32 ": ", p->alloc_stack_size);
458 retval = 0;
459 goto end;
460 }
461
462 if (p->empty_stack_size != 6) {
463 printf("p->empty_stack_size 6 != %" PRIu32 ": ", p->empty_stack_size);
464 retval = 0;
465 goto end;
466 }
467
468 retval = 1;
469end:
470 if (p != NULL)
471 PoolFree(p);
472 return retval;
473}
474
475static int PoolTestInit04 (void)
476{
477 int retval = 0;
478 char *str = NULL;
479
480 Pool *p = PoolInit(10,5,strlen("test") + 1,NULL, PoolTestInitArg,(void *)"test",PoolTestFree, NULL);
481 if (p == NULL)
482 goto end;
483
484 str = PoolGet(p);
485 if (str == NULL) {
486 printf("PoolGet returned NULL: ");
487 retval = 0;
488 goto end;
489 }
490
491 if (strcmp(str, "test") != 0) {
492 printf("Memory not properly initialized: ");
493 retval = 0;
494 goto end;
495 }
496
497 if (p->alloc_stack_size != 4) {
498 printf("p->alloc_stack_size 4 != %" PRIu32 ": ", p->alloc_stack_size);
499 retval = 0;
500 goto end;
501 }
502
503 if (p->empty_stack_size != 6) {
504 printf("p->empty_stack_size 6 != %" PRIu32 ": ", p->empty_stack_size);
505 retval = 0;
506 goto end;
507 }
508
509 retval = 1;
510end:
511 if (p != NULL)
512 PoolFree(p);
513 return retval;
514}
515
516static int PoolTestInit05 (void)
517{
518 int retval = 0;
519 void *data = NULL;
520
521 Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL, NULL,PoolTestFree, NULL);
522 if (p == NULL)
523 goto end;
524
525 data = PoolGet(p);
526 if (data == NULL) {
527 printf("PoolGet returned NULL: ");
528 retval = 0;
529 goto end;
530 }
531
532 if (p->alloc_stack_size != 4) {
533 printf("p->alloc_stack_size 4 != %" PRIu32 ": ", p->alloc_stack_size);
534 retval = 0;
535 goto end;
536 }
537
538 if (p->empty_stack_size != 6) {
539 printf("p->empty_stack_size 6 != %" PRIu32 ": ", p->empty_stack_size);
540 retval = 0;
541 goto end;
542 }
543
544 PoolReturn(p, data);
545 data = NULL;
546
547 if (p->alloc_stack_size != 5) {
548 printf("p->alloc_stack_size 5 != %" PRIu32 ": ", p->alloc_stack_size);
549 retval = 0;
550 goto end;
551 }
552
553 if (p->empty_stack_size != 5) {
554 printf("p->empty_stack_size 5 != %" PRIu32 ": ", p->empty_stack_size);
555 retval = 0;
556 goto end;
557 }
558
559 retval = 1;
560end:
561 if (p != NULL)
562 PoolFree(p);
563 return retval;
564}
565
566static int PoolTestInit06 (void)
567{
568 int retval = 0;
569 void *data = NULL;
570 void *data2 = NULL;
571
572 Pool *p = PoolInit(1,0,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
573 if (p == NULL)
574 goto end;
575
576 if (p->allocated != 0) {
577 printf("p->allocated 0 != %" PRIu32 ": ", p->allocated);
578 retval = 0;
579 goto end;
580 }
581
582 data = PoolGet(p);
583 if (data == NULL) {
584 printf("PoolGet returned NULL: ");
585 retval = 0;
586 goto end;
587 }
588
589 if (p->allocated != 1) {
590 printf("p->allocated 1 != %" PRIu32 ": ", p->allocated);
591 retval = 0;
592 goto end;
593 }
594
595 data2 = PoolGet(p);
596 if (data2 != NULL) {
597 printf("PoolGet returned %p, expected NULL: ", data2);
598 retval = 0;
599 goto end;
600 }
601
602 PoolReturn(p,data);
603 data = NULL;
604
605 if (p->allocated != 1) {
606 printf("p->allocated 1 != %" PRIu32 ": ", p->allocated);
607 retval = 0;
608 goto end;
609 }
610
611 if (p->alloc_stack_size != 1) {
612 printf("p->alloc_stack_size 1 != %" PRIu32 ": ", p->alloc_stack_size);
613 retval = 0;
614 goto end;
615 }
616
617 retval = 1;
618end:
619 if (p != NULL)
620 PoolFree(p);
621 return retval;
622}
623
624/** \test pool with unlimited size */
625static int PoolTestInit07 (void)
626{
627 int retval = 0;
628 void *data = NULL;
629 void *data2 = NULL;
630
631 Pool *p = PoolInit(0,1,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
632 if (p == NULL)
633 goto end;
634
635 if (p->max_buckets != 0) {
636 printf("p->max_buckets 0 != %" PRIu32 ": ", p->max_buckets);
637 retval = 0;
638 goto end;
639 }
640
641 if (p->allocated != 1) {
642 printf("p->allocated 1 != %" PRIu32 ": ", p->allocated);
643 retval = 0;
644 goto end;
645 }
646
647 data = PoolGet(p);
648 if (data == NULL) {
649 printf("PoolGet returned NULL: ");
650 retval = 0;
651 goto end;
652 }
653
654 if (p->allocated != 1) {
655 printf("(2) p->allocated 1 != %" PRIu32 ": ", p->allocated);
656 retval = 0;
657 goto end;
658 }
659
660 data2 = PoolGet(p);
661 if (data2 == NULL) {
662 printf("PoolGet returned NULL: ");
663 retval = 0;
664 goto end;
665 }
666
667 if (p->allocated != 2) {
668 printf("(3) p->allocated 2 != %" PRIu32 ": ", p->allocated);
669 retval = 0;
670 goto end;
671 }
672
673 PoolReturn(p,data);
674 data = NULL;
675
676 if (p->allocated != 2) {
677 printf("(4) p->allocated 2 != %" PRIu32 ": ", p->allocated);
678 retval = 0;
679 goto end;
680 }
681
682 if (p->alloc_stack_size != 1) {
683 printf("p->alloc_stack_size 1 != %" PRIu32 ": ", p->alloc_stack_size);
684 retval = 0;
685 goto end;
686 }
687
688 PoolReturn(p,data2);
689 data2 = NULL;
690
691 if (p->allocated != 1) {
692 printf("(5) p->allocated 1 != %" PRIu32 ": ", p->allocated);
693 retval = 0;
694 goto end;
695 }
696
697 retval = 1;
698end:
699 if (p != NULL)
700 PoolFree(p);
701 return retval;
702}
703#endif /* UNITTESTS */
704
706{
707#ifdef UNITTESTS
708 UtRegisterTest("PoolTestInit01", PoolTestInit01);
709 UtRegisterTest("PoolTestInit02", PoolTestInit02);
710 UtRegisterTest("PoolTestInit03", PoolTestInit03);
711 UtRegisterTest("PoolTestInit04", PoolTestInit04);
712 UtRegisterTest("PoolTestInit05", PoolTestInit05);
713 UtRegisterTest("PoolTestInit06", PoolTestInit06);
714 UtRegisterTest("PoolTestInit07", PoolTestInit07);
715
717#endif /* UNITTESTS */
718}
719
720
721/**
722 * @}
723 */
uint8_t len
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
void PoolFree(Pool *p)
Definition util-pool.c:223
Pool * PoolInit(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 *))
Init a Pool.
Definition util-pool.c:84
void PoolReturn(Pool *p, void *data)
Definition util-pool.c:329
void PoolThreadRegisterTests(void)
void PoolRegisterTests(void)
Definition util-pool.c:705
void * PoolGet(Pool *p)
Definition util-pool.c:271
void * data
Definition util-pool.h:37
struct PoolBucket_ * next
Definition util-pool.h:39
uint8_t flags
Definition util-pool.h:38
uint32_t alloc_stack_size
Definition util-pool.h:49
uint32_t max_buckets
Definition util-pool.h:44
PoolBucket * empty_stack
Definition util-pool.h:53
void(* Cleanup)(void *)
Definition util-pool.h:63
int(* Init)(void *, void *)
Definition util-pool.h:61
uint32_t allocated
Definition util-pool.h:46
uint32_t preallocated
Definition util-pool.h:45
uint32_t elt_size
Definition util-pool.h:66
int data_buffer_size
Definition util-pool.h:56
PoolBucket * pb_buffer
Definition util-pool.h:58
uint32_t empty_stack_size
Definition util-pool.h:54
void * InitData
Definition util-pool.h:62
uint32_t outstanding
Definition util-pool.h:67
void * data_buffer
Definition util-pool.h:57
void *(* Alloc)(void)
Definition util-pool.h:60
void(* Free)(void *)
Definition util-pool.h:64
PoolBucket * alloc_stack
Definition util-pool.h:51
#define str(s)
size_t strlcpy(char *dst, const char *src, size_t siz)
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnPtr(x, type)
Definition util-debug.h:293
#define SCReturn
Definition util-debug.h:279
thread_local SCError sc_errno
Definition util-error.c:31
@ SC_EINVAL
Definition util-error.h:30
@ SC_ENOMEM
Definition util-error.h:29
@ SC_OK
Definition util-error.h:27
#define SCMalloc(sz)
Definition util-mem.h:47
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)
#define POOL_BUCKET_PREALLOCATED
Definition util-pool.h:33