25
|
1 /* |
|
2 * This program is free software; you can redistribute it and/or modify |
|
3 * it under the terms of the GNU General Public License as published by |
|
4 * the Free Software Foundation; either version 2 of the License, or |
|
5 * (at your option) any later version. |
|
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/ |
|
18 * |
|
19 * 2/27/00:3am, random plans by jer |
|
20 * |
|
21 * ok based on gprof, we really need some innovation here... my thoughs are this: |
|
22 * |
|
23 * most things are strings, so have a string-based true-blue garbage collector |
|
24 * one big global hash containing all the strings created by any pstrdup, returning const char * |
|
25 * a refcount on each string block |
|
26 * when a pool is freed, it moves down the refcount |
|
27 * garbage collector collects pools on the free stack, and runs through the hash for unused strings |
|
28 * j_strcmp can check for == (if they are both from a pstrdup) |
|
29 * |
|
30 * let's see... this would change: |
|
31 * pstrdup: do a hash lookup, success=return, fail=pmalloc & hash put |
|
32 * pool_free: |
|
33 * |
|
34 * |
|
35 * |
|
36 * |
|
37 * |
|
38 */ |
|
39 |
|
40 #include "libxode.h" |
|
41 |
|
42 #ifdef POOL_DEBUG |
|
43 int pool__total = 0; |
|
44 int pool__ltotal = 0; |
|
45 HASHTABLE pool__disturbed = NULL; |
|
46 void *_pool__malloc(size_t size) |
|
47 { |
|
48 pool__total++; |
|
49 return malloc(size); |
|
50 } |
|
51 void _pool__free(void *block) |
|
52 { |
|
53 pool__total--; |
|
54 free(block); |
|
55 } |
|
56 #else |
|
57 #define _pool__malloc malloc |
|
58 #define _pool__free free |
|
59 #endif |
|
60 |
|
61 |
|
62 /* make an empty pool */ |
|
63 pool _pool_new(char *zone) |
|
64 { |
|
65 pool p; |
|
66 while((p = _pool__malloc(sizeof(_pool))) == NULL) sleep(1); |
|
67 p->cleanup = NULL; |
|
68 p->heap = NULL; |
|
69 p->size = 0; |
|
70 |
|
71 #ifdef POOL_DEBUG |
|
72 p->lsize = -1; |
|
73 p->zone[0] = '\0'; |
|
74 strcat(p->zone,zone); |
|
75 sprintf(p->name,"%X",p); |
|
76 |
|
77 if(pool__disturbed == NULL) |
|
78 pool__disturbed = ghash_create(POOL_DEBUG,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); |
|
79 ghash_put(pool__disturbed,p->name,p); |
|
80 #endif |
|
81 |
|
82 return p; |
|
83 } |
|
84 |
|
85 /* free a heap */ |
|
86 void _pool_heap_free(void *arg) |
|
87 { |
|
88 struct pheap *h = (struct pheap *)arg; |
|
89 |
|
90 _pool__free(h->block); |
|
91 _pool__free(h); |
|
92 } |
|
93 |
|
94 /* mem should always be freed last */ |
|
95 void _pool_cleanup_append(pool p, struct pfree *pf) |
|
96 { |
|
97 struct pfree *cur; |
|
98 |
|
99 if(p->cleanup == NULL) |
|
100 { |
|
101 p->cleanup = pf; |
|
102 return; |
|
103 } |
|
104 |
|
105 /* fast forward to end of list */ |
|
106 for(cur = p->cleanup; cur->next != NULL; cur = cur->next); |
|
107 |
|
108 cur->next = pf; |
|
109 } |
|
110 |
|
111 /* create a cleanup tracker */ |
|
112 struct pfree *_pool_free(pool p, pool_cleaner f, void *arg) |
|
113 { |
|
114 struct pfree *ret; |
|
115 |
|
116 /* make the storage for the tracker */ |
|
117 while((ret = _pool__malloc(sizeof(struct pfree))) == NULL) sleep(1); |
|
118 ret->f = f; |
|
119 ret->arg = arg; |
|
120 ret->next = NULL; |
|
121 |
|
122 return ret; |
|
123 } |
|
124 |
|
125 /* create a heap and make sure it get's cleaned up */ |
|
126 struct pheap *_pool_heap(pool p, int size) |
|
127 { |
|
128 struct pheap *ret; |
|
129 struct pfree *clean; |
|
130 |
|
131 /* make the return heap */ |
|
132 while((ret = _pool__malloc(sizeof(struct pheap))) == NULL) sleep(1); |
|
133 while((ret->block = _pool__malloc(size)) == NULL) sleep(1); |
|
134 ret->size = size; |
|
135 p->size += size; |
|
136 ret->used = 0; |
|
137 |
|
138 /* append to the cleanup list */ |
|
139 clean = _pool_free(p, _pool_heap_free, (void *)ret); |
|
140 clean->heap = ret; /* for future use in finding used mem for pstrdup */ |
|
141 _pool_cleanup_append(p, clean); |
|
142 |
|
143 return ret; |
|
144 } |
|
145 |
|
146 pool _pool_new_heap(int size, char *zone) |
|
147 { |
|
148 pool p; |
|
149 p = _pool_new(zone); |
|
150 p->heap = _pool_heap(p,size); |
|
151 return p; |
|
152 } |
|
153 |
|
154 void *pmalloc(pool p, int size) |
|
155 { |
|
156 void *block; |
|
157 |
|
158 if(p == NULL) |
|
159 { |
|
160 fprintf(stderr,"Memory Leak! [pmalloc received NULL pool, unable to track allocation, exiting]\n"); |
|
161 abort(); |
|
162 } |
|
163 |
|
164 /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */ |
|
165 if(p->heap == NULL || size > (p->heap->size / 2)) |
|
166 { |
|
167 while((block = _pool__malloc(size)) == NULL) sleep(1); |
|
168 p->size += size; |
|
169 _pool_cleanup_append(p, _pool_free(p, _pool__free, block)); |
|
170 return block; |
|
171 } |
|
172 |
|
173 /* we have to preserve boundaries, long story :) */ |
|
174 if(size >= 4) |
|
175 while(p->heap->used&7) p->heap->used++; |
|
176 |
|
177 /* if we don't fit in the old heap, replace it */ |
|
178 if(size > (p->heap->size - p->heap->used)) |
|
179 p->heap = _pool_heap(p, p->heap->size); |
|
180 |
|
181 /* the current heap has room */ |
|
182 block = (char *)p->heap->block + p->heap->used; |
|
183 p->heap->used += size; |
|
184 return block; |
|
185 } |
|
186 |
|
187 void *pmalloc_x(pool p, int size, char c) |
|
188 { |
|
189 void* result = pmalloc(p, size); |
|
190 if (result != NULL) |
|
191 memset(result, c, size); |
|
192 return result; |
|
193 } |
|
194 |
|
195 /* easy safety utility (for creating blank mem for structs, etc) */ |
|
196 void *pmalloco(pool p, int size) |
|
197 { |
|
198 void *block = pmalloc(p, size); |
|
199 memset(block, 0, size); |
|
200 return block; |
|
201 } |
|
202 |
|
203 /* 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 */ |
|
204 char *pstrdup(pool p, const char *src) |
|
205 { |
|
206 char *ret; |
|
207 |
|
208 if(src == NULL) |
|
209 return NULL; |
|
210 |
|
211 ret = pmalloc(p,strlen(src) + 1); |
|
212 strcpy(ret,src); |
|
213 |
|
214 return ret; |
|
215 } |
|
216 |
|
217 /* when move above, this one would actually return a new block */ |
|
218 char *pstrdupx(pool p, const char *src) |
|
219 { |
|
220 return pstrdup(p, src); |
|
221 } |
|
222 |
|
223 int pool_size(pool p) |
|
224 { |
|
225 if(p == NULL) return 0; |
|
226 |
|
227 return p->size; |
|
228 } |
|
229 |
|
230 void pool_free(pool p) |
|
231 { |
|
232 struct pfree *cur, *stub; |
|
233 |
|
234 if(p == NULL) return; |
|
235 |
|
236 cur = p->cleanup; |
|
237 while(cur != NULL) |
|
238 { |
|
239 (*cur->f)(cur->arg); |
|
240 stub = cur->next; |
|
241 _pool__free(cur); |
|
242 cur = stub; |
|
243 } |
|
244 |
|
245 #ifdef POOL_DEBUG |
|
246 ghash_remove(pool__disturbed,p->name); |
|
247 #endif |
|
248 |
|
249 _pool__free(p); |
|
250 |
|
251 } |
|
252 |
|
253 /* public cleanup utils, insert in a way that they are run FIFO, before mem frees */ |
|
254 void pool_cleanup(pool p, pool_cleaner f, void *arg) |
|
255 { |
|
256 struct pfree *clean; |
|
257 |
|
258 clean = _pool_free(p, f, arg); |
|
259 clean->next = p->cleanup; |
|
260 p->cleanup = clean; |
|
261 } |
|
262 |
|
263 #ifdef POOL_DEBUG |
|
264 void debug_log(char *zone, const char *msgfmt, ...); |
|
265 int _pool_stat(void *arg, const void *key, void *data) |
|
266 { |
|
267 pool p = (pool)data; |
|
268 |
|
269 if(p->lsize == -1) |
|
270 debug_log("leak","%s: %X is a new pool",p->zone,p->name); |
|
271 else if(p->size > p->lsize) |
|
272 debug_log("leak","%s: %X grew %d",p->zone,p->name, p->size - p->lsize); |
|
273 else if((int)arg) |
|
274 debug_log("leak","%s: %X exists %d",p->zone,p->name, p->size); |
|
275 p->lsize = p->size; |
|
276 return 1; |
|
277 } |
|
278 |
|
279 void pool_stat(int full) |
|
280 { |
|
281 ghash_walk(pool__disturbed,_pool_stat,(void *)full); |
|
282 if(pool__total != pool__ltotal) |
|
283 debug_log("leak","%d\ttotal missed mallocs",pool__total); |
|
284 pool__ltotal = pool__total; |
|
285 return; |
|
286 } |
|
287 #else |
|
288 void pool_stat(int full) |
|
289 { |
|
290 return; |
|
291 } |
|
292 #endif |