1 /* |
|
2 * pool.c |
|
3 * This code comes from jabberd - Jabber Open Source Server |
|
4 * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney, |
|
5 * Ryan Eatmon, Robert Norris |
|
6 * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ |
|
7 * |
|
8 * This program is free software; you can redistribute it and/or modify |
|
9 * it under the terms of the GNU General Public License as published by |
|
10 * the Free Software Foundation; either version 2 of the License, or |
|
11 * (at your option) any later version. |
|
12 * |
|
13 * This program is distributed in the hope that it will be useful, |
|
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the |
|
16 * GNU General Public License for more details. |
|
17 * |
|
18 * You should have received a copy of the GNU General Public License |
|
19 * along with this program; if not, write to the Free Software |
|
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA |
|
21 * |
|
22 * Copyrights |
|
23 * |
|
24 * Portions created by or assigned to Jabber.com, Inc. are |
|
25 * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact |
|
26 * information for Jabber.com, Inc. is available at http://www.jabber.com/. |
|
27 * |
|
28 * Portions Copyright (c) 1998-1999 Jeremie Miller. |
|
29 * |
|
30 * Acknowledgements |
|
31 * |
|
32 * Special thanks to the Jabber Open Source Contributors for their |
|
33 * suggestions and support of Jabber. |
|
34 * |
|
35 */ |
|
36 |
|
37 /** |
|
38 * @file pool.c |
|
39 * @brief Handling of memory pools |
|
40 * |
|
41 * Jabberd handles its memory allocations in pools. You create a pool, can |
|
42 * allocate memory from it and all allocations will be freed if you free |
|
43 * the pool. Therefore you don't have to care that each malloc is freed, |
|
44 * you only have to take care that the pool is freed. |
|
45 * |
|
46 * The normal call-flow for pools is: |
|
47 * |
|
48 * pool p = pool_new(); |
|
49 * struct mystruct *allocation1 = pmalloc(sizeof(struct mystruct)); |
|
50 * struct myotherstruct *allocation2 = pmalloc(sizeof(struct myotherstruct)); |
|
51 * ... |
|
52 * pool_free(p); |
|
53 */ |
|
54 |
|
55 #include "libxode.h" |
|
56 |
|
57 #define MAX_MALLOC_TRIES 10 /**< how many seconds we try to allocate memory */ |
|
58 |
|
59 #ifdef POOL_DEBUG |
|
60 int pool__total = 0; /**< how many memory blocks are allocated */ |
|
61 int pool__ltotal = 0; |
|
62 xht pool__disturbed = NULL; |
|
63 |
|
64 inline void *_retried__malloc(size_t size); |
|
65 |
|
66 /** |
|
67 * create a new memory allocation and increment the pool__total counter |
|
68 * |
|
69 * only used if POOL_DEBUG is defined, else it is an alias for malloc |
|
70 * |
|
71 * @param size size of the memory to allocate |
|
72 * @return pointer to the allocated memory |
|
73 */ |
|
74 void *_pool__malloc(size_t size) |
|
75 { |
|
76 pool__total++; |
|
77 return malloc(size); |
|
78 } |
|
79 |
|
80 /** |
|
81 * free memory and decrement the pool__total counter |
|
82 * |
|
83 * only used if POOL_DEBUG is defined, else it is an alias for free |
|
84 * |
|
85 * @param block pointer to the memory allocation that should be freed |
|
86 */ |
|
87 void _pool__free(void *block) |
|
88 { |
|
89 pool__total--; |
|
90 free(block); |
|
91 } |
|
92 #else |
|
93 #define _pool__malloc malloc /**< _pool__malloc updates pool__total counter if POOL_DEBUG is defined */ |
|
94 #define _pool__free free /**< _pool__free updates pool__total counter if POOL_DEBUG is defined */ |
|
95 #endif |
|
96 |
|
97 /** |
|
98 * try to allocate memory |
|
99 * |
|
100 * If allocation fails, it will be retries for MAX_MALLOC_TRIES seconds. |
|
101 * If it still fails, we exit the process |
|
102 * |
|
103 * @param size how many bytes of memory we allocate |
|
104 * @return pointer to the allocated memory |
|
105 */ |
|
106 void *_retried__malloc(size_t size) { |
|
107 void *allocated_memory; |
|
108 int malloc_tries = 0; |
|
109 |
|
110 while ((allocated_memory=_pool__malloc(size)) == NULL) { |
|
111 if (malloc_tries++ > MAX_MALLOC_TRIES) { |
|
112 exit(999); |
|
113 } |
|
114 |
|
115 sleep(1); //pth_sleep(1); |
|
116 } |
|
117 |
|
118 return allocated_memory; |
|
119 } |
|
120 |
|
121 /** |
|
122 * make an empty pool |
|
123 * |
|
124 * Use the macro pool_new() instead of a direct call to this function. The |
|
125 * macro will create the parameters for you. |
|
126 * |
|
127 * @param zone the file in which the pool_new macro is called |
|
128 * @param line the line in the file in which the pool_new macro is called |
|
129 * @return the new allocated memory pool |
|
130 */ |
|
131 pool _pool_new(char *zone, int line) |
|
132 { |
|
133 // int malloc_tries = 0; |
|
134 #ifdef POOL_DEBUG |
|
135 int old__pool__total; |
|
136 #endif |
|
137 |
|
138 pool p = _retried__malloc(sizeof(_pool)); |
|
139 |
|
140 p->cleanup = NULL; |
|
141 p->heap = NULL; |
|
142 p->size = 0; |
|
143 |
|
144 #ifdef POOL_DEBUG |
|
145 p->lsize = -1; |
|
146 p->zone[0] = '\0'; |
|
147 strcat(p->zone,zone); |
|
148 snprintf(p->zone, sizeof(p->zone), "%s:%i", zone, line); |
|
149 snprintf(p->name, sizeof(p->name), "%X", p); |
|
150 |
|
151 if(pool__disturbed == NULL) |
|
152 { |
|
153 pool__disturbed = (xht)1; /* reentrancy flag! */ |
|
154 pool__disturbed = ghash_create(POOL_DEBUG,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); |
|
155 } |
|
156 if(pool__disturbed != (xht)1) |
|
157 ghash_put(pool__disturbed,p->name,p); |
|
158 #endif |
|
159 |
|
160 return p; |
|
161 } |
|
162 |
|
163 /** |
|
164 * free a memory heap (struct pheap) |
|
165 * |
|
166 * @param arg which heep should be freed |
|
167 */ |
|
168 void _pool_heap_free(void *arg) |
|
169 { |
|
170 struct pheap *h = (struct pheap *)arg; |
|
171 |
|
172 _pool__free(h->block); |
|
173 _pool__free(h); |
|
174 } |
|
175 |
|
176 /** |
|
177 * append a pool_cleaner function (callback) to a pool |
|
178 * |
|
179 * mem should always be freed last |
|
180 * |
|
181 * All appended pool_cleaner functions will be called if a pool is freed. |
|
182 * This might be used to clean logically subpools. |
|
183 * |
|
184 * @param p to which pool the pool_cleaner should be added |
|
185 * @param pf structure containing the reference to the pool_cleaner and links for the list |
|
186 */ |
|
187 void _pool_cleanup_append(pool p, struct pfree *pf) |
|
188 { |
|
189 struct pfree *cur; |
|
190 |
|
191 if(p->cleanup == NULL) |
|
192 { |
|
193 p->cleanup = pf; |
|
194 return; |
|
195 } |
|
196 |
|
197 /* fast forward to end of list */ |
|
198 for(cur = p->cleanup; cur->next != NULL; cur = cur->next); |
|
199 |
|
200 cur->next = pf; |
|
201 } |
|
202 |
|
203 /** |
|
204 * create a cleanup tracker |
|
205 * |
|
206 * this function is used to create a pfree structure that can be passed to _pool_cleanup_append() |
|
207 * |
|
208 * @param p the pool to which the pool_cleaner should be added |
|
209 * @param f the function that should be called if the pool is freed |
|
210 * @param arg the parameter that should be passed to the pool_cleaner function |
|
211 * @return pointer to the new pfree structure |
|
212 */ |
|
213 struct pfree *_pool_free(pool p, pool_cleaner f, void *arg) |
|
214 { |
|
215 struct pfree *ret; |
|
216 |
|
217 /* make the storage for the tracker */ |
|
218 ret = _retried__malloc(sizeof(struct pfree)); |
|
219 ret->f = f; |
|
220 ret->arg = arg; |
|
221 ret->next = NULL; |
|
222 |
|
223 return ret; |
|
224 } |
|
225 |
|
226 /** |
|
227 * create a heap and make sure it get's cleaned up |
|
228 * |
|
229 * pheaps are used by memory pools internally to handle the memory allocations |
|
230 * |
|
231 * @note the macro pool_heap calls _pool_new_heap and NOT _pool_heap |
|
232 * |
|
233 * @param p for which pool the heap should be created |
|
234 * @param size how big the pool should be |
|
235 * @return pointer to the new pheap |
|
236 */ |
|
237 struct pheap *_pool_heap(pool p, int size) |
|
238 { |
|
239 struct pheap *ret; |
|
240 struct pfree *clean; |
|
241 |
|
242 /* make the return heap */ |
|
243 ret = _retried__malloc(sizeof(struct pheap)); |
|
244 ret->block = _retried__malloc(size); |
|
245 ret->size = size; |
|
246 p->size += size; |
|
247 ret->used = 0; |
|
248 |
|
249 /* append to the cleanup list */ |
|
250 clean = _pool_free(p, _pool_heap_free, (void *)ret); |
|
251 clean->heap = ret; /* for future use in finding used mem for pstrdup */ |
|
252 _pool_cleanup_append(p, clean); |
|
253 |
|
254 return ret; |
|
255 } |
|
256 |
|
257 /** |
|
258 * create a new memory pool and set the initial heap size |
|
259 * |
|
260 * @note you should not call this function but use the macro pool_heap instead which fills zone and line automatically |
|
261 * |
|
262 * @param size the initial size of the memory pool |
|
263 * @param zone the file where this function is called (for debugging) |
|
264 * @param line the line in the file where this function is called |
|
265 * @return the new memory pool |
|
266 */ |
|
267 pool _pool_new_heap(int size, char *zone, int line) |
|
268 { |
|
269 pool p; |
|
270 p = _pool_new(zone, line); |
|
271 p->heap = _pool_heap(p,size); |
|
272 return p; |
|
273 } |
|
274 |
|
275 /** |
|
276 * allocate memory from a memory pool |
|
277 * |
|
278 * @param p the pool to use |
|
279 * @param size how much memory to allocate |
|
280 * @return pointer to the allocated memory |
|
281 */ |
|
282 void *pmalloc(pool p, int size) |
|
283 { |
|
284 void *block; |
|
285 |
|
286 if(p == NULL) |
|
287 { |
|
288 fprintf(stderr,"Memory Leak! [pmalloc received NULL pool, unable to track allocation, exiting]\n"); |
|
289 abort(); |
|
290 } |
|
291 |
|
292 /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */ |
|
293 if(p->heap == NULL || size > (p->heap->size / 2)) |
|
294 { |
|
295 block = _retried__malloc(size); |
|
296 p->size += size; |
|
297 _pool_cleanup_append(p, _pool_free(p, _pool__free, block)); |
|
298 return block; |
|
299 } |
|
300 |
|
301 /* we have to preserve boundaries, long story :) */ |
|
302 if(size >= 4) |
|
303 while(p->heap->used&7) p->heap->used++; |
|
304 |
|
305 /* if we don't fit in the old heap, replace it */ |
|
306 if(size > (p->heap->size - p->heap->used)) |
|
307 p->heap = _pool_heap(p, p->heap->size); |
|
308 |
|
309 /* the current heap has room */ |
|
310 block = (char *)p->heap->block + p->heap->used; |
|
311 p->heap->used += size; |
|
312 return block; |
|
313 } |
|
314 |
|
315 /** |
|
316 * allocate memory and initialize the memory with the given char c |
|
317 * |
|
318 * @deprecated jabberd does use pmalloco instead, this function will be removed |
|
319 * |
|
320 * @param p which pool to use |
|
321 * @param size the size of the allocation |
|
322 * @param c the initialization character |
|
323 * @return pointer to the allocated memory |
|
324 */ |
|
325 void *pmalloc_x(pool p, int size, char c) |
|
326 { |
|
327 void* result = pmalloc(p, size); |
|
328 if (result != NULL) |
|
329 memset(result, c, size); |
|
330 return result; |
|
331 } |
|
332 |
|
333 /** |
|
334 * allocate memory and initialize the memory with zero bytes |
|
335 * |
|
336 * easy safety utility (for creating blank mem for structs, etc) |
|
337 * |
|
338 * @param p which pool to use |
|
339 * @param size the size of the allocation |
|
340 * @return pointer to the allocated memory |
|
341 */ |
|
342 void *pmalloco(pool p, int size) |
|
343 { |
|
344 void *block = pmalloc(p, size); |
|
345 memset(block, 0, size); |
|
346 return block; |
|
347 } |
|
348 |
|
349 /** |
|
350 * duplicate a string and allocate memory for it |
|
351 * |
|
352 * @todo efficient: move this to const char* and then loop through the existing heaps to see if src is within a block in this pool |
|
353 * |
|
354 * @param p the pool to use |
|
355 * @param src the string that should be duplicated |
|
356 * @return the duplicated string |
|
357 */ |
|
358 char *pstrdup(pool p, const char *src) |
|
359 { |
|
360 char *ret; |
|
361 |
|
362 if(src == NULL) |
|
363 return NULL; |
|
364 |
|
365 ret = pmalloc(p,strlen(src) + 1); |
|
366 strcpy(ret,src); |
|
367 |
|
368 return ret; |
|
369 } |
|
370 |
|
371 /** |
|
372 * when pstrdup() is moved to "const char*", this one would actually return a new block |
|
373 */ |
|
374 char *pstrdupx(pool p, const char *src) |
|
375 { |
|
376 return pstrdup(p, src); |
|
377 } |
|
378 |
|
379 /** |
|
380 * get the size of a memory pool |
|
381 * |
|
382 * @param p the pool |
|
383 * @return the size |
|
384 */ |
|
385 int pool_size(pool p) |
|
386 { |
|
387 if(p == NULL) return 0; |
|
388 |
|
389 return p->size; |
|
390 } |
|
391 |
|
392 /** |
|
393 * free a pool (and all memory that is allocated in it) |
|
394 * |
|
395 * @param p which pool to free |
|
396 */ |
|
397 void pool_free(pool p) |
|
398 { |
|
399 struct pfree *cur, *stub; |
|
400 |
|
401 if(p == NULL) return; |
|
402 |
|
403 cur = p->cleanup; |
|
404 while(cur != NULL) |
|
405 { |
|
406 (*cur->f)(cur->arg); |
|
407 stub = cur->next; |
|
408 _pool__free(cur); |
|
409 cur = stub; |
|
410 } |
|
411 |
|
412 #ifdef POOL_DEBUG |
|
413 ghash_remove(pool__disturbed,p->name); |
|
414 #endif |
|
415 |
|
416 _pool__free(p); |
|
417 |
|
418 } |
|
419 |
|
420 /** |
|
421 * public cleanup utils, insert in a way that they are run FIFO, before mem frees |
|
422 */ |
|
423 void pool_cleanup(pool p, pool_cleaner f, void *arg) |
|
424 { |
|
425 struct pfree *clean; |
|
426 |
|
427 clean = _pool_free(p, f, arg); |
|
428 clean->next = p->cleanup; |
|
429 p->cleanup = clean; |
|
430 } |
|
431 |
|
432 #ifdef POOL_DEBUG |
|
433 void debug_log(char *zone, const char *msgfmt, ...); |
|
434 void _pool_stat(xht h, const char *key, void *data, void *arg) |
|
435 { |
|
436 pool p = (pool)data; |
|
437 |
|
438 if(p->lsize == -1) |
|
439 debug_log("pool_debug","%s: %s is a new pool",p->zone, p->name); |
|
440 else if(p->size > p->lsize) |
|
441 debug_log("pool_debug","%s: %s grew %d",p->zone, p->name, p->size - p->lsize); |
|
442 else if((int)arg) |
|
443 debug_log("pool_debug","%s: %s exists %d",p->zone,p->name, p->size); |
|
444 p->lsize = p->size; |
|
445 } |
|
446 |
|
447 /** |
|
448 * print memory pool statistics (for debugging purposes) |
|
449 * |
|
450 * @param full make a full report? (0 = no, 1 = yes) |
|
451 */ |
|
452 void pool_stat(int full) |
|
453 { |
|
454 if (pool__disturbed == NULL || pool__disturbed == (xht)1) |
|
455 return; |
|
456 |
|
457 ghash_walk(pool__disturbed,_pool_stat,(void *)full); |
|
458 if(pool__total != pool__ltotal) |
|
459 debug_log("pool_debug","%d\ttotal missed mallocs",pool__total); |
|
460 pool__ltotal = pool__total; |
|
461 |
|
462 return; |
|
463 } |
|
464 #else |
|
465 /** |
|
466 * dummy implementation: print memory pool statistics (for debugging purposes, real implementation if POOL_DEBUG is defined) |
|
467 * |
|
468 * @param full make a full report? (0 = no, 1 = yes) |
|
469 */ |
|
470 void pool_stat(int full) |
|
471 { |
|
472 return; |
|
473 } |
|
474 #endif |
|