/src/suricata/src/util-pool-thread.c
Line | Count | Source |
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 | | #include "util-validate.h" |
38 | | |
39 | | /** |
40 | | * \brief per thread Pool, initialization function |
41 | | * \param thread number of threads this is for. Can start with 1 and be expanded. |
42 | | * Other params are as for PoolInit() |
43 | | */ |
44 | | PoolThread *PoolThreadInit(int threads, uint32_t size, uint32_t prealloc_size, |
45 | | uint32_t elt_size, void *(*Alloc)(void), int (*Init)(void *, void *), |
46 | | void *InitData, void (*Cleanup)(void *), void (*Free)(void *)) |
47 | 8 | { |
48 | 8 | sc_errno = SC_OK; |
49 | | |
50 | 8 | if (threads <= 0) { |
51 | 0 | SCLogDebug("error"); |
52 | 0 | sc_errno = SC_EINVAL; |
53 | 0 | return NULL; |
54 | 0 | } |
55 | | |
56 | 8 | PoolThread *pt = SCCalloc(1, sizeof(*pt)); |
57 | 8 | if (unlikely(pt == NULL)) { |
58 | 0 | SCLogDebug("memory alloc error"); |
59 | 0 | sc_errno = SC_ENOMEM; |
60 | 0 | goto error; |
61 | 0 | } |
62 | | |
63 | 8 | SCLogDebug("size %d", threads); |
64 | 8 | pt->array = SCMalloc(threads * sizeof(PoolThreadElement)); |
65 | 8 | if (pt->array == NULL) { |
66 | 0 | SCLogDebug("memory alloc error"); |
67 | 0 | sc_errno = SC_ENOMEM; |
68 | 0 | goto error; |
69 | 0 | } |
70 | 8 | pt->size = threads; |
71 | | |
72 | 16 | for (int i = 0; i < threads; i++) { |
73 | 8 | PoolThreadElement *e = &pt->array[i]; |
74 | | |
75 | 8 | SCMutexInit(&e->lock, NULL); |
76 | 8 | SCMutexLock(&e->lock); |
77 | | // SCLogDebug("size %u prealloc_size %u elt_size %u Alloc %p Init %p InitData %p Cleanup %p Free %p", |
78 | | // size, prealloc_size, elt_size, |
79 | | // Alloc, Init, InitData, Cleanup, Free); |
80 | 8 | e->pool = PoolInit(size, prealloc_size, elt_size, Alloc, Init, InitData, Cleanup, Free); |
81 | 8 | SCMutexUnlock(&e->lock); |
82 | 8 | if (e->pool == NULL) { |
83 | 0 | SCLogDebug("error"); |
84 | 0 | goto error; |
85 | 0 | } |
86 | 8 | } |
87 | | |
88 | 8 | return pt; |
89 | 0 | error: |
90 | 0 | if (pt != NULL) |
91 | 0 | PoolThreadFree(pt); |
92 | 0 | return NULL; |
93 | 8 | } |
94 | | |
95 | | /** \brief expand pool by one for a new thread |
96 | | * \retval -1 or pool thread id |
97 | | */ |
98 | | int PoolThreadExpand(PoolThread *pt) |
99 | 0 | { |
100 | 0 | if (pt == NULL || pt->array == NULL || pt->size == 0) { |
101 | 0 | SCLogError("pool grow failed"); |
102 | 0 | return -1; |
103 | 0 | } |
104 | | |
105 | 0 | size_t newsize = pt->size + 1; |
106 | 0 | SCLogDebug("newsize %"PRIuMAX, (uintmax_t)newsize); |
107 | |
|
108 | 0 | void *ptmp = SCRealloc(pt->array, (newsize * sizeof(PoolThreadElement))); |
109 | 0 | if (ptmp == NULL) { |
110 | 0 | SCFree(pt->array); |
111 | 0 | pt->array = NULL; |
112 | 0 | SCLogError("pool grow failed"); |
113 | 0 | return -1; |
114 | 0 | } |
115 | 0 | pt->array = ptmp; |
116 | 0 | pt->size = newsize; |
117 | | |
118 | | /* copy settings from first thread that registered the pool */ |
119 | 0 | Pool settings; |
120 | 0 | memset(&settings, 0x0, sizeof(settings)); |
121 | 0 | PoolThreadElement *e = &pt->array[0]; |
122 | 0 | SCMutexLock(&e->lock); |
123 | 0 | settings.max_buckets = e->pool->max_buckets; |
124 | 0 | settings.preallocated = e->pool->preallocated; |
125 | 0 | settings.elt_size = e->pool->elt_size; |
126 | 0 | settings.Alloc = e->pool->Alloc; |
127 | 0 | settings.Init = e->pool->Init; |
128 | 0 | settings.InitData = e->pool->InitData; |
129 | 0 | settings.Cleanup = e->pool->Cleanup; |
130 | 0 | settings.Free = e->pool->Free; |
131 | 0 | SCMutexUnlock(&e->lock); |
132 | |
|
133 | 0 | e = &pt->array[newsize - 1]; |
134 | 0 | memset(e, 0x00, sizeof(*e)); |
135 | 0 | SCMutexInit(&e->lock, NULL); |
136 | 0 | SCMutexLock(&e->lock); |
137 | 0 | e->pool = PoolInit(settings.max_buckets, settings.preallocated, |
138 | 0 | settings.elt_size, settings.Alloc, settings.Init, settings.InitData, |
139 | 0 | settings.Cleanup, settings.Free); |
140 | 0 | SCMutexUnlock(&e->lock); |
141 | 0 | if (e->pool == NULL) { |
142 | 0 | SCLogError("pool grow failed"); |
143 | 0 | return -1; |
144 | 0 | } |
145 | | |
146 | 0 | return (int)(newsize - 1); |
147 | 0 | } |
148 | | |
149 | | int PoolThreadSize(PoolThread *pt) |
150 | 0 | { |
151 | 0 | if (pt == NULL) |
152 | 0 | return -1; |
153 | 0 | return (int)pt->size; |
154 | 0 | } |
155 | | |
156 | | void PoolThreadFree(PoolThread *pt) |
157 | 0 | { |
158 | 0 | if (pt == NULL) |
159 | 0 | return; |
160 | | |
161 | 0 | if (pt->array != NULL) { |
162 | 0 | for (int i = 0; i < (int)pt->size; i++) { |
163 | 0 | PoolThreadElement *e = &pt->array[i]; |
164 | 0 | SCMutexLock(&e->lock); |
165 | 0 | PoolFree(e->pool); |
166 | 0 | SCMutexUnlock(&e->lock); |
167 | 0 | SCMutexDestroy(&e->lock); |
168 | 0 | } |
169 | 0 | SCFree(pt->array); |
170 | 0 | } |
171 | 0 | SCFree(pt); |
172 | 0 | } |
173 | | |
174 | | void *PoolThreadGetById(PoolThread *pt, uint16_t id) |
175 | 3.88M | { |
176 | 3.88M | void *data = NULL; |
177 | | |
178 | 3.88M | if (pt == NULL || id >= pt->size) |
179 | 0 | return NULL; |
180 | | |
181 | 3.88M | PoolThreadElement *e = &pt->array[id]; |
182 | 3.88M | SCMutexLock(&e->lock); |
183 | 3.88M | data = PoolGet(e->pool); |
184 | 3.88M | SCMutexUnlock(&e->lock); |
185 | 3.88M | if (data) { |
186 | 3.88M | PoolThreadId *did = data; |
187 | 3.88M | *did = id; |
188 | 3.88M | } |
189 | | |
190 | 3.88M | return data; |
191 | 3.88M | } |
192 | | |
193 | | void PoolThreadReturn(PoolThread *pt, void *data) |
194 | 0 | { |
195 | 0 | PoolThreadId *id = data; |
196 | |
|
197 | 0 | if (pt == NULL || *id >= pt->size) |
198 | 0 | return; |
199 | | |
200 | 0 | SCLogDebug("returning to id %u", *id); |
201 | |
|
202 | 0 | PoolThreadElement *e = &pt->array[*id]; |
203 | 0 | SCMutexLock(&e->lock); |
204 | 0 | PoolReturn(e->pool, data); |
205 | 0 | SCMutexUnlock(&e->lock); |
206 | 0 | } |
207 | | |
208 | | void PoolThreadLock(PoolThread *pt, PoolThreadId id) |
209 | 60.6k | { |
210 | 60.6k | DEBUG_VALIDATE_BUG_ON(pt == NULL || id >= pt->size); |
211 | 60.6k | PoolThreadElement *e = &pt->array[id]; |
212 | 60.6k | SCMutexLock(&e->lock); |
213 | 60.6k | } |
214 | | |
215 | | void PoolThreadReturnRaw(PoolThread *pt, PoolThreadId id, void *data) |
216 | 3.88M | { |
217 | 3.88M | DEBUG_VALIDATE_BUG_ON(pt == NULL || id >= pt->size); |
218 | 3.88M | PoolThreadElement *e = &pt->array[id]; |
219 | 3.88M | PoolReturn(e->pool, data); |
220 | 3.88M | } |
221 | | |
222 | | void PoolThreadUnlock(PoolThread *pt, PoolThreadId id) |
223 | 60.6k | { |
224 | 60.6k | DEBUG_VALIDATE_BUG_ON(pt == NULL || id >= pt->size); |
225 | 60.6k | PoolThreadElement *e = &pt->array[id]; |
226 | 60.6k | SCMutexUnlock(&e->lock); |
227 | 60.6k | } |
228 | | |
229 | | #ifdef UNITTESTS |
230 | | struct PoolThreadTestData { |
231 | | PoolThreadId res; |
232 | | int abc; |
233 | | }; |
234 | | |
235 | | static void *PoolThreadTestAlloc(void) |
236 | | { |
237 | | void *data = SCMalloc(sizeof(struct PoolThreadTestData)); |
238 | | return data; |
239 | | } |
240 | | |
241 | | static |
242 | | int PoolThreadTestInit(void *data, void *allocdata) |
243 | | { |
244 | | if (!data) |
245 | | return 0; |
246 | | |
247 | | memset(data,0x00,sizeof(allocdata)); |
248 | | struct PoolThreadTestData *pdata = data; |
249 | | pdata->abc = *(int *)allocdata; |
250 | | return 1; |
251 | | } |
252 | | |
253 | | static |
254 | | void PoolThreadTestFree(void *data) |
255 | | { |
256 | | } |
257 | | |
258 | | static int PoolThreadTestInit01(void) |
259 | | { |
260 | | PoolThread *pt = PoolThreadInit(4, /* threads */ |
261 | | 10, 5, 10, PoolThreadTestAlloc, |
262 | | NULL, NULL, NULL, NULL); |
263 | | FAIL_IF(pt == NULL); |
264 | | PoolThreadFree(pt); |
265 | | PASS; |
266 | | } |
267 | | |
268 | | static int PoolThreadTestInit02(void) |
269 | | { |
270 | | int i = 123; |
271 | | |
272 | | PoolThread *pt = PoolThreadInit(4, /* threads */ |
273 | | 10, 5, 10, |
274 | | PoolThreadTestAlloc, PoolThreadTestInit, |
275 | | &i, PoolThreadTestFree, NULL); |
276 | | FAIL_IF(pt == NULL); |
277 | | PoolThreadFree(pt); |
278 | | PASS; |
279 | | } |
280 | | |
281 | | static int PoolThreadTestGet01(void) |
282 | | { |
283 | | PoolThread *pt = PoolThreadInit(4, /* threads */ |
284 | | 10, 5, 10, PoolThreadTestAlloc, |
285 | | NULL, NULL, NULL, NULL); |
286 | | FAIL_IF(pt == NULL); |
287 | | |
288 | | void *data = PoolThreadGetById(pt, 3); |
289 | | FAIL_IF_NULL(data); |
290 | | |
291 | | struct PoolThreadTestData *pdata = data; |
292 | | FAIL_IF(pdata->res != 3); |
293 | | |
294 | | PoolThreadFree(pt); |
295 | | PASS; |
296 | | } |
297 | | |
298 | | static int PoolThreadTestGet02(void) |
299 | | { |
300 | | int i = 123; |
301 | | |
302 | | PoolThread *pt = PoolThreadInit(4, /* threads */ |
303 | | 10, 5, 10, PoolThreadTestAlloc, |
304 | | PoolThreadTestInit, &i, PoolThreadTestFree, NULL); |
305 | | FAIL_IF_NULL(pt); |
306 | | |
307 | | void *data = PoolThreadGetById(pt, 3); |
308 | | FAIL_IF_NULL(data); |
309 | | |
310 | | struct PoolThreadTestData *pdata = data; |
311 | | FAIL_IF_NOT (pdata->res == 3); |
312 | | |
313 | | FAIL_IF_NOT (pdata->abc == 123); |
314 | | |
315 | | PoolThreadFree(pt); |
316 | | PASS; |
317 | | } |
318 | | |
319 | | static int PoolThreadTestReturn01(void) |
320 | | { |
321 | | int i = 123; |
322 | | |
323 | | PoolThread *pt = PoolThreadInit(4, /* threads */ |
324 | | 10, 5, 10, PoolThreadTestAlloc, |
325 | | PoolThreadTestInit, &i, PoolThreadTestFree, NULL); |
326 | | FAIL_IF_NULL(pt); |
327 | | |
328 | | void *data = PoolThreadGetById(pt, 3); |
329 | | FAIL_IF_NULL(data); |
330 | | |
331 | | struct PoolThreadTestData *pdata = data; |
332 | | FAIL_IF_NOT (pdata->res == 3); |
333 | | |
334 | | FAIL_IF_NOT (pdata->abc == 123); |
335 | | |
336 | | FAIL_IF_NOT (pt->array[3].pool->outstanding == 1); |
337 | | |
338 | | PoolThreadReturn(pt, data); |
339 | | |
340 | | FAIL_IF_NOT (pt->array[3].pool->outstanding == 0); |
341 | | |
342 | | PoolThreadFree(pt); |
343 | | PASS; |
344 | | } |
345 | | |
346 | | static int PoolThreadTestGrow01(void) |
347 | | { |
348 | | PoolThread *pt = PoolThreadInit(4, /* threads */ |
349 | | 10, 5, 10, PoolThreadTestAlloc, |
350 | | NULL, NULL, NULL, NULL); |
351 | | FAIL_IF_NULL(pt); |
352 | | FAIL_IF(PoolThreadExpand(pt) < 0); |
353 | | |
354 | | PoolThreadFree(pt); |
355 | | PASS; |
356 | | } |
357 | | |
358 | | static int PoolThreadTestGrow02(void) |
359 | | { |
360 | | int i = 123; |
361 | | |
362 | | PoolThread *pt = PoolThreadInit(4, /* threads */ |
363 | | 10, 5, 10, PoolThreadTestAlloc, |
364 | | PoolThreadTestInit, &i, PoolThreadTestFree, NULL); |
365 | | FAIL_IF_NULL(pt); |
366 | | FAIL_IF(PoolThreadExpand(pt) < 0); |
367 | | |
368 | | PoolThreadFree(pt); |
369 | | PASS; |
370 | | } |
371 | | |
372 | | static int PoolThreadTestGrow03(void) |
373 | | { |
374 | | int i = 123; |
375 | | |
376 | | PoolThread *pt = PoolThreadInit(4, /* threads */ |
377 | | 10, 5, 10, PoolThreadTestAlloc, |
378 | | PoolThreadTestInit, &i, PoolThreadTestFree, NULL); |
379 | | FAIL_IF_NULL(pt); |
380 | | FAIL_IF(PoolThreadExpand(pt) < 0); |
381 | | |
382 | | void *data = PoolThreadGetById(pt, 4); |
383 | | FAIL_IF_NULL(data); |
384 | | |
385 | | struct PoolThreadTestData *pdata = data; |
386 | | FAIL_IF_NOT(pdata->res == 4); |
387 | | |
388 | | FAIL_IF_NOT(pdata->abc == 123); |
389 | | |
390 | | FAIL_IF_NOT(pt->array[4].pool->outstanding == 1); |
391 | | |
392 | | PoolThreadReturn(pt, data); |
393 | | |
394 | | FAIL_IF_NOT(pt->array[4].pool->outstanding == 0); |
395 | | |
396 | | PoolThreadFree(pt); |
397 | | PASS; |
398 | | } |
399 | | |
400 | | #endif |
401 | | |
402 | | void PoolThreadRegisterTests(void) |
403 | 0 | { |
404 | | #ifdef UNITTESTS |
405 | | UtRegisterTest("PoolThreadTestInit01", PoolThreadTestInit01); |
406 | | UtRegisterTest("PoolThreadTestInit02", PoolThreadTestInit02); |
407 | | |
408 | | UtRegisterTest("PoolThreadTestGet01", PoolThreadTestGet01); |
409 | | UtRegisterTest("PoolThreadTestGet02", PoolThreadTestGet02); |
410 | | |
411 | | UtRegisterTest("PoolThreadTestReturn01", PoolThreadTestReturn01); |
412 | | |
413 | | UtRegisterTest("PoolThreadTestGrow01", PoolThreadTestGrow01); |
414 | | UtRegisterTest("PoolThreadTestGrow02", PoolThreadTestGrow02); |
415 | | UtRegisterTest("PoolThreadTestGrow03", PoolThreadTestGrow03); |
416 | | #endif |
417 | 0 | } |
418 | | |
419 | | /** |
420 | | * @} |
421 | | */ |