Allow early exit from traverse (bug #3221)
[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_DELME       (1 << 0)                /* Object has been deleted, remove on last unref */
36 #define ASTOBJ_FLAG_MARKED      (1 << 1)                /* Object has been marked for possible deletion */
37
38 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
39 #define __builtin_expect(exp, c) (exp)
40 #endif
41
42 /* C++ is simply a syntactic crutch for those who cannot think for themselves
43    in an object oriented way. */
44
45 #define ASTOBJ_RDLOCK(object) ast_mutex_lock(&(object)->_lock)
46 #define ASTOBJ_WRLOCK(object) ast_mutex_lock(&(object)->_lock)
47 #define ASTOBJ_UNLOCK(object) ast_mutex_unlock(&(object)->_lock)
48
49 #ifdef ASTOBJ_CONTAINER_HASHMODEL 
50 #define __ASTOBJ_HASH(type,hashes) \
51         type *next[hashes] 
52 #else 
53 #define __ASTOBJ_HASH(type,hashes) \
54         type *next[1] 
55 #endif  
56
57 #define ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes) \
58         char name[namelen]; \
59         int refcount; \
60         int objflags; \
61         __ASTOBJ_HASH(type,hashes)
62         
63 #define ASTOBJ_COMPONENTS_NOLOCK(type) \
64         ASTOBJ_COMPONENTS_NOLOCK_FULL(type,ASTOBJ_DEFAULT_NAMELEN,1)
65
66 #define ASTOBJ_COMPONENTS(type) \
67         ASTOBJ_COMPONENTS_NOLOCK(type); \
68         ast_mutex_t _lock; 
69         
70 #define ASTOBJ_COMPONENTS_FULL(type,namelen,hashes) \
71         ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes); \
72         ast_mutex_t _lock; 
73
74 #define ASTOBJ_REF(object) \
75         ({ \
76                 ASTOBJ_WRLOCK(object); \
77                 (object)->refcount++; \
78                 ASTOBJ_UNLOCK(object); \
79                 (object); \
80         })
81         
82 #define ASTOBJ_UNREF(object,destructor) \
83         do { \
84                 ASTOBJ_WRLOCK(object); \
85                 if (__builtin_expect((object)->refcount, 1)) \
86                         (object)->refcount--; \
87                 else \
88                         ast_log(LOG_WARNING, "Unreferencing unreferenced (object)!\n"); \
89                 ASTOBJ_UNLOCK(object); \
90                 ASTOBJ_DESTROY(object,destructor); \
91                 (object) = NULL; \
92         } while(0)
93
94 #define ASTOBJ_MARK(object) \
95         do { \
96                 ASTOBJ_WRLOCK(object); \
97                 (object)->objflags |= ASTOBJ_FLAG_MARKED; \
98                 ASTOBJ_UNLOCK(object); \
99         } while(0)
100         
101 #define ASTOBJ_UNMARK(object) \
102         do { \
103                 ASTOBJ_WRLOCK(object); \
104                 (object)->objflags &= ~ASTOBJ_FLAG_MARKED; \
105                 ASTOBJ_UNLOCK(object); \
106         } while(0)
107
108 #define ASTOBJ_DESTROY(object,destructor) \
109         do { \
110                 if (__builtin_expect((object)->refcount, 1)) { \
111                         ASTOBJ_WRLOCK(object); \
112                         (object)->objflags |= ASTOBJ_FLAG_DELME; \
113                         ASTOBJ_UNLOCK(object); \
114                 } else { \
115                         ast_mutex_destroy(&(object)->_lock); \
116                         destructor((object)); \
117                 } \
118         } while(0)
119         
120 #define ASTOBJ_INIT(object) \
121         do { \
122                 ast_mutex_init(&(object)->_lock); \
123                 object->name[0] = '\0'; \
124                 object->refcount = 1; \
125         } while(0)
126
127 /* Containers for objects -- current implementation is linked lists, but
128    should be able to be converted to hashes relatively easily */
129
130 #define ASTOBJ_CONTAINER_RDLOCK(container) ast_mutex_lock(&(container)->_lock)
131 #define ASTOBJ_CONTAINER_WRLOCK(container) ast_mutex_lock(&(container)->_lock)
132 #define ASTOBJ_CONTAINER_UNLOCK(container) ast_mutex_unlock(&(container)->_lock)
133
134 #ifdef ASTOBJ_CONTAINER_HASHMODEL
135 #error "Hash model for object containers not yet implemented!"
136 #else
137 /* Linked lists */
138 #define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,hashes,buckets) \
139         type *head
140
141 #define ASTOBJ_CONTAINER_INIT_FULL(container,hashes,buckets) \
142         do { \
143                 ast_mutex_init(&(container)->_lock); \
144         } while(0)
145         
146 #define ASTOBJ_CONTAINER_DESTROY_FULL(container,hashes,buckets) \
147         do { \
148                 ast_mutex_destroy(&(container)->_lock); \
149         } while(0)
150
151 #define ASTOBJ_CONTAINER_TRAVERSE(container,continue,eval) \
152         do { \
153                 typeof((container)->head) iterator; \
154                 typeof((container)->head) next; \
155                 ASTOBJ_CONTAINER_RDLOCK(container); \
156                 next = (container)->head; \
157                 while((continue) && (iterator = next)) { \
158                         next = iterator->next[0]; \
159                         eval; \
160                 } \
161                 ASTOBJ_CONTAINER_UNLOCK(container); \
162         } while(0)
163
164 #define ASTOBJ_CONTAINER_FIND_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
165         ({ \
166                 typeof((container)->head) found = NULL; \
167                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
168                         ASTOBJ_RDLOCK(iterator); \
169                         if (!(comparefunc(iterator->field, (data)))) { \
170                                 found = ASTOBJ_REF(iterator); \
171                         } \
172                         ASTOBJ_UNLOCK(iterator); \
173                 } while (0)); \
174                 found; \
175         })
176
177 #define ASTOBJ_CONTAINER_DESTROYALL(container,destructor) \
178         do { \
179                 typeof((container)->head) iterator; \
180                 ASTOBJ_CONTAINER_WRLOCK(container); \
181                 while((iterator = (container)->head)) { \
182                         (container)->head = (iterator)->next[0]; \
183                         ASTOBJ_DESTROY(iterator,destructor); \
184                         ASTOBJ_UNREF(iterator,destructor); \
185                 } \
186                 ASTOBJ_CONTAINER_UNLOCK(container); \
187         } while(0)
188
189 #define ASTOBJ_CONTAINER_FIND_UNLINK_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
190         ({ \
191                 typeof((container)->head) found = NULL; \
192                 typeof((container)->head) prev = NULL; \
193                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
194                         ASTOBJ_RDLOCK(iterator); \
195                         if (!(comparefunc(iterator->field, (data)))) { \
196                                 found = iterator; \
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                         ASTOBJ_UNLOCK(iterator); \
205                         prev = iterator; \
206                 } while (0)); \
207                 found; \
208         })
209
210 #define ASTOBJ_CONTAINER_PRUNE_MARKED(container,destructor) \
211         do { \
212                 typeof((container)->head) prev = NULL; \
213                 ASTOBJ_CONTAINER_TRAVERSE(container, 1, do { \
214                         ASTOBJ_RDLOCK(iterator); \
215                         if (iterator->objflags & ASTOBJ_FLAG_MARKED) { \
216                                 ASTOBJ_CONTAINER_WRLOCK(container); \
217                                 if (prev) \
218                                         prev->next[0] = next; \
219                                 else \
220                                         (container)->head = next; \
221                                 ASTOBJ_CONTAINER_UNLOCK(container); \
222                                 ASTOBJ_UNLOCK(iterator); \
223                                 ASTOBJ_DESTROY(iterator,destructor); \
224                                 continue; \
225                         } \
226                         ASTOBJ_UNLOCK(iterator); \
227                         prev = iterator; \
228                 } while (0)); \
229         } while(0)
230
231 #define ASTOBJ_CONTAINER_LINK_FULL(container,newobj,data,field,hashfunc,hashoffset,comparefunc) \
232         do { \
233                 ASTOBJ_CONTAINER_WRLOCK(container); \
234                 (newobj)->next[0] = (container)->head; \
235                 (container)->head = ASTOBJ_REF(newobj); \
236                 ASTOBJ_CONTAINER_UNLOCK(container); \
237         } while(0)
238
239 #endif /* List model */
240
241 /* Common to hash and linked list models */
242 #define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type) \
243         ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,1,ASTOBJ_DEFAULT_BUCKETS)
244
245 #define ASTOBJ_CONTAINER_COMPONENTS(type) \
246         ast_mutex_t _lock; \
247         ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type)
248
249 #define ASTOBJ_CONTAINER_INIT(container) \
250         ASTOBJ_CONTAINER_INIT_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS)
251
252 #define ASTOBJ_CONTAINER_DESTROY(container) \
253         ASTOBJ_CONTAINER_DESTROY_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS)
254
255 #define ASTOBJ_CONTAINER_FIND(container,namestr) \
256         ASTOBJ_CONTAINER_FIND_FULL(container,namestr,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp)
257
258 #define ASTOBJ_CONTAINER_FIND_UNLINK(container,namestr) \
259         ASTOBJ_CONTAINER_FIND_UNLINK_FULL(container,namestr,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp)
260
261 #define ASTOBJ_CONTAINER_LINK(container,newobj) \
262         ASTOBJ_CONTAINER_LINK_FULL(container,newobj,(newobj)->name,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp)
263
264 #define ASTOBJ_CONTAINER_MARKALL(container) \
265         ASTOBJ_CONTAINER_TRAVERSE(container, 1, ASTOBJ_MARK(iterator))
266
267 #define ASTOBJ_CONTAINER_UNMARKALL(container) \
268         ASTOBJ_CONTAINER_TRAVERSE(container, 1, ASTOBJ_UNMARK(iterator))
269
270 #define ASTOBJ_DUMP(s,slen,obj) \
271         snprintf((s),(slen),"name: %s\nobjflags: %d\nrefcount: %d\n\n", (obj)->name, (obj)->objflags, (obj)->refcount);
272
273 #define ASTOBJ_CONTAINER_DUMP(fd,s,slen,container) \
274         ASTOBJ_CONTAINER_TRAVERSE(container, 1, do { ASTOBJ_DUMP(s,slen,iterator); ast_cli(fd, s); } while(0))
275
276 #if defined(__cplusplus) || defined(c_plusplus)
277 }
278 #endif
279
280 #endif