comparison mcabber/libjabber/pool.c @ 417:c3ae9251c197

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