30 #define DEBUG_THREADS_LOOSE_ABI
42 #define ast_log ast_log_safe
58 #if defined(AO2_DEBUG)
96 #define AO2_MAGIC 0xa70b123
97 #define AO2_WEAK 0xa70b122
98 #define IS_AO2_MAGIC_BAD(p) (AO2_MAGIC != (p->priv_data.magic | 1))
110 ao2_weakproxy_notification_cb cb;
151 struct ao2_stats ao2;
154 #define INTERNAL_OBJ_MUTEX(user_data) \
155 ((struct astobj2_lock *) (((char *) (user_data)) - sizeof(struct astobj2_lock)))
157 #define INTERNAL_OBJ_RWLOCK(user_data) \
158 ((struct astobj2_rwlock *) (((char *) (user_data)) - sizeof(struct astobj2_rwlock)))
160 #define INTERNAL_OBJ_LOCKOBJ(user_data) \
161 ((struct astobj2_lockobj *) (((char *) (user_data)) - sizeof(struct astobj2_lockobj)))
163 #define INTERNAL_OBJ(user_data) \
164 (struct astobj2 *) ((char *) user_data - sizeof(struct astobj2))
171 #define __INTERNAL_OBJ_CHECK(user_data, file, line, func) \
173 struct astobj2 *p ## __LINE__; \
175 || !(p ## __LINE__ = INTERNAL_OBJ(user_data)) \
176 || IS_AO2_MAGIC_BAD(p ## __LINE__)) { \
177 log_bad_ao2(user_data, file, line, func); \
178 p ## __LINE__ = NULL; \
183 #define INTERNAL_OBJ_CHECK(user_data) \
184 __INTERNAL_OBJ_CHECK(user_data, __FILE__, __LINE__, __PRETTY_FUNCTION__)
191 #define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
193 int internal_is_ao2_object(
void *user_data)
201 p = INTERNAL_OBJ(user_data);
203 return !p || IS_AO2_MAGIC_BAD(p) ? 0 : 1;
206 void log_bad_ao2(
void *user_data,
const char *file,
int line,
const char *func)
212 __ast_assert_failed(0,
"user_data is NULL", file, line, func);
216 p = INTERNAL_OBJ(user_data);
217 snprintf(bad_magic,
sizeof(bad_magic),
"bad magic number 0x%x for object %p",
218 p->priv_data.magic, user_data);
219 __ast_assert_failed(0, bad_magic, file, line, func);
222 int __ao2_lock(
void *user_data,
enum ao2_lock_req lock_how,
const char *file,
const char *func,
int line,
const char *var)
240 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
241 res = __ast_pthread_mutex_lock(file, line, func, var, &obj_mutex->mutex.lock);
249 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
253 res = __ast_rwlock_wrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
262 res = __ast_rwlock_rdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
275 case AO2_ALLOC_OPT_LOCK_OBJ:
276 obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
277 res =
__ao2_lock(obj_lockobj->lockobj.lock, lock_how, file, func, line, var);
280 ast_log(__LOG_ERROR, file, line, func,
"Invalid lock option on ao2 object %p\n",
288 int __ao2_unlock(
void *user_data,
const char *file,
const char *func,
int line,
const char *var)
303 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
304 res = __ast_pthread_mutex_unlock(file, line, func, var, &obj_mutex->mutex.lock);
312 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
315 if (current_value < 0) {
319 res = __ast_rwlock_unlock(file, line, func, &obj_rwlock->rwlock.lock, var);
329 case AO2_ALLOC_OPT_LOCK_OBJ:
330 obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
331 res =
__ao2_unlock(obj_lockobj->lockobj.lock, file, func, line, var);
334 ast_log(__LOG_ERROR, file, line, func,
"Invalid lock option on ao2 object %p\n",
360 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
361 res = __ast_pthread_mutex_trylock(file, line, func, var, &obj_mutex->mutex.lock);
369 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
373 res = __ast_rwlock_trywrlock(file, line, func, &obj_rwlock->rwlock.lock, var);
382 res = __ast_rwlock_tryrdlock(file, line, func, &obj_rwlock->rwlock.lock, var);
395 case AO2_ALLOC_OPT_LOCK_OBJ:
396 obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
397 res =
__ao2_trylock(obj_lockobj->lockobj.lock, lock_how, file, func, line, var);
400 ast_log(__LOG_ERROR, file, line, func,
"Invalid lock option on ao2 object %p\n",
427 struct astobj2 *obj = INTERNAL_OBJ(user_data);
434 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
445 if (lock_how != orig_lock) {
447 ao2_unlock(user_data);
448 ao2_wrlock(user_data);
452 if (!keep_stronger && lock_how != orig_lock) {
454 ao2_unlock(user_data);
455 ao2_rdlock(user_data);
460 case AO2_ALLOC_OPT_LOCK_OBJ:
461 obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
462 orig_lock = __adjust_lock(obj_lockobj->lockobj.lock, lock_how, keep_stronger);
465 ast_log(LOG_ERROR,
"Invalid lock option on ao2 object %p\n", user_data);
481 obj = INTERNAL_OBJ_CHECK(user_data);
489 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
490 return &obj_mutex->mutex.lock;
498 int __ao2_ref(
void *user_data,
int delta,
499 const char *tag,
const char *file,
int line,
const char *func)
505 int32_t current_value;
507 uint32_t privdataoptions;
509 const char *lock_state;
512 if (ref_log && user_data) {
513 fprintf(ref_log,
"%p,%d,%d,%s,%d,%s,**invalid**,%s\n",
514 user_data, delta,
ast_get_tid(), file, line, func, tag ?:
"");
525 if (delta < 0 && obj->priv_data.magic == AO2_MAGIC && (weakproxy = obj->priv_data.
weakptr)) {
531 current_value = ret + delta;
540 if (current_value == 1) {
542 struct astobj2 *internal_weakproxy;
544 internal_weakproxy = INTERNAL_OBJ_CHECK(weakproxy);
547 internal_weakproxy->priv_data.
weakptr = NULL;
551 cbs.destroyed_cb = weakproxy->destroyed_cb;
558 ao2_unlock(weakproxy);
560 if (current_value == 1) {
565 destroyed_cb->cb(weakproxy, destroyed_cb->data);
566 ast_free(destroyed_cb);
573 if (0 < current_value) {
575 #define EXCESSIVE_REF_COUNT 100000
577 if (EXCESSIVE_REF_COUNT <= current_value && ret < EXCESSIVE_REF_COUNT) {
578 char excessive_ref_buf[100];
581 snprintf(excessive_ref_buf,
sizeof(excessive_ref_buf),
582 "Excessive refcount %d reached on ao2 object %p",
583 (
int)current_value, user_data);
584 ast_log(__LOG_ERROR, file, line, func,
"%s\n", excessive_ref_buf);
586 __ast_assert_failed(0, excessive_ref_buf, file, line, func);
590 fprintf(ref_log,
"%p,%s%d,%d,%s,%d,%s,%d,%s\n", user_data,
592 file, line, func, (
int)ret, tag ?:
"");
599 if (current_value < 0) {
600 ast_log(__LOG_ERROR, file, line, func,
601 "Invalid refcount %d on ao2 object %p\n", (
int)current_value, user_data);
604 fprintf(ref_log,
"%p,%d,%d,%s,%d,%s,**invalid**,%s\n",
605 user_data, delta,
ast_get_tid(), file, line, func, tag ?:
"");
614 if (obj->priv_data.destructor_fn != NULL) {
615 obj->priv_data.destructor_fn(user_data);
624 obj->priv_data.magic = 0;
626 privdataoptions = obj->priv_data.
options;
630 obj_mutex = INTERNAL_OBJ_MUTEX(user_data);
631 lock_state = obj->priv_data.
lockused ?
"used" :
"unused";
632 ast_mutex_destroy(&obj_mutex->mutex.lock);
637 obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data);
638 lock_state = obj->priv_data.
lockused ?
"used" :
"unused";
639 ast_rwlock_destroy(&obj_rwlock->rwlock.lock);
641 ast_free(obj_rwlock);
647 case AO2_ALLOC_OPT_LOCK_OBJ:
648 obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
649 lock_state = obj->priv_data.
lockused ?
"used" :
"unused";
650 ao2_t_ref(obj_lockobj->lockobj.lock, -1,
"release lockobj");
652 ast_free(obj_lockobj);
655 ast_log(__LOG_ERROR, file, line, func,
656 "Invalid lock option on ao2 object %p\n", user_data);
657 lock_state =
"invalid";
662 fprintf(ref_log,
"%p,%d,%d,%s,%d,%s,**destructor**lock-state:%s**,%s\n",
663 user_data, delta,
ast_get_tid(), file, line, func, lock_state, tag ?:
"");
670 void __ao2_cleanup_debug(
void *obj,
const char *tag,
const char *file,
int line,
const char *
function)
673 __ao2_ref(obj, -1, tag, file, line,
function);
684 static void *internal_ao2_alloc(
size_t data_size,
ao2_destructor_fn destructor_fn,
unsigned int options,
685 void *lockobj,
const char *tag,
const char *file,
int line,
const char *func)
696 overhead =
sizeof(*obj_mutex);
697 obj_mutex = __ast_calloc(1, overhead + data_size, file, line, func);
698 if (obj_mutex == NULL) {
702 ast_mutex_init(&obj_mutex->mutex.lock);
703 obj = (
struct astobj2 *) &obj_mutex->priv_data;
706 overhead =
sizeof(*obj_rwlock);
707 obj_rwlock = __ast_calloc(1, overhead + data_size, file, line, func);
708 if (obj_rwlock == NULL) {
713 obj = (
struct astobj2 *) &obj_rwlock->priv_data;
716 overhead =
sizeof(*obj);
717 obj = __ast_calloc(1, overhead + data_size, file, line, func);
722 case AO2_ALLOC_OPT_LOCK_OBJ:
723 lockobj = ao2_t_bump(lockobj,
"set lockobj");
725 ast_log(__LOG_ERROR, file, line, func,
"AO2_ALLOC_OPT_LOCK_OBJ requires a non-NULL lockobj.\n");
729 overhead =
sizeof(*obj_lockobj);
730 obj_lockobj = __ast_calloc(1, overhead + data_size, file, line, func);
731 if (obj_lockobj == NULL) {
732 ao2_t_ref(lockobj, -1,
"release lockobj for failed alloc");
736 obj_lockobj->lockobj.lock = lockobj;
737 obj = (
struct astobj2 *) &obj_lockobj->priv_data;
741 ast_log(__LOG_DEBUG, file, line, func,
"Invalid lock option requested\n");
746 obj->priv_data.destructor_fn = destructor_fn;
748 obj->priv_data.
options = options;
749 obj->priv_data.magic = AO2_MAGIC;
752 obj->priv_data.data_size = data_size;
758 if (ref_log && !(obj->priv_data.
options & AO2_ALLOC_OPT_NO_REF_DEBUG)) {
759 fprintf(ref_log,
"%p,+1,%d,%s,%d,%s,**constructor**%zu**%zu**,%s\n",
768 void *__ao2_alloc(
size_t data_size,
ao2_destructor_fn destructor_fn,
unsigned int options,
769 const char *tag,
const char *file,
int line,
const char *func)
771 return internal_ao2_alloc(data_size, destructor_fn, options, NULL, tag, file, line, func);
774 void *__ao2_alloc_with_lockobj(
size_t data_size,
ao2_destructor_fn destructor_fn,
void *lockobj,
775 const char *tag,
const char *file,
int line,
const char *func)
777 return internal_ao2_alloc(data_size, destructor_fn, AO2_ALLOC_OPT_LOCK_OBJ, lockobj,
778 tag, file, line, func);
785 orig_obj = INTERNAL_OBJ_CHECK(obj);
789 return orig_obj->priv_data.
options;
794 const char *tag,
const char *file,
int line,
const char *func)
798 if (data_size <
sizeof(*weakproxy)) {
800 ast_log(LOG_ERROR,
"Requested data_size smaller than minimum.\n");
805 tag, file, line, func);
808 struct astobj2 *weakproxy_internal;
811 weakproxy_internal = INTERNAL_OBJ(weakproxy);
812 weakproxy_internal->priv_data.magic = AO2_WEAK;
818 int __ao2_weakproxy_set_object(
void *weakproxy,
void *obj,
int flags,
819 const char *tag,
const char *file,
int line,
const char *func)
825 if (!weakproxy_internal
826 || weakproxy_internal->priv_data.magic != AO2_WEAK) {
831 || obj_internal->priv_data.
weakptr
832 || obj_internal->priv_data.magic != AO2_MAGIC) {
840 if (!weakproxy_internal->priv_data.
weakptr) {
841 __ao2_ref(obj, +1, tag, file, line, func);
842 __ao2_ref(weakproxy, +1, tag, file, line, func);
844 weakproxy_internal->priv_data.
weakptr = obj;
845 obj_internal->priv_data.
weakptr = weakproxy;
850 if (!(flags & OBJ_NOLOCK)) {
851 ao2_unlock(weakproxy);
862 int __ao2_weakproxy_ref_object(
void *weakproxy,
int delta,
int flags,
863 const char *tag,
const char *file,
int line,
const char *func)
868 if (!
internal || internal->priv_data.magic != AO2_WEAK) {
874 if (!(flags & OBJ_NOLOCK)) {
878 if (internal->priv_data.weakptr) {
879 ret = __ao2_ref(internal->priv_data.weakptr, delta, tag, file, line, func);
882 if (!(flags & OBJ_NOLOCK)) {
883 ao2_unlock(weakproxy);
889 void *__ao2_weakproxy_get_object(
void *weakproxy,
int flags,
890 const char *tag,
const char *file,
int line,
const char *func)
895 if (!
internal || internal->priv_data.magic != AO2_WEAK) {
901 if (!(flags & OBJ_NOLOCK)) {
905 obj =
internal->priv_data.weakptr;
907 __ao2_ref(obj, +1, tag, file, line, func);
910 if (!(flags & OBJ_NOLOCK)) {
911 ao2_unlock(weakproxy);
917 void *__ao2_get_weakproxy(
void *obj,
const char *tag,
const char *file,
int line,
const char *func)
921 if (!obj_internal || obj_internal->priv_data.magic != AO2_MAGIC) {
926 if (!obj_internal->priv_data.
weakptr) {
930 __ao2_ref(obj_internal->priv_data.
weakptr, +1, tag, file, line, func);
931 return obj_internal->priv_data.
weakptr;
936 struct astobj2 *weakproxy_internal = INTERNAL_OBJ_CHECK(weakproxy);
940 if (!weakproxy_internal || weakproxy_internal->priv_data.magic != AO2_WEAK) {
944 if (!(flags & OBJ_NOLOCK)) {
948 hasobj = weakproxy_internal->priv_data.
weakptr != NULL;
961 if (!(flags & OBJ_NOLOCK)) {
962 ao2_unlock(weakproxy);
975 struct astobj2 *internal_weakproxy = INTERNAL_OBJ_CHECK(weakproxy);
980 if (!internal_weakproxy || internal_weakproxy->priv_data.magic != AO2_WEAK || !destroyed_cb) {
984 if (!(flags & OBJ_NOLOCK)) {
990 if (sub->cb == destroyed_cb && sub->data == data) {
1001 if (!(flags & OBJ_NOLOCK)) {
1002 ao2_unlock(weakproxy);
1010 static int print_cb(
void *obj,
void *arg,
int flag)
1013 char *s = (
char *)obj;
1015 ast_cli(a->fd,
"string <%s>\n", s);
1026 e->
command =
"astobj2 show stats";
1027 e->
usage =
"Usage: astobj2 show stats\n"
1028 " Show astobj2 show stats\n";
1033 ast_cli(a->fd,
"Objects : %d\n", ao2.total_objects);
1034 ast_cli(a->fd,
"Containers : %d\n", ao2.total_containers);
1035 ast_cli(a->fd,
"Memory : %d\n", ao2.total_mem);
1036 ast_cli(a->fd,
"Locked : %d\n", ao2.total_locked);
1037 ast_cli(a->fd,
"Refs : %d\n", ao2.total_refs);
1050 static int prof_id = -1;
1056 e->
usage =
"Usage: astobj2 test <num>\n"
1057 " Runs astobj2 test. Creates 'num' objects,\n"
1058 " and test iterators, callbacks and maybe other stuff\n";
1065 return CLI_SHOWUSAGE;
1068 if (prof_id == -1) {
1072 ast_cli(a->fd,
"argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]);
1073 lim = atoi(a->argv[2]);
1074 ast_cli(a->fd,
"called astobj_test\n");
1076 handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
1082 ast_cli(a->fd,
"container allocated as %p\n", c1);
1089 for (i = 0; i < lim; i++) {
1090 ast_mark(prof_id, 1 );
1091 obj = ao2_t_alloc(80, NULL,
"test");
1092 ast_mark(prof_id, 0 );
1093 ast_cli(a->fd,
"object %d allocated as %p\n", i, obj);
1094 sprintf(obj,
"-- this is obj %d --", i);
1101 ao2_t_ref(obj, -1,
"test");
1104 ast_cli(a->fd,
"testing callbacks\n");
1105 ao2_t_callback(c1, 0, print_cb, a,
"test callback");
1107 ast_cli(a->fd,
"testing container cloning\n");
1110 ast_cli(a->fd,
"Cloned container does not have the same number of objects!\n");
1112 ao2_t_callback(c2, 0, print_cb, a,
"test callback");
1114 ast_cli(a->fd,
"testing iterators, remove every second object\n");
1120 while ( (obj = ao2_t_iterator_next(&ai,
"test")) ) {
1121 ast_cli(a->fd,
"iterator on <%s>\n", obj);
1123 ao2_t_unlink(c1, obj,
"test");
1124 ao2_t_ref(obj, -1,
"test");
1127 ast_cli(a->fd,
"testing iterators again\n");
1129 while ( (obj = ao2_t_iterator_next(&ai,
"test")) ) {
1130 ast_cli(a->fd,
"iterator on <%s>\n", obj);
1131 ao2_t_ref(obj, -1,
"test");
1136 ast_cli(a->fd,
"testing callbacks again\n");
1137 ao2_t_callback(c1, 0, print_cb, a,
"test callback");
1139 ast_verbose(
"now you should see an error and possible assertion failure messages:\n");
1140 ao2_t_ref(&i, -1,
"");
1142 ast_cli(a->fd,
"destroy container\n");
1143 ao2_t_ref(c1, -1,
"");
1144 ao2_t_ref(c2, -1,
"");
1145 handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
1150 #if defined(AO2_DEBUG)
1152 AST_CLI_DEFINE(handle_astobj2_stats,
"Print astobj2 statistics"),
1153 AST_CLI_DEFINE(handle_astobj2_test,
"Test astobj2"),
1157 static void astobj2_cleanup(
void)
1159 #if defined(AO2_DEBUG)
1163 if (ast_opt_ref_debug) {
1171 char ref_filename[1024];
1173 if (ast_opt_ref_debug) {
1174 snprintf(ref_filename,
sizeof(ref_filename),
"%s/refs", ast_config_AST_LOG_DIR);
1175 ref_log = fopen(ref_filename,
"w");
1177 ast_log(LOG_ERROR,
"Could not open ref debug log file: %s\n", ref_filename);
1183 if (container_init() != 0) {
1189 #if defined(AO2_DEBUG)
int __ao2_trylock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
Try locking– (don't block if fail)
unsigned int ao2_options_get(void *obj)
Retrieve the ao2 options used to create the object.
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
uint32_t options
The ao2 object option flags.
int __ao2_lock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var)
Lock an object.
ao2_lock_req
Which lock to request.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Common, private definitions for astobj2.
#define ao2_container_clone(orig, flags)
Create a clone/copy of the given container.
descriptor for a cli entry.
#define ast_rwlock_init(rwlock)
wrapper for rwlock with tracking enabled
#define ast_atomic_fetch_add(ptr, val, memorder)
Support for atomic instructions.
int ao2_weakproxy_subscribe(void *weakproxy, ao2_weakproxy_notification_cb cb, void *data, int flags)
Request notification when weakproxy points to NULL.
Assume that the ao2_container is already locked.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define __INTERNAL_OBJ_CHECK(user_data, file, line, func)
convert from a pointer _p to a user-defined object
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
int __ao2_unlock(void *user_data, const char *file, const char *func, int line, const char *var)
Unlock an object.
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
void __ao2_cleanup(void *obj)
void * ao2_object_get_lockaddr(void *user_data)
Return the mutex lock address of an object.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Asterisk file paths, configured in asterisk.conf.
int ast_get_tid(void)
Get current thread ID.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
uint32_t lockused
Set to 1 when the lock is used if refdebug is enabled.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define EXTERNAL_OBJ(_p)
convert from a pointer _p to an astobj2 object
int ast_add_profile(const char *, uint64_t scale)
support for event profiling
void(* ao2_destructor_fn)(void *vdoomed)
Typedef for an object destructor.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
#define ast_calloc(num, len)
A wrapper for calloc()
Prototypes for public functions only of internal interest,.
int ao2_weakproxy_unsubscribe(void *weakproxy, ao2_weakproxy_notification_cb destroyed_cb, void *data, int flags)
Remove notification of real object destruction.
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Structure for rwlock and tracking information.
Standard Command Line Interface.
This struct should be opaque, but it's size is needed.
Common, private definitions for astobj2 containers.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
Structure for mutex and tracking information.
#define ao2_link(container, obj)
Add an object to a container.