]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cbdata.cc
Fixed probable memory accounting leak. We were calling free() when
[thirdparty/squid.git] / src / cbdata.cc
1
2 /*
3 * $Id: cbdata.cc,v 1.18 1998/03/30 22:58:51 wessels Exp $
4 *
5 * DEBUG: section 45 Callback Data Registry
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
31
32 /*
33 * These routines manage a set of registered callback data pointers.
34 * One of the easiest ways to make Squid coredump is to issue a
35 * callback to for some data structure which has previously been
36 * freed. With these routines, we register (add) callback data
37 * pointers, lock them just before registering the callback function,
38 * validate them before issuing the callback, and then free them
39 * when finished.
40 *
41 * In terms of time, the sequence goes something like this:
42 *
43 * foo = xcalloc(sizeof(foo));
44 * cbdataAdd(foo);
45 * ...
46 * cbdataLock(foo);
47 * some_blocking_operation(..., callback_func, foo);
48 * ...
49 * some_blocking_operation_completes()
50 * if (cbdataValid(foo))
51 * callback_func(..., foo)
52 * cbdataUnlock(foo);
53 * ...
54 * cbdataFree(foo);
55 *
56 * The nice thing is that, we do not need to require that Unlock
57 * occurs before Free. If the Free happens first, then the
58 * callback data is marked invalid and the callback will never
59 * be made. When we Unlock and the lock count reaches zero,
60 * we free the memory if it is marked invalid.
61 */
62
63 #include "squid.h"
64
65 static hash_table *htable = NULL;
66
67 static int cbdataCount = 0;
68
69 typedef struct _cbdata {
70 const void *key;
71 struct _cbdata *next;
72 int valid;
73 int locks;
74 mem_type mem_type;
75 #if CBDATA_DEBUG
76 const char *file;
77 int line;
78 #endif
79 } cbdata;
80
81 static HASHCMP cbdata_cmp;
82 static HASHHASH cbdata_hash;
83 static void cbdataReallyFree(cbdata *c);
84
85 static int
86 cbdata_cmp(const void *p1, const void *p2)
87 {
88 return (char *) p1 - (char *) p2;
89 }
90
91 static unsigned int
92 cbdata_hash(const void *p, unsigned int mod)
93 {
94 return ((unsigned long) p >> 8) % mod;
95 }
96
97
98 void
99 cbdataInit(void)
100 {
101 debug(45, 3) ("cbdataInit\n");
102 htable = hash_create(cbdata_cmp, 1 << 8, cbdata_hash);
103 cachemgrRegister("cbdata",
104 "Callback Data Registry Contents",
105 cbdataDump, 0);
106 }
107
108 void
109 #if CBDATA_DEBUG
110 cbdataAddDbg(const void *p, mem_type mem_type, const char *file, int line)
111 #else
112 cbdataAdd(const void *p, mem_type mem_type)
113 #endif
114 {
115 cbdata *c;
116 assert(p);
117 debug(45, 3) ("cbdataAdd: %p\n", p);
118 assert(htable != NULL);
119 assert(hash_lookup(htable, p) == NULL);
120 c = xcalloc(1, sizeof(cbdata));
121 c->key = p;
122 c->valid = 1;
123 c->mem_type = mem_type;
124 #if CBDATA_DEBUG
125 c->file = file;
126 c->line = line;
127 #endif
128 hash_join(htable, (hash_link *) c);
129 cbdataCount++;
130 }
131
132 static void
133 cbdataReallyFree(cbdata *c)
134 {
135 mem_type mem_type = c->mem_type;
136 void *p = (void *) c->key;
137 hash_remove_link(htable, (hash_link *) c);
138 cbdataCount--;
139 xfree(c);
140 debug(45, 3) ("cbdataReallyFree: Freeing %p\n", p);
141 if (mem_type == MEM_NONE)
142 xfree(p);
143 else
144 memFree(mem_type, p);
145 }
146
147 void
148 cbdataFree(void *p)
149 {
150 cbdata *c = (cbdata *) hash_lookup(htable, p);
151 assert(p);
152 debug(45, 3) ("cbdataFree: %p\n", p);
153 assert(c != NULL);
154 c->valid = 0;
155 if (c->locks) {
156 debug(45, 3) ("cbdataFree: %p has %d locks, not freeing\n",
157 p, c->locks);
158 return;
159 }
160 cbdataReallyFree(c);
161 }
162
163 void
164 cbdataLock(const void *p)
165 {
166 cbdata *c;
167 if (p == NULL)
168 return;
169 c = (cbdata *) hash_lookup(htable, p);
170 debug(45, 3) ("cbdataLock: %p\n", p);
171 assert(c != NULL);
172 c->locks++;
173 }
174
175 void
176 cbdataUnlock(const void *p)
177 {
178 cbdata *c;
179 if (p == NULL)
180 return;
181 c = (cbdata *) hash_lookup(htable, p);
182 debug(45, 3) ("cbdataUnlock: %p\n", p);
183 assert(c != NULL);
184 assert(c->locks > 0);
185 c->locks--;
186 if (c->valid || c->locks)
187 return;
188 cbdataReallyFree(c);
189 }
190
191 int
192 cbdataValid(const void *p)
193 {
194 cbdata *c;
195 /* Maybe NULL should be considered valid? */
196 if (p == NULL)
197 return 0;
198 c = (cbdata *) hash_lookup(htable, p);
199 debug(45, 3) ("cbdataValid: %p\n", p);
200 assert(c != NULL);
201 assert(c->locks > 0);
202 return c->valid;
203 }
204
205
206 void
207 cbdataDump(StoreEntry * sentry)
208 {
209 hash_link *hptr;
210 cbdata *c;
211 storeAppendPrintf(sentry, "%d cbdata entries\n", cbdataCount);
212 for (hptr = hash_first(htable); hptr; hptr = hash_next(htable)) {
213 c = (cbdata *) hptr;
214 #if CBDATA_DEBUG
215 storeAppendPrintf(sentry, "%20p %10s %d locks %s:%d\n",
216 c->key,
217 c->valid ? "VALID" : "NOT VALID",
218 c->locks,
219 c->file, c->line);
220 #else
221 storeAppendPrintf(sentry, "%20p %10s %d locks\n",
222 c->key,
223 c->valid ? "VALID" : "NOT VALID",
224 c->locks);
225 #endif
226 }
227 }