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