Include lock performance (bug #3234)
[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(container,namestr) \
165         ({ \
166                 typeof((container)->head) found = NULL; \
167                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
168                         if (!(strcasecmp(iterator->name, (namestr)))) \
169                                 found = ASTOBJ_REF(iterator); \
170                 } while (0)); \
171                 found; \
172         })
173
174 #define ASTOBJ_CONTAINER_FIND_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
175         ({ \
176                 typeof((container)->head) found = NULL; \
177                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
178                         ASTOBJ_RDLOCK(iterator); \
179                         if (!(comparefunc(iterator->field, (data)))) { \
180                                 found = ASTOBJ_REF(iterator); \
181                         } \
182                         ASTOBJ_UNLOCK(iterator); \
183                 } while (0)); \
184                 found; \
185         })
186
187 #define ASTOBJ_CONTAINER_DESTROYALL(container,destructor) \
188         do { \
189                 typeof((container)->head) iterator; \
190                 ASTOBJ_CONTAINER_WRLOCK(container); \
191                 while((iterator = (container)->head)) { \
192                         (container)->head = (iterator)->next[0]; \
193                         ASTOBJ_DESTROY(iterator,destructor); \
194                         ASTOBJ_UNREF(iterator,destructor); \
195                 } \
196                 ASTOBJ_CONTAINER_UNLOCK(container); \
197         } while(0)
198
199 #define ASTOBJ_CONTAINER_FIND_UNLINK(container,namestr) \
200         ({ \
201                 typeof((container)->head) found = NULL; \
202                 typeof((container)->head) prev = NULL; \
203                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
204                         if (!(strcasecmp(iterator->name, (namestr)))) { \
205                                 found = iterator; \
206                                 ASTOBJ_CONTAINER_WRLOCK(container); \
207                                 if (prev) \
208                                         prev->next[0] = next; \
209                                 else \
210                                         (container)->head = next; \
211                                 ASTOBJ_CONTAINER_UNLOCK(container); \
212                         } \
213                         prev = iterator; \
214                 } while (0)); \
215                 found; \
216         })
217
218 #define ASTOBJ_CONTAINER_FIND_UNLINK_FULL(container,data,field,hashfunc,hashoffset,comparefunc) \
219         ({ \
220                 typeof((container)->head) found = NULL; \
221                 typeof((container)->head) prev = NULL; \
222                 ASTOBJ_CONTAINER_TRAVERSE(container, !found, do { \
223                         ASTOBJ_RDLOCK(iterator); \
224                         if (!(comparefunc(iterator->field, (data)))) { \
225                                 found = iterator; \
226                                 ASTOBJ_CONTAINER_WRLOCK(container); \
227                                 if (prev) \
228                                         prev->next[0] = next; \
229                                 else \
230                                         (container)->head = next; \
231                                 ASTOBJ_CONTAINER_UNLOCK(container); \
232                         } \
233                         ASTOBJ_UNLOCK(iterator); \
234                         prev = iterator; \
235                 } while (0)); \
236                 found; \
237         })
238
239 #define ASTOBJ_CONTAINER_PRUNE_MARKED(container,destructor) \
240         do { \
241                 typeof((container)->head) prev = NULL; \
242                 ASTOBJ_CONTAINER_TRAVERSE(container, 1, do { \
243                         ASTOBJ_RDLOCK(iterator); \
244                         if (iterator->objflags & ASTOBJ_FLAG_MARKED) { \
245                                 ASTOBJ_CONTAINER_WRLOCK(container); \
246                                 if (prev) \
247                                         prev->next[0] = next; \
248                                 else \
249                                         (container)->head = next; \
250                                 ASTOBJ_CONTAINER_UNLOCK(container); \
251                                 ASTOBJ_UNLOCK(iterator); \
252                                 ASTOBJ_DESTROY(iterator,destructor); \
253                                 continue; \
254                         } \
255                         ASTOBJ_UNLOCK(iterator); \
256                         prev = iterator; \
257                 } while (0)); \
258         } while(0)
259
260 #define ASTOBJ_CONTAINER_LINK_FULL(container,newobj,data,field,hashfunc,hashoffset,comparefunc) \
261         do { \
262                 ASTOBJ_CONTAINER_WRLOCK(container); \
263                 (newobj)->next[0] = (container)->head; \
264                 (container)->head = ASTOBJ_REF(newobj); \
265                 ASTOBJ_CONTAINER_UNLOCK(container); \
266         } while(0)
267
268 #endif /* List model */
269
270 /* Common to hash and linked list models */
271 #define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type) \
272         ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,1,ASTOBJ_DEFAULT_BUCKETS)
273
274 #define ASTOBJ_CONTAINER_COMPONENTS(type) \
275         ast_mutex_t _lock; \
276         ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type)
277
278 #define ASTOBJ_CONTAINER_INIT(container) \
279         ASTOBJ_CONTAINER_INIT_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS)
280
281 #define ASTOBJ_CONTAINER_DESTROY(container) \
282         ASTOBJ_CONTAINER_DESTROY_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS)
283
284 #define ASTOBJ_CONTAINER_LINK(container,newobj) \
285         ASTOBJ_CONTAINER_LINK_FULL(container,newobj,(newobj)->name,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp)
286
287 #define ASTOBJ_CONTAINER_MARKALL(container) \
288         ASTOBJ_CONTAINER_TRAVERSE(container, 1, ASTOBJ_MARK(iterator))
289
290 #define ASTOBJ_CONTAINER_UNMARKALL(container) \
291         ASTOBJ_CONTAINER_TRAVERSE(container, 1, ASTOBJ_UNMARK(iterator))
292
293 #define ASTOBJ_DUMP(s,slen,obj) \
294         snprintf((s),(slen),"name: %s\nobjflags: %d\nrefcount: %d\n\n", (obj)->name, (obj)->objflags, (obj)->refcount);
295
296 #define ASTOBJ_CONTAINER_DUMP(fd,s,slen,container) \
297         ASTOBJ_CONTAINER_TRAVERSE(container, 1, do { ASTOBJ_DUMP(s,slen,iterator); ast_cli(fd, s); } while(0))
298
299 #if defined(__cplusplus) || defined(c_plusplus)
300 }
301 #endif
302
303 #endif