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  return delta >= 0 && delta <= p->data_buffer_size;
62 }
63 
64 /** \brief Init a Pool
65  *
66  * PoolInit() creates a ::Pool. The Alloc function must only do
67  * allocation stuff. The Cleanup function must not try to free
68  * the PoolBucket::data. This is done by the ::Pool management
69  * system.
70  *
71  * \param size
72  * \param prealloc_size
73  * \param elt_size Memory size of an element
74  * \param Alloc An allocation function or NULL to use a standard SCMalloc
75  * \param Init An init function or NULL to use a standard memset to 0
76  * \param InitData Init data
77  * \param Cleanup a free function or NULL if no special treatment is needed
78  * \param Free free func
79  * \retval the allocated Pool
80  */
81 Pool *PoolInit(uint32_t size, uint32_t prealloc_size, uint32_t elt_size,
82  void *(*Alloc)(void), int (*Init)(void *, void *), void *InitData,
83  void (*Cleanup)(void *), void (*Free)(void *))
84 {
85  sc_errno = SC_OK;
86 
87  Pool *p = NULL;
88 
89  if (size != 0 && prealloc_size > size) {
91  goto error;
92  }
93  if (size != 0 && elt_size == 0) {
95  goto error;
96  }
97  if (elt_size && Free) {
99  goto error;
100  }
101  if (elt_size == 0 && Alloc == NULL) {
103  goto error;
104  }
105 
106  /* setup the filter */
107  p = SCCalloc(1, sizeof(Pool));
108  if (unlikely(p == NULL)) {
110  goto error;
111  }
112 
113  p->max_buckets = size;
114  p->preallocated = prealloc_size;
115  p->elt_size = elt_size;
116  p->data_buffer_size = prealloc_size * elt_size;
117  p->Alloc = Alloc;
118  p->Init = Init;
119  p->InitData = InitData;
120  p->Cleanup = Cleanup;
121  p->Free = Free;
122  if (p->Init == NULL) {
123  p->Init = PoolMemset;
124  p->InitData = p;
125  }
126 
127  /* alloc the buckets and place them in the empty list */
128  uint32_t u32 = 0;
129  if (size > 0) {
130  PoolBucket *pb = SCCalloc(size, sizeof(PoolBucket));
131  if (unlikely(pb == NULL)) {
133  goto error;
134  }
135  memset(pb, 0, size * sizeof(PoolBucket));
136  p->pb_buffer = pb;
137  for (u32 = 0; u32 < size; u32++) {
138  /* populate pool */
139  pb->next = p->empty_stack;
141  p->empty_stack = pb;
142  p->empty_stack_size++;
143  pb++;
144  }
145 
146  p->data_buffer = SCCalloc(prealloc_size, elt_size);
147  /* FIXME better goto */
148  if (p->data_buffer == NULL) {
150  goto error;
151  }
152  }
153  /* prealloc the buckets and requeue them to the alloc list */
154  for (u32 = 0; u32 < prealloc_size; u32++) {
155  if (size == 0) { /* unlimited */
156  PoolBucket *pb = SCCalloc(1, sizeof(PoolBucket));
157  if (unlikely(pb == NULL)) {
159  goto error;
160  }
161 
162  if (p->Alloc) {
163  pb->data = p->Alloc();
164  } else {
165  pb->data = SCMalloc(p->elt_size);
166  }
167  if (pb->data == NULL) {
168  SCFree(pb);
170  goto error;
171  }
172  if (p->Init(pb->data, p->InitData) != 1) {
173  if (p->Free)
174  p->Free(pb->data);
175  else
176  SCFree(pb->data);
177  SCFree(pb);
179  goto error;
180  }
181  p->allocated++;
182 
183  pb->next = p->alloc_stack;
184  p->alloc_stack = pb;
185  p->alloc_stack_size++;
186  } else {
187  PoolBucket *pb = p->empty_stack;
188  if (pb == NULL) {
190  goto error;
191  }
192 
193  pb->data = (char *)p->data_buffer + u32 * elt_size;
194  if (p->Init(pb->data, p->InitData) != 1) {
195  pb->data = NULL;
197  goto error;
198  }
199 
200  p->empty_stack = pb->next;
201  p->empty_stack_size--;
202 
203  p->allocated++;
204 
205  pb->next = p->alloc_stack;
206  p->alloc_stack = pb;
207  p->alloc_stack_size++;
208  }
209  }
210 
211  return p;
212 
213 error:
214  if (p != NULL) {
215  PoolFree(p);
216  }
217  return NULL;
218 }
219 
220 void PoolFree(Pool *p)
221 {
222  if (p == NULL)
223  return;
224 
225  while (p->alloc_stack != NULL) {
226  PoolBucket *pb = p->alloc_stack;
227  p->alloc_stack = pb->next;
228  if (p->Cleanup)
229  p->Cleanup(pb->data);
230  if (!PoolDataPreAllocated(p, pb->data)) {
231  if (p->Free)
232  p->Free(pb->data);
233  else
234  SCFree(pb->data);
235  }
236  pb->data = NULL;
237  if (!(pb->flags & POOL_BUCKET_PREALLOCATED)) {
238  SCFree(pb);
239  }
240  }
241 
242  while (p->empty_stack != NULL) {
243  PoolBucket *pb = p->empty_stack;
244  p->empty_stack = pb->next;
245  if (pb->data!= NULL) {
246  if (p->Cleanup)
247  p->Cleanup(pb->data);
248  if (!PoolDataPreAllocated(p, pb->data)) {
249  if (p->Free)
250  p->Free(pb->data);
251  else
252  SCFree(pb->data);
253  }
254  pb->data = NULL;
255  }
256  if (!(pb->flags & POOL_BUCKET_PREALLOCATED)) {
257  SCFree(pb);
258  }
259  }
260 
261  if (p->pb_buffer)
262  SCFree(p->pb_buffer);
263  if (p->data_buffer)
264  SCFree(p->data_buffer);
265  SCFree(p);
266 }
267 
268 void *PoolGet(Pool *p)
269 {
270  SCEnter();
271 
272  PoolBucket *pb = p->alloc_stack;
273  if (pb != NULL) {
274  /* pull from the alloc list */
275  p->alloc_stack = pb->next;
276  p->alloc_stack_size--;
277 
278  /* put in the empty list */
279  pb->next = p->empty_stack;
280  p->empty_stack = pb;
281  p->empty_stack_size++;
282  } else {
283  if (p->max_buckets == 0 || p->allocated < p->max_buckets) {
284  void *pitem;
285  SCLogDebug("max_buckets %"PRIu32"", p->max_buckets);
286 
287  if (p->Alloc != NULL) {
288  pitem = p->Alloc();
289  } else {
290  pitem = SCMalloc(p->elt_size);
291  }
292 
293  if (pitem != NULL) {
294  if (p->Init(pitem, p->InitData) != 1) {
295  if (p->Free != NULL)
296  p->Free(pitem);
297  else
298  SCFree(pitem);
299  SCReturnPtr(NULL, "void");
300  }
301 
302  p->allocated++;
303  p->outstanding++;
304 #ifdef DEBUG
305  if (p->outstanding > p->max_outstanding)
306  p->max_outstanding = p->outstanding;
307 #endif
308  }
309 
310  SCReturnPtr(pitem, "void");
311  } else {
312  SCReturnPtr(NULL, "void");
313  }
314  }
315 
316  void *ptr = pb->data;
317  pb->data = NULL;
318  p->outstanding++;
319 #ifdef DEBUG
320  if (p->outstanding > p->max_outstanding)
321  p->max_outstanding = p->outstanding;
322 #endif
323  SCReturnPtr(ptr,"void");
324 }
325 
326 void PoolReturn(Pool *p, void *data)
327 {
328  SCEnter();
329 
330  PoolBucket *pb = p->empty_stack;
331 
332  SCLogDebug("pb %p", pb);
333 
334  if (pb == NULL) {
335  p->allocated--;
336  p->outstanding--;
337  if (p->Cleanup != NULL) {
338  p->Cleanup(data);
339  }
340  if (!PoolDataPreAllocated(p, data)) {
341  if (p->Free)
342  p->Free(data);
343  else
344  SCFree(data);
345  }
346 
347  SCLogDebug("tried to return data %p to the pool %p, but no more "
348  "buckets available. Just freeing the data.", data, p);
349  SCReturn;
350  }
351 
352  /* pull from the alloc list */
353  p->empty_stack = pb->next;
354  p->empty_stack_size--;
355 
356  /* put in the alloc list */
357  pb->next = p->alloc_stack;
358  p->alloc_stack = pb;
359  p->alloc_stack_size++;
360 
361  pb->data = data;
362  p->outstanding--;
363  SCReturn;
364 }
365 
366 /*
367  * ONLY TESTS BELOW THIS COMMENT
368  */
369 
370 #ifdef UNITTESTS
371 static void *PoolTestAlloc(void)
372 {
373  void *ptr = SCMalloc(10);
374  if (unlikely(ptr == NULL))
375  return NULL;
376  return ptr;
377 }
378 static int PoolTestInitArg(void *data, void *allocdata)
379 {
380  size_t len = strlen((char *)allocdata) + 1;
381  char *str = data;
382  if (str != NULL)
383  strlcpy(str,(char *)allocdata,len);
384  return 1;
385 }
386 
387 static void PoolTestFree(void *ptr)
388 {
389 }
390 
391 static int PoolTestInit01 (void)
392 {
393  Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
394  FAIL_IF_NOT(p != NULL);
395 
396  PoolFree(p);
397  PASS;
398 }
399 
400 static int PoolTestInit02 (void)
401 {
402  Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
403  FAIL_IF_NOT(p != NULL);
404 
405  FAIL_IF_NOT(p->alloc_stack != NULL);
406 
407  FAIL_IF_NOT(p->empty_stack != NULL);
408 
409  FAIL_IF_NOT(p->Alloc == PoolTestAlloc);
410 
411  FAIL_IF_NOT(p->Cleanup == PoolTestFree);
412 
413  PoolFree(p);
414  PASS;
415 }
416 
417 static int PoolTestInit03 (void)
418 {
419  Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
420  FAIL_IF_NOT(p != NULL);
421 
422  void *data = PoolGet(p);
423  FAIL_IF_NOT(data != NULL);
424 
425  FAIL_IF_NOT(p->alloc_stack_size == 4);
426 
427  FAIL_IF_NOT(p->empty_stack_size == 6);
428 
429  PoolFree(p);
430  PASS;
431 }
432 
433 static int PoolTestInit04 (void)
434 {
435  Pool *p = PoolInit(10,5,strlen("test") + 1,NULL, PoolTestInitArg,(void *)"test",PoolTestFree, NULL);
436  FAIL_IF_NOT(p != NULL);
437 
438  char *str = PoolGet(p);
439  FAIL_IF_NOT(str != NULL);
440 
441  FAIL_IF_NOT(strcmp(str, "test") == 0);
442 
443  FAIL_IF_NOT(p->alloc_stack_size == 4);
444 
445  FAIL_IF_NOT(p->empty_stack_size == 6);
446 
447  PoolFree(p);
448  PASS;
449 }
450 
451 static int PoolTestInit05 (void)
452 {
453  Pool *p = PoolInit(10,5,10,PoolTestAlloc,NULL, NULL,PoolTestFree, NULL);
454  FAIL_IF_NOT(p != NULL);
455 
456  void *data = PoolGet(p);
457  FAIL_IF_NOT(data != NULL);
458 
459  FAIL_IF_NOT(p->alloc_stack_size == 4);
460 
461  FAIL_IF_NOT(p->empty_stack_size == 6);
462 
463  PoolReturn(p, data);
464  data = NULL;
465 
466  FAIL_IF_NOT(p->alloc_stack_size == 5);
467 
468  FAIL_IF_NOT(p->empty_stack_size == 5);
469 
470  PoolFree(p);
471  PASS;
472 }
473 
474 static int PoolTestInit06 (void)
475 {
476  Pool *p = PoolInit(1,0,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
477  FAIL_IF_NOT(p != NULL);
478 
479  FAIL_IF_NOT(p->allocated == 0);
480 
481  void *data = PoolGet(p);
482  FAIL_IF_NOT(data != NULL);
483 
484  FAIL_IF_NOT(p->allocated == 1);
485 
486  void *data2 = PoolGet(p);
487  FAIL_IF_NOT(data2 == NULL);
488 
489  PoolReturn(p,data);
490  data = NULL;
491 
492  FAIL_IF_NOT(p->allocated == 1);
493 
494  FAIL_IF_NOT(p->alloc_stack_size == 1);
495 
496  PoolFree(p);
497  PASS;
498 }
499 
500 /** \test pool with unlimited size */
501 static int PoolTestInit07 (void)
502 {
503  Pool *p = PoolInit(0,1,10,PoolTestAlloc,NULL,NULL,PoolTestFree, NULL);
504  FAIL_IF_NOT(p != NULL);
505 
506  FAIL_IF_NOT(p->max_buckets == 0);
507 
508  FAIL_IF_NOT(p->allocated == 1);
509 
510  void *data = PoolGet(p);
511  FAIL_IF_NOT(data != NULL);
512 
513  FAIL_IF_NOT(p->allocated == 1);
514 
515  void *data2 = PoolGet(p);
516  FAIL_IF_NOT(data2 != NULL);
517 
518  FAIL_IF_NOT(p->allocated == 2);
519 
520  PoolReturn(p,data);
521  data = NULL;
522 
523  FAIL_IF_NOT(p->allocated == 2);
524 
525  FAIL_IF_NOT(p->alloc_stack_size == 1);
526 
527  PoolReturn(p,data2);
528  data2 = NULL;
529 
530  FAIL_IF_NOT(p->allocated == 1);
531 
532  PoolFree(p);
533  PASS;
534 }
535 #endif /* UNITTESTS */
536 
538 {
539 #ifdef UNITTESTS
540  UtRegisterTest("PoolTestInit01", PoolTestInit01);
541  UtRegisterTest("PoolTestInit02", PoolTestInit02);
542  UtRegisterTest("PoolTestInit03", PoolTestInit03);
543  UtRegisterTest("PoolTestInit04", PoolTestInit04);
544  UtRegisterTest("PoolTestInit05", PoolTestInit05);
545  UtRegisterTest("PoolTestInit06", PoolTestInit06);
546  UtRegisterTest("PoolTestInit07", PoolTestInit07);
547 
549 #endif /* UNITTESTS */
550 }
551 
552 
553 /**
554  * @}
555  */
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:282
POOL_BUCKET_PREALLOCATED
#define POOL_BUCKET_PREALLOCATED
Definition: util-pool.h:33
util-pool-thread.h
PoolRegisterTests
void PoolRegisterTests(void)
Definition: util-pool.c:537
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
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
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
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
PoolBucket_::next
struct PoolBucket_ * next
Definition: util-pool.h:39
SCEnter
#define SCEnter(...)
Definition: util-debug.h:284
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:286
PoolFree
void PoolFree(Pool *p)
Definition: util-pool.c:220
SC_OK
@ SC_OK
Definition: util-error.h:27
SCReturnPtr
#define SCReturnPtr(x, type)
Definition: util-debug.h:300
PoolBucket_
Definition: util-pool.h:36
PoolReturn
void PoolReturn(Pool *p, void *data)
Definition: util-pool.c:326
Pool_::max_buckets
uint32_t max_buckets
Definition: util-pool.h:44
PoolThreadRegisterTests
void PoolThreadRegisterTests(void)
Definition: util-pool-thread.c:402
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:308
PoolGet
void * PoolGet(Pool *p)
Definition: util-pool.c:268
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:81
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