3127
|
1 /* --------------------------------------------------------------------------
|
|
2 *
|
|
3 * License
|
|
4 *
|
|
5 * The contents of this file are subject to the Jabber Open Source License
|
|
6 * Version 1.0 (the "JOSL"). You may not copy or use this file, in either
|
|
7 * source code or executable form, except in compliance with the JOSL. You
|
|
8 * may obtain a copy of the JOSL at http://www.jabber.org/ or at
|
|
9 * http://www.opensource.org/.
|
2086
|
10 *
|
3127
|
11 * Software distributed under the JOSL is distributed on an "AS IS" basis,
|
|
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL
|
|
13 * for the specific language governing rights and limitations under the
|
|
14 * JOSL.
|
2086
|
15 *
|
3127
|
16 * Copyrights
|
|
17 *
|
|
18 * Portions created by or assigned to Jabber.com, Inc. are
|
|
19 * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact
|
|
20 * information for Jabber.com, Inc. is available at http://www.jabber.com/.
|
2086
|
21 *
|
3127
|
22 * Portions Copyright (c) 1998-1999 Jeremie Miller.
|
|
23 *
|
|
24 * Acknowledgements
|
|
25 *
|
|
26 * Special thanks to the Jabber Open Source Contributors for their
|
|
27 * suggestions and support of Jabber.
|
|
28 *
|
|
29 * Alternatively, the contents of this file may be used under the terms of the
|
|
30 * GNU General Public License Version 2 or later (the "GPL"), in which case
|
|
31 * the provisions of the GPL are applicable instead of those above. If you
|
|
32 * wish to allow use of your version of this file only under the terms of the
|
|
33 * GPL and not to allow others to use your version of this file under the JOSL,
|
|
34 * indicate your decision by deleting the provisions above and replace them
|
|
35 * with the notice and other provisions required by the GPL. If you do not
|
|
36 * delete the provisions above, a recipient may use your version of this file
|
|
37 * under either the JOSL or the GPL.
|
|
38 *
|
|
39 *
|
|
40 * --------------------------------------------------------------------------*/
|
2086
|
41
|
3127
|
42 #include "lib.h"
|
2086
|
43
|
|
44
|
|
45 #ifdef POOL_DEBUG
|
|
46 int pool__total = 0;
|
|
47 int pool__ltotal = 0;
|
|
48 HASHTABLE pool__disturbed = NULL;
|
|
49 void *_pool__malloc(size_t size)
|
|
50 {
|
|
51 pool__total++;
|
|
52 return malloc(size);
|
|
53 }
|
|
54 void _pool__free(void *block)
|
|
55 {
|
|
56 pool__total--;
|
|
57 free(block);
|
|
58 }
|
|
59 #else
|
|
60 #define _pool__malloc malloc
|
|
61 #define _pool__free free
|
|
62 #endif
|
|
63
|
|
64
|
|
65 /* make an empty pool */
|
|
66 pool _pool_new(char *zone)
|
|
67 {
|
|
68 pool p;
|
|
69 while((p = _pool__malloc(sizeof(_pool))) == NULL) sleep(1);
|
|
70 p->cleanup = NULL;
|
|
71 p->heap = NULL;
|
|
72 p->size = 0;
|
|
73
|
|
74 #ifdef POOL_DEBUG
|
|
75 p->lsize = -1;
|
|
76 p->zone[0] = '\0';
|
|
77 strcat(p->zone,zone);
|
|
78 sprintf(p->name,"%X",p);
|
|
79
|
|
80 if(pool__disturbed == NULL)
|
3127
|
81 {
|
|
82 pool__disturbed = 1; /* reentrancy flag! */
|
2086
|
83 pool__disturbed = ghash_create(POOL_DEBUG,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
|
3127
|
84 }
|
|
85 if(pool__disturbed != 1)
|
|
86 ghash_put(pool__disturbed,p->name,p);
|
2086
|
87 #endif
|
|
88
|
|
89 return p;
|
|
90 }
|
|
91
|
|
92 /* free a heap */
|
|
93 void _pool_heap_free(void *arg)
|
|
94 {
|
|
95 struct pheap *h = (struct pheap *)arg;
|
|
96
|
|
97 _pool__free(h->block);
|
|
98 _pool__free(h);
|
|
99 }
|
|
100
|
|
101 /* mem should always be freed last */
|
|
102 void _pool_cleanup_append(pool p, struct pfree *pf)
|
|
103 {
|
|
104 struct pfree *cur;
|
|
105
|
|
106 if(p->cleanup == NULL)
|
|
107 {
|
|
108 p->cleanup = pf;
|
|
109 return;
|
|
110 }
|
|
111
|
|
112 /* fast forward to end of list */
|
|
113 for(cur = p->cleanup; cur->next != NULL; cur = cur->next);
|
|
114
|
|
115 cur->next = pf;
|
|
116 }
|
|
117
|
|
118 /* create a cleanup tracker */
|
|
119 struct pfree *_pool_free(pool p, pool_cleaner f, void *arg)
|
|
120 {
|
|
121 struct pfree *ret;
|
|
122
|
|
123 /* make the storage for the tracker */
|
|
124 while((ret = _pool__malloc(sizeof(struct pfree))) == NULL) sleep(1);
|
|
125 ret->f = f;
|
|
126 ret->arg = arg;
|
|
127 ret->next = NULL;
|
|
128
|
|
129 return ret;
|
|
130 }
|
|
131
|
|
132 /* create a heap and make sure it get's cleaned up */
|
|
133 struct pheap *_pool_heap(pool p, int size)
|
|
134 {
|
|
135 struct pheap *ret;
|
|
136 struct pfree *clean;
|
|
137
|
|
138 /* make the return heap */
|
|
139 while((ret = _pool__malloc(sizeof(struct pheap))) == NULL) sleep(1);
|
|
140 while((ret->block = _pool__malloc(size)) == NULL) sleep(1);
|
|
141 ret->size = size;
|
|
142 p->size += size;
|
|
143 ret->used = 0;
|
|
144
|
|
145 /* append to the cleanup list */
|
|
146 clean = _pool_free(p, _pool_heap_free, (void *)ret);
|
|
147 clean->heap = ret; /* for future use in finding used mem for pstrdup */
|
|
148 _pool_cleanup_append(p, clean);
|
|
149
|
|
150 return ret;
|
|
151 }
|
|
152
|
|
153 pool _pool_new_heap(int size, char *zone)
|
|
154 {
|
|
155 pool p;
|
|
156 p = _pool_new(zone);
|
|
157 p->heap = _pool_heap(p,size);
|
|
158 return p;
|
|
159 }
|
|
160
|
|
161 void *pmalloc(pool p, int size)
|
|
162 {
|
|
163 void *block;
|
|
164
|
|
165 if(p == NULL)
|
|
166 {
|
|
167 fprintf(stderr,"Memory Leak! [pmalloc received NULL pool, unable to track allocation, exiting]\n");
|
|
168 abort();
|
|
169 }
|
|
170
|
|
171 /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */
|
|
172 if(p->heap == NULL || size > (p->heap->size / 2))
|
|
173 {
|
|
174 while((block = _pool__malloc(size)) == NULL) sleep(1);
|
|
175 p->size += size;
|
|
176 _pool_cleanup_append(p, _pool_free(p, _pool__free, block));
|
|
177 return block;
|
|
178 }
|
|
179
|
|
180 /* we have to preserve boundaries, long story :) */
|
|
181 if(size >= 4)
|
|
182 while(p->heap->used&7) p->heap->used++;
|
|
183
|
|
184 /* if we don't fit in the old heap, replace it */
|
|
185 if(size > (p->heap->size - p->heap->used))
|
|
186 p->heap = _pool_heap(p, p->heap->size);
|
|
187
|
|
188 /* the current heap has room */
|
|
189 block = (char *)p->heap->block + p->heap->used;
|
|
190 p->heap->used += size;
|
|
191 return block;
|
|
192 }
|
|
193
|
|
194 void *pmalloc_x(pool p, int size, char c)
|
|
195 {
|
|
196 void* result = pmalloc(p, size);
|
|
197 if (result != NULL)
|
|
198 memset(result, c, size);
|
|
199 return result;
|
|
200 }
|
|
201
|
|
202 /* easy safety utility (for creating blank mem for structs, etc) */
|
|
203 void *pmalloco(pool p, int size)
|
|
204 {
|
|
205 void *block = pmalloc(p, size);
|
|
206 memset(block, 0, size);
|
|
207 return block;
|
|
208 }
|
|
209
|
|
210 /* 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 */
|
|
211 char *pstrdup(pool p, const char *src)
|
|
212 {
|
|
213 char *ret;
|
|
214
|
|
215 if(src == NULL)
|
|
216 return NULL;
|
|
217
|
|
218 ret = pmalloc(p,strlen(src) + 1);
|
|
219 strcpy(ret,src);
|
|
220
|
|
221 return ret;
|
|
222 }
|
|
223
|
|
224 /* when move above, this one would actually return a new block */
|
|
225 char *pstrdupx(pool p, const char *src)
|
|
226 {
|
|
227 return pstrdup(p, src);
|
|
228 }
|
|
229
|
|
230 int pool_size(pool p)
|
|
231 {
|
|
232 if(p == NULL) return 0;
|
|
233
|
|
234 return p->size;
|
|
235 }
|
|
236
|
|
237 void pool_free(pool p)
|
|
238 {
|
|
239 struct pfree *cur, *stub;
|
|
240
|
|
241 if(p == NULL) return;
|
|
242
|
|
243 cur = p->cleanup;
|
|
244 while(cur != NULL)
|
|
245 {
|
|
246 (*cur->f)(cur->arg);
|
|
247 stub = cur->next;
|
|
248 _pool__free(cur);
|
|
249 cur = stub;
|
|
250 }
|
|
251
|
|
252 #ifdef POOL_DEBUG
|
|
253 ghash_remove(pool__disturbed,p->name);
|
|
254 #endif
|
|
255
|
|
256 _pool__free(p);
|
|
257
|
|
258 }
|
|
259
|
|
260 /* public cleanup utils, insert in a way that they are run FIFO, before mem frees */
|
|
261 void pool_cleanup(pool p, pool_cleaner f, void *arg)
|
|
262 {
|
|
263 struct pfree *clean;
|
|
264
|
|
265 clean = _pool_free(p, f, arg);
|
|
266 clean->next = p->cleanup;
|
|
267 p->cleanup = clean;
|
|
268 }
|
|
269
|
|
270 #ifdef POOL_DEBUG
|
|
271 void debug_log(char *zone, const char *msgfmt, ...);
|
|
272 int _pool_stat(void *arg, const void *key, void *data)
|
|
273 {
|
|
274 pool p = (pool)data;
|
|
275
|
|
276 if(p->lsize == -1)
|
|
277 debug_log("leak","%s: %X is a new pool",p->zone,p->name);
|
|
278 else if(p->size > p->lsize)
|
|
279 debug_log("leak","%s: %X grew %d",p->zone,p->name, p->size - p->lsize);
|
|
280 else if((int)arg)
|
|
281 debug_log("leak","%s: %X exists %d",p->zone,p->name, p->size);
|
|
282 p->lsize = p->size;
|
|
283 return 1;
|
|
284 }
|
|
285
|
|
286 void pool_stat(int full)
|
|
287 {
|
|
288 ghash_walk(pool__disturbed,_pool_stat,(void *)full);
|
|
289 if(pool__total != pool__ltotal)
|
|
290 debug_log("leak","%d\ttotal missed mallocs",pool__total);
|
|
291 pool__ltotal = pool__total;
|
|
292 return;
|
|
293 }
|
|
294 #else
|
|
295 void pool_stat(int full)
|
|
296 {
|
|
297 return;
|
|
298 }
|
|
299 #endif
|