Merge kpflemings ASTOBJ improvements (bug #3167)
[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 /* C++ is simply a syntactic crutch for those who cannot think for themselves
39    in an object oriented way. */
40
41 #define ASTOBJ_RDLOCK(object) ast_mutex_lock(&(object)->_lock)
42 #define ASTOBJ_WRLOCK(object) ast_mutex_lock(&(object)->_lock)
43 #define ASTOBJ_UNLOCK(object) ast_mutex_unlock(&(object)->_lock)
44
45 #ifdef ASTOBJ_CONTAINER_HASHMODEL 
46 #define __ASTOBJ_HASH(type,hashes) \
47         type *next[hashes] 
48 #else 
49 #define __ASTOBJ_HASH(type,hashes) \
50         type *next[1] 
51 #endif  
52
53 #define ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes) \
54         char name[namelen]; \
55         int refcount; \
56         int objflags; \
57         __ASTOBJ_HASH(type,hashes)
58         
59 #define ASTOBJ_COMPONENTS_NOLOCK(type) \
60         ASTOBJ_COMPONENTS_NOLOCK_FULL(type,ASTOBJ_DEFAULT_NAMELEN,1)
61
62 #define ASTOBJ_COMPONENTS(type) \
63         ast_mutex_t _lock; \
64         ASTOBJ_COMPONENTS_NOLOCK(type)
65         
66 #define ASTOBJ_COMPONENTS_FULL(type,namelen,hashes) \
67         ast_mutex_t _lock; \
68         ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes)
69
70 #define ASTOBJ_REF(object) \
71         ({ \
72                 ASTOBJ_WRLOCK(object); \
73                 (object)->refcount++; \
74                 ASTOBJ_UNLOCK(object); \
75                 (object); \
76         })
77         
78 #define ASTOBJ_UNREF(object,destructor) \
79         do { \
80                 ASTOBJ_WRLOCK(object); \
81                 if (__builtin_expect((object)->refcount, 1)) \
82                         (object)->refcount--; \
83                 else \
84                         ast_log(LOG_WARNING, "Unreferencing unreferenced (object)!\n"); \
85                 ASTOBJ_UNLOCK(object); \
86                 ASTOBJ_DESTROY(object,destructor); \
87                 (object) = NULL; \
88         } while(0)
89
90 #define ASTOBJ_MARK(object) \
91         do { \
92                 ASTOBJ_WRLOCK(object); \
93                 (object)->objflags |= ASTOBJ_FLAG_MARKED; \
94                 ASTOBJ_UNLOCK(object); \
95         } while(0)
96         
97 #define ASTOBJ_UNMARK(object) \
98         do { \
99                 ASTOBJ_WRLOCK(object); \
100                 (object)->objflags &= ~ASTOBJ_FLAG_MARKED; \
101                 ASTOBJ_UNLOCK(object); \
102         } while(0)
103
104 #define ASTOBJ_DESTROY(object,destructor) \
105         do { \
106                 if (__builtin_expect((object)->refcount, 1)) { \
107                         ASTOBJ_WRLOCK(object); \
108                         (object)->objflags |= ASTOBJ_FLAG_DELME; \
109                         ASTOBJ_UNLOCK(object); \
110                 } else { \
111                         ast_mutex_destroy(&(object)->_lock); \
112                         destructor((object)); \
113                 } \
114         } while(0)
115         
116 #define ASTOBJ_INIT(object) \
117         do { \
118                 ast_mutex_init(&(object)->_lock); \
119                 object->name[0] = '\0'; \
120                 object->refcount = 1; \
121         } while(0)
122
123 /* Containers for objects -- current implementation is linked lists, but
124    should be able to be converted to hashes relatively easily */
125
126 #define ASTOBJ_CONTAINER_RDLOCK(container) ast_mutex_lock(&(container)->_lock)
127 #define ASTOBJ_CONTAINER_WRLOCK(container) ast_mutex_lock(&(container)->_lock)
128 #define ASTOBJ_CONTAINER_UNLOCK(container) ast_mutex_unlock(&(container)->_lock)
129
130 #ifdef ASTOBJ_CONTAINER_HASHMODEL
131 #error "Hash model for object containers not yet implemented!"
132 #else
133 /* Linked lists */
134 #define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,hashes,buckets) \
135         type *head
136
137 #define ASTOBJ_CONTAINER_INIT_FULL(container,hashes,buckets) \
138         do { \
139                 ast_mutex_init(&(container)->_lock); \
140         } while(0)
141         
142 #define ASTOBJ_CONTAINER_DESTROY_FULL(container,hashes,buckets) \
143         do { \
144                 ast_mutex_destroy(&(container)->_lock); \
145         } while(0)
146
147 #define ASTOBJ_CONTAINER_TRAVERSE(container,eval) \
148         do { \
149                 typeof((container)->head) iterator; \
150                 typeof((container)->head) next; \
151                 ASTOBJ_CONTAINER_RDLOCK(container); \
152                 next = (container)->head; \
153                 while((iterator = next)) { \
154                         next = iterator->next[0]; \
155                         eval; \
156                 } \
157                 ASTOBJ_CONTAINER_UNLOCK(container); \
158         } while(0)
159
160 #define ASTOBJ_CONTAINER_FIND_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
161         ({ \
162                 typeof((container)->head) found = NULL; \
163                 ASTOBJ_CONTAINER_TRAVERSE(container, do { \
164                         ASTOBJ_RDLOCK(iterator); \
165                         if (!(comparefunc(iterator->field, (data)))) { \
166                                 found = ASTOBJ_REF(iterator); \
167                         } \
168                         ASTOBJ_UNLOCK(iterator); \
169                         if (found) \
170                                 break; \
171                 } while (0)); \
172                 found; \
173         })
174
175 #define ASTOBJ_CONTAINER_DESTROYALL(container,destructor) \
176         do { \
177                 typeof((container)->head) iterator; \
178                 ASTOBJ_CONTAINER_WRLOCK(container); \
179                 while((iterator = (container)->head)) { \
180                         (container)->head = (iterator)->next[0]; \
181                         ASTOBJ_DESTROY(iterator,destructor); \
182                 } \
183                 ASTOBJ_CONTAINER_UNLOCK(container); \
184         } while(0)
185
186 #define ASTOBJ_CONTAINER_FIND_UNLINK_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
187         ({ \
188                 typeof((container)->head) found = NULL; \
189                 typeof((container)->head) prev = NULL; \
190                 ASTOBJ_CONTAINER_TRAVERSE(container, do { \
191                         ASTOBJ_RDLOCK(iterator); \
192                         if (!(comparefunc(iterator->field, (data)))) { \
193                                 found = ASTOBJ_REF(iterator); \
194                                 ASTOBJ_CONTAINER_WRLOCK(container); \
195                                 if (prev) \
196                                         prev->next[0] = next; \
197                                 else \
198                                         (container)->head = next; \
199                                 ASTOBJ_CONTAINER_UNLOCK(container); \
200                                 break; \
201                         } \
202                         ASTOBJ_UNLOCK(iterator); \
203                         if (found) \
204                                 break; \
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, 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 = (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,ASTOBJ_MARK(iterator))
266
267 #define ASTOBJ_CONTAINER_UNMARKALL(container) \
268         ASTOBJ_CONTAINER_TRAVERSE(container,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,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