Improve object destruction (bug #3286)
[asterisk/asterisk.git] / include / asterisk / astobj.h
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Object Model for Asterisk
5  * 
6  * Copyright (C) 2004-2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #ifndef _ASTERISK_ASTOBJ_H
15 #define _ASTERISK_ASTOBJ_H
16
17 #include <string.h>
18
19 /*!
20   \file astobj.h
21   \brief A set of macros implementing the asterisk object and container.  Macros
22          are used for maximum performance, to support multiple inheritance, and
23                  to be easily integrated into existing structures without additional 
24                  malloc calls, etc.
25 */
26
27 #if defined(__cplusplus) || defined(c_plusplus)
28 extern "C" {
29 #endif
30
31 #define ASTOBJ_DEFAULT_NAMELEN  80
32 #define ASTOBJ_DEFAULT_BUCKETS  256
33 #define ASTOBJ_DEFAULT_HASH             ast_strhash
34
35 #define ASTOBJ_FLAG_MARKED      (1 << 0)                /* Object has been marked for future operation */
36
37 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
38 #define __builtin_expect(exp, c) (exp)
39 #endif
40
41 /* C++ is simply a syntactic crutch for those who cannot think for themselves
42    in an object oriented way. */
43
44 #define ASTOBJ_RDLOCK(object) ast_mutex_lock(&(object)->_lock)
45 #define ASTOBJ_WRLOCK(object) ast_mutex_lock(&(object)->_lock)
46 #define ASTOBJ_UNLOCK(object) ast_mutex_unlock(&(object)->_lock)
47
48 #ifdef ASTOBJ_CONTAINER_HASHMODEL 
49 #define __ASTOBJ_HASH(type,hashes) \
50         type *next[hashes] 
51 #else 
52 #define __ASTOBJ_HASH(type,hashes) \
53         type *next[1] 
54 #endif  
55
56 #define ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes) \
57         char name[namelen]; \
58         int refcount; \
59         int objflags; \
60         __ASTOBJ_HASH(type,hashes)
61         
62 #define ASTOBJ_COMPONENTS_NOLOCK(type) \
63         ASTOBJ_COMPONENTS_NOLOCK_FULL(type,ASTOBJ_DEFAULT_NAMELEN,1)
64
65 #define ASTOBJ_COMPONENTS(type) \
66         ASTOBJ_COMPONENTS_NOLOCK(type); \
67         ast_mutex_t _lock; 
68         
69 #define ASTOBJ_COMPONENTS_FULL(type,namelen,hashes) \
70         ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes); \
71         ast_mutex_t _lock; 
72
73 #define ASTOBJ_REF(object) \
74         ({ \
75                 ASTOBJ_WRLOCK(object); \
76                 (object)->refcount++; \
77                 ASTOBJ_UNLOCK(object); \
78                 (object); \
79         })
80         
81 #define ASTOBJ_UNREF(object,destructor) \
82         do { \
83                 int newcount = 0; \
84                 ASTOBJ_WRLOCK(object); \
85                 if (__builtin_expect((object)->refcount, 1)) \
86                         newcount = --((object)->refcount); \
87                 else \
88                         ast_log(LOG_WARNING, "Unreferencing unreferenced (object)!\n"); \
89                 ASTOBJ_UNLOCK(object); \
90                 if (newcount == 0) { \
91                         ast_mutex_destroy(&(object)->_lock); \
92                         destructor((object)); \
93                 } \
94                 (object) = NULL; \
95         } while(0)
96
97 #define ASTOBJ_MARK(object) \
98         do { \
99                 ASTOBJ_WRLOCK(object); \
100                 (object)->objflags |= ASTOBJ_FLAG_MARKED; \
101                 ASTOBJ_UNLOCK(object); \
102         } while(0)
103         
104 #define ASTOBJ_UNMARK(object) \
105         do { \
106                 ASTOBJ_WRLOCK(object); \
107                 (object)->objflags &= ~ASTOBJ_FLAG_MARKED; \
108                 ASTOBJ_UNLOCK(object); \
109         } while(0)
110
111 #define ASTOBJ_INIT(object) \
112         do { \
113                 ast_mutex_init(&(object)->_lock); \
114                 object->name[0] = '\0'; \
115                 object->refcount = 1; \
116         } while(0)
117
118 /* Containers for objects -- current implementation is linked lists, but
119    should be able to be converted to hashes relatively easily */
120
121 #define ASTOBJ_CONTAINER_RDLOCK(container) ast_mutex_lock(&(container)->_lock)
122 #define ASTOBJ_CONTAINER_WRLOCK(container) ast_mutex_lock(&(container)->_lock)
123 #define ASTOBJ_CONTAINER_UNLOCK(container) ast_mutex_unlock(&(container)->_lock)
124
125 #ifdef ASTOBJ_CONTAINER_HASHMODEL
126 #error "Hash model for object containers not yet implemented!"
127 #else
128 /* Linked lists */
129 #define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,hashes,buckets) \
130         type *head
131
132 #define ASTOBJ_CONTAINER_INIT_FULL(container,hashes,buckets) \
133         do { \
134                 ast_mutex_init(&(container)->_lock); \
135         } while(0)
136         
137 #define ASTOBJ_CONTAINER_DESTROY_FULL(container,hashes,buckets) \
138         do { \
139                 ast_mutex_destroy(&(container)->_lock); \
140         } while(0)
141
142 #define ASTOBJ_CONTAINER_TRAVERSE(container,continue,eval) \
143         do { \
144                 typeof((container)->head) iterator; \
145                 typeof((container)->head) next; \
146                 ASTOBJ_CONTAINER_RDLOCK(container); \
147                 next = (container)->head; \
148                 while((continue) && (iterator = next)) { \
149                         next = iterator->next[0]; \
150                         eval; \
151                 } \
152                 ASTOBJ_CONTAINER_UNLOCK(container); \
153         } while(0)
154
155 #define ASTOBJ_CONTAINER_FIND(container,namestr) \
156         ({ \
157                 typeof((container)->head) found = NULL; \
158                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
159                         if (!(strcasecmp(iterator->name, (namestr)))) \
160                                 found = ASTOBJ_REF(iterator); \
161                 } while (0)); \
162                 found; \
163         })
164
165 #define ASTOBJ_CONTAINER_FIND_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
166         ({ \
167                 typeof((container)->head) found = NULL; \
168                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
169                         ASTOBJ_RDLOCK(iterator); \
170                         if (!(comparefunc(iterator->field, (data)))) { \
171                                 found = ASTOBJ_REF(iterator); \
172                         } \
173                         ASTOBJ_UNLOCK(iterator); \
174                 } while (0)); \
175                 found; \
176         })
177
178 #define ASTOBJ_CONTAINER_DESTROYALL(container,destructor) \
179         do { \
180                 typeof((container)->head) iterator; \
181                 ASTOBJ_CONTAINER_WRLOCK(container); \
182                 while((iterator = (container)->head)) { \
183                         (container)->head = (iterator)->next[0]; \
184                         ASTOBJ_UNREF(iterator,destructor); \
185                 } \
186                 ASTOBJ_CONTAINER_UNLOCK(container); \
187         } while(0)
188
189 #define ASTOBJ_CONTAINER_FIND_UNLINK(container,namestr) \
190         ({ \
191                 typeof((container)->head) found = NULL; \
192                 typeof((container)->head) prev = NULL; \
193                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
194                         if (!(strcasecmp(iterator->name, (namestr)))) { \
195                                 found = iterator; \
196                                 found->next[0] = NULL; \
197                                 ASTOBJ_CONTAINER_WRLOCK(container); \
198                                 if (prev) \
199                                         prev->next[0] = next; \
200                                 else \
201                                         (container)->head = next; \
202                                 ASTOBJ_CONTAINER_UNLOCK(container); \
203                         } \
204                         prev = iterator; \
205                 } while (0)); \
206                 found; \
207         })
208
209 #define ASTOBJ_CONTAINER_FIND_UNLINK_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
210         ({ \
211                 typeof((container)->head) found = NULL; \
212                 typeof((container)->head) prev = NULL; \
213                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
214                         ASTOBJ_RDLOCK(iterator); \
215                         if (!(comparefunc(iterator->field, (data)))) { \
216                                 found = iterator; \
217                                 found->next[0] = NULL; \
218                                 ASTOBJ_CONTAINER_WRLOCK(container); \
219                                 if (prev) \
220                                         prev->next[0] = next; \
221                                 else \
222                                         (container)->head = next; \
223                                 ASTOBJ_CONTAINER_UNLOCK(container); \
224                         } \
225                         ASTOBJ_UNLOCK(iterator); \
226                         prev = iterator; \
227                 } while (0)); \
228                 found; \
229         })
230
231 #define ASTOBJ_CONTAINER_PRUNE_MARKED(container,destructor) \
232         do { \
233                 typeof((container)->head) prev = NULL; \
234                 ASTOBJ_CONTAINER_TRAVERSE(container, 1, do { \
235                         ASTOBJ_RDLOCK(iterator); \
236                         if (iterator->objflags & ASTOBJ_FLAG_MARKED) { \
237                                 ASTOBJ_CONTAINER_WRLOCK(container); \
238                                 if (prev) \
239                                         prev->next[0] = next; \
240                                 else \
241                                         (container)->head = next; \
242                                 ASTOBJ_CONTAINER_UNLOCK(container); \
243                                 ASTOBJ_UNLOCK(iterator); \
244                                 ASTOBJ_UNREF(iterator,destructor); \
245                                 continue; \
246                         } \
247                         ASTOBJ_UNLOCK(iterator); \
248                         prev = iterator; \
249                 } while (0)); \
250         } while(0)
251
252 #define ASTOBJ_CONTAINER_LINK_FULL(container,newobj,data,field,hashfunc,hashoffset,comparefunc) \
253         do { \
254                 ASTOBJ_CONTAINER_WRLOCK(container); \
255                 (newobj)->next[0] = (container)->head; \
256                 (container)->head = ASTOBJ_REF(newobj); \
257                 ASTOBJ_CONTAINER_UNLOCK(container); \
258         } while(0)
259
260 #endif /* List model */
261
262 /* Common to hash and linked list models */
263 #define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type) \
264         ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,1,ASTOBJ_DEFAULT_BUCKETS)
265
266 #define ASTOBJ_CONTAINER_COMPONENTS(type) \
267         ast_mutex_t _lock; \
268         ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type)
269
270 #define ASTOBJ_CONTAINER_INIT(container) \
271         ASTOBJ_CONTAINER_INIT_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS)
272
273 #define ASTOBJ_CONTAINER_DESTROY(container) \
274         ASTOBJ_CONTAINER_DESTROY_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS)
275
276 #define ASTOBJ_CONTAINER_LINK(container,newobj) \
277         ASTOBJ_CONTAINER_LINK_FULL(container,newobj,(newobj)->name,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp)
278
279 #define ASTOBJ_CONTAINER_MARKALL(container) \
280         ASTOBJ_CONTAINER_TRAVERSE(container, 1, ASTOBJ_MARK(iterator))
281
282 #define ASTOBJ_CONTAINER_UNMARKALL(container) \
283         ASTOBJ_CONTAINER_TRAVERSE(container, 1, ASTOBJ_UNMARK(iterator))
284
285 #define ASTOBJ_DUMP(s,slen,obj) \
286         snprintf((s),(slen),"name: %s\nobjflags: %d\nrefcount: %d\n\n", (obj)->name, (obj)->objflags, (obj)->refcount);
287
288 #define ASTOBJ_CONTAINER_DUMP(fd,s,slen,container) \
289         ASTOBJ_CONTAINER_TRAVERSE(container, 1, do { ASTOBJ_DUMP(s,slen,iterator); ast_cli(fd, s); } while(0))
290
291 #if defined(__cplusplus) || defined(c_plusplus)
292 }
293 #endif
294
295 #endif