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 
47 static 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 */
58 static 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  */
84 Pool *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 {
88  sc_errno = SC_OK;
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 
216 error:
217  if (p != NULL) {
218  PoolFree(p);
219  }
220  return NULL;
221 }
222 
223 void PoolFree(Pool *p)
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)
267  SCFree(p->data_buffer);
268  SCFree(p);
269 }
270 
271 void *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 
329 void 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
374 static void *PoolTestAlloc(void)
375 {
376  void *ptr = SCMalloc(10);
377  if (unlikely(ptr == NULL))
378  return NULL;
379  return ptr;
380 }
381 static 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 
390 static void PoolTestFree(void *ptr)
391 {
392 }
393 
394 static 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 
404 static 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;
434 end:
435  if (p != NULL)
436  PoolFree(p);
437  return retval;
438 }
439 
440 static 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;
469 end:
470  if (p != NULL)
471  PoolFree(p);
472  return retval;
473 }
474 
475 static 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;
510 end:
511  if (p != NULL)
512  PoolFree(p);
513  return retval;
514 }
515 
516 static 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;
560 end:
561  if (p != NULL)
562  PoolFree(p);
563  return retval;
564 }
565 
566 static 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;
618 end:
619  if (p != NULL)
620  PoolFree(p);
621  return retval;
622 }
623 
624 /** \test pool with unlimited size */
625 static 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;
698 end:
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  */
len
uint8_t len
Definition: app-layer-dnp3.h:2
Pool_::pb_buffer
PoolBucket * pb_buffer
Definition: util-pool.h:58
Pool_::Free
void(* Free)(void *)
Definition: util-pool.h:64
Pool_::Init
int(* Init)(void *, void *)
Definition: util-pool.h:61
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
POOL_BUCKET_PREALLOCATED
#define POOL_BUCKET_PREALLOCATED
Definition: util-pool.h:33
util-pool-thread.h
PoolRegisterTests
void PoolRegisterTests(void)
Definition: util-pool.c:705
SC_EINVAL
@ SC_EINVAL
Definition: util-error.h:30
Pool_::alloc_stack
PoolBucket * alloc_stack
Definition: util-pool.h:51
Pool_
Definition: util-pool.h:43
Pool_::Cleanup
void(* Cleanup)(void *)
Definition: util-pool.h:63
util-unittest.h
SC_ENOMEM
@ SC_ENOMEM
Definition: util-error.h:29
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
util-debug.h
Pool_::empty_stack_size
uint32_t empty_stack_size
Definition: util-pool.h:54
PoolBucket_::next
struct PoolBucket_ * next
Definition: util-pool.h:39
SCEnter
#define SCEnter(...)
Definition: util-debug.h:271
Pool_::outstanding
uint32_t outstanding
Definition: util-pool.h:67
PoolBucket_::data
void * data
Definition: util-pool.h:37
SCReturn
#define SCReturn
Definition: util-debug.h:273
PoolFree
void PoolFree(Pool *p)
Definition: util-pool.c:223
SC_OK
@ SC_OK
Definition: util-error.h:27
SCReturnPtr
#define SCReturnPtr(x, type)
Definition: util-debug.h:287
PoolBucket_
Definition: util-pool.h:36
PoolReturn
void PoolReturn(Pool *p, void *data)
Definition: util-pool.c:329
Pool_::max_buckets
uint32_t max_buckets
Definition: util-pool.h:44
PoolThreadRegisterTests
void PoolThreadRegisterTests(void)
Definition: util-pool-thread.c:401
suricata-common.h
Pool_::Alloc
void *(* Alloc)(void)
Definition: util-pool.h:60
Pool_::preallocated
uint32_t preallocated
Definition: util-pool.h:45
Pool_::allocated
uint32_t allocated
Definition: util-pool.h:46
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
str
#define str(s)
Definition: suricata-common.h:291
PoolGet
void * PoolGet(Pool *p)
Definition: util-pool.c:271
Pool_::data_buffer_size
int data_buffer_size
Definition: util-pool.h:56
Pool_::empty_stack
PoolBucket * empty_stack
Definition: util-pool.h:53
SCFree
#define SCFree(p)
Definition: util-mem.h:61
sc_errno
thread_local SCError sc_errno
Definition: util-error.c:31
PoolBucket_::flags
uint8_t flags
Definition: util-pool.h:38
Pool_::elt_size
uint32_t elt_size
Definition: util-pool.h:66
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
util-pool.h
PoolInit
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
Pool_::InitData
void * InitData
Definition: util-pool.h:62
Pool_::alloc_stack_size
uint32_t alloc_stack_size
Definition: util-pool.h:49
Pool_::data_buffer
void * data_buffer
Definition: util-pool.h:57