2 * astobj2 - replacement containers for asterisk data structures.
4 * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
6 * See http://www.asterisk.org for more information about
7 * the Asterisk project. Please do not directly contact
8 * any of the maintainers of this project for assistance;
9 * the project provides a web site, mailing lists and IRC
10 * channels for your use.
12 * This program is free software, distributed under the terms of
13 * the GNU General Public License Version 2. See the LICENSE file
14 * at the top of the source tree.
18 * Function implementing astobj2 objects.
22 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
24 #include "asterisk/_private.h"
25 #include "asterisk/astobj2.h"
26 #include "asterisk/utils.h"
27 #include "asterisk/cli.h"
28 #define REF_FILE "/tmp/refs"
31 * astobj2 objects are always preceded by this data structure,
32 * which contains a lock, a reference counter,
33 * the flags and a pointer to a destructor.
34 * The refcount is used to decide when it is time to
35 * invoke the destructor.
36 * The magic number is used for consistency check.
37 * XXX the lock is not always needed, and its initialization may be
38 * expensive. Consider making it external.
43 ao2_destructor_fn destructor_fn
;
46 /*! magic number. This is used to verify that a pointer passed in is a
51 #define AO2_MAGIC 0xa570b123
54 * What an astobj2 object looks like: fixed-size private data
55 * followed by variable-size user data.
58 struct __priv_data priv_data
;
68 volatile int total_objects
;
69 volatile int total_mem
;
70 volatile int total_containers
;
71 volatile int total_refs
;
72 volatile int total_locked
;
75 static struct ao2_stats ao2
;
78 #ifndef HAVE_BKTR /* backtrace support */
81 #include <execinfo.h> /* for backtrace */
90 c
= backtrace(addresses
, N1
);
91 strings
= backtrace_symbols(addresses
,c
);
92 ast_verbose("backtrace returned: %d\n", c
);
93 for(i
= 0; i
< c
; i
++) {
94 ast_verbose("%d: %p %s\n", i
, addresses
[i
], strings
[i
]);
101 * \brief convert from a pointer _p to a user-defined object
103 * \return the pointer to the astobj2 structure
105 static inline struct astobj2
*INTERNAL_OBJ(void *user_data
)
110 ast_log(LOG_ERROR
, "user_data is NULL\n");
114 p
= (struct astobj2
*) ((char *) user_data
- sizeof(*p
));
115 if (AO2_MAGIC
!= (p
->priv_data
.magic
) ) {
116 ast_log(LOG_ERROR
, "bad magic number 0x%x for %p\n", p
->priv_data
.magic
, p
);
124 * \brief convert from a pointer _p to an astobj2 object
126 * \return the pointer to the user-defined portion.
128 #define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
130 /* the underlying functions common to debug and non-debug versions */
132 static int __ao2_ref(void *user_data
, const int delta
);
133 static void *__ao2_alloc(size_t data_size
, ao2_destructor_fn destructor_fn
);
134 static struct ao2_container
*__ao2_container_alloc(struct ao2_container
*c
, const uint n_buckets
, ao2_hash_fn
*hash_fn
,
135 ao2_callback_fn
*cmp_fn
);
136 static struct bucket_list
*__ao2_link(struct ao2_container
*c
, void *user_data
);
137 static void *__ao2_callback(struct ao2_container
*c
,
138 const enum search_flags flags
, ao2_callback_fn
*cb_fn
, void *arg
,
139 char *tag
, char *file
, int line
, const char *funcname
);
140 static void * __ao2_iterator_next(struct ao2_iterator
*a
, struct bucket_list
**q
);
142 int ao2_lock(void *user_data
)
144 struct astobj2
*p
= INTERNAL_OBJ(user_data
);
150 ast_atomic_fetchadd_int(&ao2
.total_locked
, 1);
153 return ast_mutex_lock(&p
->priv_data
.lock
);
156 int ao2_unlock(void *user_data
)
158 struct astobj2
*p
= INTERNAL_OBJ(user_data
);
164 ast_atomic_fetchadd_int(&ao2
.total_locked
, -1);
167 return ast_mutex_unlock(&p
->priv_data
.lock
);
170 int ao2_trylock(void *user_data
)
172 struct astobj2
*p
= INTERNAL_OBJ(user_data
);
177 ret
= ast_mutex_trylock(&p
->priv_data
.lock
);
180 ast_atomic_fetchadd_int(&ao2
.total_locked
, 1);
185 void *ao2_object_get_lockaddr(void *obj
)
187 struct astobj2
*p
= INTERNAL_OBJ(obj
);
192 return &p
->priv_data
.lock
;
196 * The argument is a pointer to the user portion.
200 int _ao2_ref_debug(void *user_data
, const int delta
, char *tag
, char *file
, int line
, const char *funcname
)
202 struct astobj2
*obj
= INTERNAL_OBJ(user_data
);
208 FILE *refo
= fopen(REF_FILE
,"a");
209 fprintf(refo
, "%p %s%d %s:%d:%s (%s) [@%d]\n", user_data
, (delta
<0? "":"+"), delta
, file
, line
, funcname
, tag
, obj
->priv_data
.ref_counter
);
212 if (obj
->priv_data
.ref_counter
+ delta
== 0 && obj
->priv_data
.destructor_fn
!= NULL
) { /* this isn't protected with lock; just for o/p */
213 FILE *refo
= fopen(REF_FILE
,"a");
214 fprintf(refo
, "%p **call destructor** %s:%d:%s (%s)\n", user_data
, file
, line
, funcname
, tag
);
217 return __ao2_ref(user_data
, delta
);
220 int _ao2_ref(void *user_data
, const int delta
)
222 struct astobj2
*obj
= INTERNAL_OBJ(user_data
);
227 return __ao2_ref(user_data
, delta
);
230 static int __ao2_ref(void *user_data
, const int delta
)
232 struct astobj2
*obj
= INTERNAL_OBJ(user_data
);
236 /* if delta is 0, just return the refcount */
238 return (obj
->priv_data
.ref_counter
);
240 /* we modify with an atomic operation the reference counter */
241 ret
= ast_atomic_fetchadd_int(&obj
->priv_data
.ref_counter
, delta
);
242 current_value
= ret
+ delta
;
245 ast_atomic_fetchadd_int(&ao2
.total_refs
, delta
);
248 /* this case must never happen */
249 if (current_value
< 0)
250 ast_log(LOG_ERROR
, "refcount %d on object %p\n", current_value
, user_data
);
252 if (current_value
<= 0) { /* last reference, destroy the object */
253 if (obj
->priv_data
.destructor_fn
!= NULL
) {
254 obj
->priv_data
.destructor_fn(user_data
);
257 ast_mutex_destroy(&obj
->priv_data
.lock
);
259 ast_atomic_fetchadd_int(&ao2
.total_mem
, - obj
->priv_data
.data_size
);
260 ast_atomic_fetchadd_int(&ao2
.total_objects
, -1);
262 /* for safety, zero-out the astobj2 header and also the
263 * first word of the user-data, which we make sure is always
265 bzero(obj
, sizeof(struct astobj2
*) + sizeof(void *) );
273 * We always alloc at least the size of a void *,
274 * for debugging purposes.
276 static void *__ao2_alloc(size_t data_size
, ao2_destructor_fn destructor_fn
)
281 if (data_size
< sizeof(void *))
282 data_size
= sizeof(void *);
284 obj
= ast_calloc(1, sizeof(*obj
) + data_size
);
289 ast_mutex_init(&obj
->priv_data
.lock
);
290 obj
->priv_data
.magic
= AO2_MAGIC
;
291 obj
->priv_data
.data_size
= data_size
;
292 obj
->priv_data
.ref_counter
= 1;
293 obj
->priv_data
.destructor_fn
= destructor_fn
; /* can be NULL */
296 ast_atomic_fetchadd_int(&ao2
.total_objects
, 1);
297 ast_atomic_fetchadd_int(&ao2
.total_mem
, data_size
);
298 ast_atomic_fetchadd_int(&ao2
.total_refs
, 1);
301 /* return a pointer to the user data */
302 return EXTERNAL_OBJ(obj
);
305 void *_ao2_alloc_debug(size_t data_size
, ao2_destructor_fn destructor_fn
, char *tag
, char *file
, int line
, const char *funcname
)
309 FILE *refo
= fopen(REF_FILE
,"a");
311 obj
= __ao2_alloc(data_size
, destructor_fn
);
317 fprintf(refo
, "%p =1 %s:%d:%s (%s)\n", obj
, file
, line
, funcname
, tag
);
321 /* return a pointer to the user data */
325 void *_ao2_alloc(size_t data_size
, ao2_destructor_fn destructor_fn
)
327 return __ao2_alloc(data_size
, destructor_fn
);
331 /* internal callback to destroy a container. */
332 static void container_destruct(void *c
);
334 /* internal callback to destroy a container. */
335 static void container_destruct_debug(void *c
);
337 /* each bucket in the container is a tailq. */
338 AST_LIST_HEAD_NOLOCK(bucket
, bucket_list
);
341 * A container; stores the hash and callback functions, information on
342 * the size, the hash bucket heads, and a version number, starting at 0
343 * (for a newly created, empty container)
344 * and incremented every time an object is inserted or deleted.
345 * The assumption is that an object is never moved in a container,
346 * but removed and readded with the new number.
347 * The version number is especially useful when implementing iterators.
348 * In fact, we can associate a unique, monotonically increasing number to
349 * each object, which means that, within an iterator, we can store the
350 * version number of the current object, and easily look for the next one,
351 * which is the next one in the list with a higher number.
352 * Since all objects have a version >0, we can use 0 as a marker for
353 * 'we need the first object in the bucket'.
355 * \todo Linking and unlink objects is typically expensive, as it
356 * involves a malloc() of a small object which is very inefficient.
357 * To optimize this, we allocate larger arrays of bucket_list's
358 * when we run out of them, and then manage our own freelist.
359 * This will be more efficient as we can do the freelist management while
360 * we hold the lock (that we need anyways).
362 struct ao2_container
{
363 ao2_hash_fn
*hash_fn
;
364 ao2_callback_fn
*cmp_fn
;
366 /*! Number of elements in the container */
368 /*! described above */
371 struct bucket buckets
[0];
375 * \brief always zero hash function
377 * it is convenient to have a hash function that always returns 0.
378 * This is basically used when we want to have a container that is
379 * a simple linked list.
383 static int hash_zero(const void *user_obj
, const int flags
)
389 * A container is just an object, after all!
391 static struct ao2_container
*__ao2_container_alloc(struct ao2_container
*c
, const uint n_buckets
, ao2_hash_fn
*hash_fn
,
392 ao2_callback_fn
*cmp_fn
)
394 /* XXX maybe consistency check on arguments ? */
395 /* compute the container size */
400 c
->version
= 1; /* 0 is a reserved value here */
401 c
->n_buckets
= n_buckets
;
402 c
->hash_fn
= hash_fn
? hash_fn
: hash_zero
;
406 ast_atomic_fetchadd_int(&ao2
.total_containers
, 1);
412 struct ao2_container
*_ao2_container_alloc_debug(const uint n_buckets
, ao2_hash_fn
*hash_fn
,
413 ao2_callback_fn
*cmp_fn
, char *tag
, char *file
, int line
, const char *funcname
)
415 /* XXX maybe consistency check on arguments ? */
416 /* compute the container size */
417 size_t container_size
= sizeof(struct ao2_container
) + n_buckets
* sizeof(struct bucket
);
418 struct ao2_container
*c
= _ao2_alloc_debug(container_size
, container_destruct_debug
, tag
, file
, line
, funcname
);
420 return __ao2_container_alloc(c
, n_buckets
, hash_fn
, cmp_fn
);
423 struct ao2_container
*
424 _ao2_container_alloc(const uint n_buckets
, ao2_hash_fn
*hash_fn
,
425 ao2_callback_fn
*cmp_fn
)
427 /* XXX maybe consistency check on arguments ? */
428 /* compute the container size */
430 size_t container_size
= sizeof(struct ao2_container
) + n_buckets
* sizeof(struct bucket
);
431 struct ao2_container
*c
= _ao2_alloc(container_size
, container_destruct
);
433 return __ao2_container_alloc(c
, n_buckets
, hash_fn
, cmp_fn
);
437 * return the number of elements in the container
439 int ao2_container_count(struct ao2_container
*c
)
445 * A structure to create a linked list of entries,
446 * used within a bucket.
447 * XXX \todo this should be private to the container code
450 AST_LIST_ENTRY(bucket_list
) entry
;
452 struct astobj2
*astobj
; /* pointer to internal data */
456 * link an object to a container
459 static struct bucket_list
*__ao2_link(struct ao2_container
*c
, void *user_data
)
462 /* create a new list entry */
463 struct bucket_list
*p
;
464 struct astobj2
*obj
= INTERNAL_OBJ(user_data
);
469 if (INTERNAL_OBJ(c
) == NULL
)
472 p
= ast_calloc(1, sizeof(*p
));
476 i
= c
->hash_fn(user_data
, OBJ_POINTER
);
481 p
->version
= ast_atomic_fetchadd_int(&c
->version
, 1);
482 AST_LIST_INSERT_TAIL(&c
->buckets
[i
], p
, entry
);
483 ast_atomic_fetchadd_int(&c
->elements
, 1);
485 /* the last two operations (ao2_ref, ao2_unlock) must be done by the calling func */
489 void *_ao2_link_debug(struct ao2_container
*c
, void *user_data
, char *tag
, char *file
, int line
, const char *funcname
)
491 struct bucket_list
*p
= __ao2_link(c
, user_data
);
494 _ao2_ref_debug(user_data
, +1, tag
, file
, line
, funcname
);
500 void *_ao2_link(struct ao2_container
*c
, void *user_data
)
502 struct bucket_list
*p
= __ao2_link(c
, user_data
);
505 _ao2_ref(user_data
, +1);
512 * \brief another convenience function is a callback that matches on address
514 int ao2_match_by_addr(void *user_data
, void *arg
, int flags
)
516 return (user_data
== arg
) ? (CMP_MATCH
| CMP_STOP
) : 0;
520 * Unlink an object from the container
521 * and destroy the associated * ao2_bucket_list structure.
523 void *_ao2_unlink_debug(struct ao2_container
*c
, void *user_data
, char *tag
,
524 char *file
, int line
, const char *funcname
)
526 if (INTERNAL_OBJ(user_data
) == NULL
) /* safety check on the argument */
529 _ao2_callback_debug(c
, OBJ_UNLINK
| OBJ_POINTER
| OBJ_NODATA
, ao2_match_by_addr
, user_data
, tag
, file
, line
, funcname
);
534 void *_ao2_unlink(struct ao2_container
*c
, void *user_data
)
536 if (INTERNAL_OBJ(user_data
) == NULL
) /* safety check on the argument */
539 _ao2_callback(c
, OBJ_UNLINK
| OBJ_POINTER
| OBJ_NODATA
, ao2_match_by_addr
, user_data
);
545 * \brief special callback that matches all
547 static int cb_true(void *user_data
, void *arg
, int flags
)
549 ast_log(LOG_ERROR
,"If you see this, something is strange!\n");
554 * Browse the container using different stategies accoding the flags.
555 * \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is
557 * Luckily, for debug purposes, the added args (tag, file, line, funcname)
558 * aren't an excessive load to the system, as the callback should not be
559 * called as often as, say, the ao2_ref func is called.
561 static void *__ao2_callback(struct ao2_container
*c
,
562 const enum search_flags flags
, ao2_callback_fn
*cb_fn
, void *arg
,
563 char *tag
, char *file
, int line
, const char *funcname
)
565 int i
, last
; /* search boundaries */
568 if (INTERNAL_OBJ(c
) == NULL
) /* safety check on the argument */
571 if ((flags
& (OBJ_MULTIPLE
| OBJ_NODATA
)) == OBJ_MULTIPLE
) {
572 ast_log(LOG_WARNING
, "multiple data return not implemented yet (flags %x)\n", flags
);
576 /* override the match function if necessary */
577 if (cb_fn
== NULL
) /* if NULL, match everything */
580 * XXX this can be optimized.
581 * If we have a hash function and lookup by pointer,
582 * run the hash function. Otherwise, scan the whole container
583 * (this only for the time being. We need to optimize this.)
585 if ((flags
& OBJ_POINTER
)) /* we know hash can handle this case */
586 i
= c
->hash_fn(arg
, flags
& OBJ_POINTER
) % c
->n_buckets
;
587 else /* don't know, let's scan all buckets */
588 i
= -1; /* XXX this must be fixed later. */
590 /* determine the search boundaries: i..last-1 */
598 ao2_lock(c
); /* avoid modifications to the content */
600 for (; i
< last
; i
++) {
601 /* scan the list with prev-cur pointers */
602 struct bucket_list
*cur
;
604 AST_LIST_TRAVERSE_SAFE_BEGIN(&c
->buckets
[i
], cur
, entry
) {
605 int match
= cb_fn(EXTERNAL_OBJ(cur
->astobj
), arg
, flags
) & (CMP_MATCH
| CMP_STOP
);
607 /* we found the object, performing operations according flags */
608 if (match
== 0) { /* no match, no stop, continue */
610 } else if (match
== CMP_STOP
) { /* no match but stop, we are done */
614 /* we have a match (CMP_MATCH) here */
615 if (!(flags
& OBJ_NODATA
)) { /* if must return the object, record the value */
616 /* it is important to handle this case before the unlink */
617 ret
= EXTERNAL_OBJ(cur
->astobj
);
619 _ao2_ref_debug(ret
, 1, tag
, file
, line
, funcname
);
624 if (flags
& OBJ_UNLINK
) { /* must unlink */
625 struct bucket_list
*x
= cur
;
627 /* we are going to modify the container, so update version */
628 ast_atomic_fetchadd_int(&c
->version
, 1);
629 AST_LIST_REMOVE_CURRENT(entry
);
630 /* update number of elements and version */
631 ast_atomic_fetchadd_int(&c
->elements
, -1);
633 _ao2_ref_debug(EXTERNAL_OBJ(x
->astobj
), -1, tag
, file
, line
, funcname
);
635 _ao2_ref(EXTERNAL_OBJ(x
->astobj
), -1);
636 free(x
); /* free the link record */
639 if ((match
& CMP_STOP
) || (flags
& OBJ_MULTIPLE
) == 0) {
640 /* We found the only match we need */
641 i
= last
; /* force exit from outer loop */
644 if (!(flags
& OBJ_NODATA
)) {
645 #if 0 /* XXX to be completed */
647 * This is the multiple-return case. We need to link
648 * the object in a list. The refcount is already increased.
653 AST_LIST_TRAVERSE_SAFE_END
;
659 void *_ao2_callback_debug(struct ao2_container
*c
,
660 const enum search_flags flags
,
661 ao2_callback_fn
*cb_fn
, void *arg
,
662 char *tag
, char *file
, int line
, const char *funcname
)
664 return __ao2_callback(c
,flags
, cb_fn
, arg
, tag
, file
, line
, funcname
);
667 void *_ao2_callback(struct ao2_container
*c
,const enum search_flags flags
,
668 ao2_callback_fn
*cb_fn
, void *arg
)
670 return __ao2_callback(c
,flags
, cb_fn
, arg
, NULL
, NULL
, 0, NULL
);
674 * the find function just invokes the default callback with some reasonable flags.
676 void *_ao2_find_debug(struct ao2_container
*c
, void *arg
, enum search_flags flags
, char *tag
, char *file
, int line
, const char *funcname
)
678 return _ao2_callback_debug(c
, flags
, c
->cmp_fn
, arg
, tag
, file
, line
, funcname
);
681 void *_ao2_find(struct ao2_container
*c
, void *arg
, enum search_flags flags
)
683 return _ao2_callback(c
, flags
, c
->cmp_fn
, arg
);
687 * initialize an iterator so we start from the first object
689 struct ao2_iterator
ao2_iterator_init(struct ao2_container
*c
, int flags
)
691 struct ao2_iterator a
= {
700 * move to the next element in the container.
702 static void * __ao2_iterator_next(struct ao2_iterator
*a
, struct bucket_list
**q
)
705 struct bucket_list
*p
= NULL
;
710 if (INTERNAL_OBJ(a
->c
) == NULL
)
713 if (!(a
->flags
& F_AO2I_DONTLOCK
))
716 /* optimization. If the container is unchanged and
717 * we have a pointer, try follow it
719 if (a
->c
->version
== a
->c_version
&& (p
= a
->obj
) ) {
720 if ( (p
= AST_LIST_NEXT(p
, entry
)) )
722 /* nope, start from the next bucket */
728 lim
= a
->c
->n_buckets
;
730 /* Browse the buckets array, moving to the next
731 * buckets if we don't find the entry in the current one.
732 * Stop when we find an element with version number greater
733 * than the current one (we reset the version to 0 when we
736 for (; a
->bucket
< lim
; a
->bucket
++, a
->version
= 0) {
737 /* scan the current bucket */
738 AST_LIST_TRAVERSE(&a
->c
->buckets
[a
->bucket
], p
, entry
) {
739 if (p
->version
> a
->version
)
746 a
->version
= p
->version
;
748 a
->c_version
= a
->c
->version
;
749 ret
= EXTERNAL_OBJ(p
->astobj
);
750 /* inc refcount of returned object */
757 void * _ao2_iterator_next_debug(struct ao2_iterator
*a
, char *tag
, char *file
, int line
, const char *funcname
)
759 struct bucket_list
*p
;
762 ret
= __ao2_iterator_next(a
, &p
);
765 /* inc refcount of returned object */
766 _ao2_ref_debug(ret
, 1, tag
, file
, line
, funcname
);
769 if (!(a
->flags
& F_AO2I_DONTLOCK
))
775 void * _ao2_iterator_next(struct ao2_iterator
*a
)
777 struct bucket_list
*p
= NULL
;
780 ret
= __ao2_iterator_next(a
, &p
);
783 /* inc refcount of returned object */
787 if (!(a
->flags
& F_AO2I_DONTLOCK
))
793 /* callback for destroying container.
794 * we can make it simple as we know what it does
796 static int cd_cb(void *obj
, void *arg
, int flag
)
802 static int cd_cb_debug(void *obj
, void *arg
, int flag
)
804 _ao2_ref_debug(obj
, -1, "deref object via container destroy", __FILE__
, __LINE__
, __PRETTY_FUNCTION__
);
808 static void container_destruct(void *_c
)
810 struct ao2_container
*c
= _c
;
812 _ao2_callback(c
, OBJ_UNLINK
, cd_cb
, NULL
);
815 ast_atomic_fetchadd_int(&ao2
.total_containers
, -1);
819 static void container_destruct_debug(void *_c
)
821 struct ao2_container
*c
= _c
;
823 _ao2_callback_debug(c
, OBJ_UNLINK
, cd_cb_debug
, NULL
, "container_destruct_debug called", __FILE__
, __LINE__
, __PRETTY_FUNCTION__
);
826 ast_atomic_fetchadd_int(&ao2
.total_containers
, -1);
831 static int print_cb(void *obj
, void *arg
, int flag
)
834 char *s
= (char *)obj
;
836 ast_cli(*fd
, "string <%s>\n", s
);
843 static char *handle_astobj2_stats(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
847 e
->command
= "astobj2 stats";
848 e
->usage
= "Usage: astobj2 stats\n"
849 " Show astobj2 stats\n";
854 ast_cli(a
->fd
, "Objects : %d\n", ao2
.total_objects
);
855 ast_cli(a
->fd
, "Containers : %d\n", ao2
.total_containers
);
856 ast_cli(a
->fd
, "Memory : %d\n", ao2
.total_mem
);
857 ast_cli(a
->fd
, "Locked : %d\n", ao2
.total_locked
);
858 ast_cli(a
->fd
, "Refs : %d\n", ao2
.total_refs
);
863 * This is testing code for astobj
865 static char *handle_astobj2_test(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
867 struct ao2_container
*c1
;
870 static int prof_id
= -1;
871 struct ast_cli_args fake_args
= { a
->fd
, 0, NULL
};
875 e
->command
= "astobj2 test";
876 e
->usage
= "Usage: astobj2 test <num>\n"
877 " Runs astobj2 test. Creates 'num' objects,\n"
878 " and test iterators, callbacks and may be other stuff\n";
885 return CLI_SHOWUSAGE
;
889 prof_id
= ast_add_profile("ao2_alloc", 0);
891 ast_cli(a
->fd
, "argc %d argv %s %s %s\n", a
->argc
, a
->argv
[0], a
->argv
[1], a
->argv
[2]);
892 lim
= atoi(a
->argv
[2]);
893 ast_cli(a
->fd
, "called astobj_test\n");
895 handle_astobj2_stats(e
, CLI_HANDLER
, &fake_args
);
897 * allocate a container with no default callback, and no hash function.
898 * No hash means everything goes in the same bucket.
900 c1
= ao2_t_container_alloc(100, NULL
/* no callback */, NULL
/* no hash */,"test");
901 ast_cli(a
->fd
, "container allocated as %p\n", c1
);
904 * fill the container with objects.
905 * ao2_alloc() gives us a reference which we pass to the
906 * container when we do the insert.
908 for (i
= 0; i
< lim
; i
++) {
909 ast_mark(prof_id
, 1 /* start */);
910 obj
= ao2_t_alloc(80, NULL
,"test");
911 ast_mark(prof_id
, 0 /* stop */);
912 ast_cli(a
->fd
, "object %d allocated as %p\n", i
, obj
);
913 sprintf(obj
, "-- this is obj %d --", i
);
916 ast_cli(a
->fd
, "testing callbacks\n");
917 ao2_t_callback(c1
, 0, print_cb
, &a
->fd
,"test callback");
918 ast_cli(a
->fd
, "testing iterators, remove every second object\n");
920 struct ao2_iterator ai
;
923 ai
= ao2_iterator_init(c1
, 0);
924 while ( (obj
= ao2_t_iterator_next(&ai
,"test")) ) {
925 ast_cli(a
->fd
, "iterator on <%s>\n", obj
);
927 ao2_t_unlink(c1
, obj
,"test");
928 ao2_t_ref(obj
, -1,"test");
930 ast_cli(a
->fd
, "testing iterators again\n");
931 ai
= ao2_iterator_init(c1
, 0);
932 while ( (obj
= ao2_t_iterator_next(&ai
,"test")) ) {
933 ast_cli(a
->fd
, "iterator on <%s>\n", obj
);
934 ao2_t_ref(obj
, -1,"test");
937 ast_cli(a
->fd
, "testing callbacks again\n");
938 ao2_t_callback(c1
, 0, print_cb
, &a
->fd
,"test callback");
940 ast_verbose("now you should see an error message:\n");
941 ao2_t_ref(&i
, -1, ""); /* i is not a valid object so we print an error here */
943 ast_cli(a
->fd
, "destroy container\n");
944 ao2_t_ref(c1
, -1, ""); /* destroy container */
945 handle_astobj2_stats(e
, CLI_HANDLER
, &fake_args
);
949 static struct ast_cli_entry cli_astobj2
[] = {
950 AST_CLI_DEFINE(handle_astobj2_stats
, "Print astobj2 statistics"),
951 AST_CLI_DEFINE(handle_astobj2_test
, "Test astobj2"),
953 #endif /* AO2_DEBUG */
955 int astobj2_init(void)
958 ast_cli_register_multiple(cli_astobj2
, ARRAY_LEN(cli_astobj2
));