diff options
Diffstat (limited to 'main/astobj2.c')
-rw-r--r-- | main/astobj2.c | 308 |
1 files changed, 269 insertions, 39 deletions
diff --git a/main/astobj2.c b/main/astobj2.c index 8c783955b..007f12545 100644 --- a/main/astobj2.c +++ b/main/astobj2.c @@ -25,6 +25,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/astobj2.h" #include "asterisk/utils.h" #include "asterisk/cli.h" +#define REF_FILE "/tmp/refs" /*! * astobj2 objects are always preceded by this data structure, @@ -126,6 +127,18 @@ static inline struct astobj2 *INTERNAL_OBJ(void *user_data) */ #define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data) +/* the underlying functions common to debug and non-debug versions */ + +static int __ao2_ref(void *user_data, const int delta); +static void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn); +static struct ao2_container *__ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn, + ao2_callback_fn *cmp_fn); +static struct bucket_list *__ao2_link(struct ao2_container *c, void *user_data); +static void *__ao2_callback(struct ao2_container *c, + const enum search_flags flags, ao2_callback_fn *cb_fn, void *arg, + char *tag, char *file, int line, const char *funcname); +static void * __ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q); + int ao2_lock(void *user_data) { struct astobj2 *p = INTERNAL_OBJ(user_data); @@ -154,18 +167,72 @@ int ao2_unlock(void *user_data) return ast_mutex_unlock(&p->priv_data.lock); } +int ao2_trylock(void *user_data) +{ + struct astobj2 *p = INTERNAL_OBJ(user_data); + int ret; + + if (p == NULL) + return -1; + ret = ast_mutex_trylock(&p->priv_data.lock); +#ifdef AO2_DEBUG + if (!ret) + ast_atomic_fetchadd_int(&ao2.total_locked, 1); +#endif + return ret; +} + +void *ao2_object_get_lockaddr(void *obj) +{ + struct astobj2 *p = INTERNAL_OBJ(obj); + + if (p == NULL) + return NULL; + + return &p->priv_data.lock; +} + /* * The argument is a pointer to the user portion. */ -int ao2_ref(void *user_data, const int delta) + + +int _ao2_ref_debug(void *user_data, const int delta, char *tag, char *file, int line, const char *funcname) +{ + struct astobj2 *obj = INTERNAL_OBJ(user_data); + + if (obj == NULL) + return -1; + + if (delta != 0) { + FILE *refo = fopen(REF_FILE,"a"); + 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); + fclose(refo); + } + if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */ + FILE *refo = fopen(REF_FILE,"a"); + fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag); + fclose(refo); + } + return __ao2_ref(user_data, delta); +} + +int _ao2_ref(void *user_data, const int delta) { - int current_value; - int ret; struct astobj2 *obj = INTERNAL_OBJ(user_data); if (obj == NULL) return -1; + return __ao2_ref(user_data, delta); +} + +static int __ao2_ref(void *user_data, const int delta) +{ + struct astobj2 *obj = INTERNAL_OBJ(user_data); + int current_value; + int ret; + /* if delta is 0, just return the refcount */ if (delta == 0) return (obj->priv_data.ref_counter); @@ -183,8 +250,9 @@ int ao2_ref(void *user_data, const int delta) ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data); if (current_value <= 0) { /* last reference, destroy the object */ - if (obj->priv_data.destructor_fn != NULL) + if (obj->priv_data.destructor_fn != NULL) { obj->priv_data.destructor_fn(user_data); + } ast_mutex_destroy(&obj->priv_data.lock); #ifdef AO2_DEBUG @@ -205,7 +273,7 @@ int ao2_ref(void *user_data, const int delta) * We always alloc at least the size of a void *, * for debugging purposes. */ -void *ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn) +static void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn) { /* allocation */ struct astobj2 *obj; @@ -234,9 +302,38 @@ void *ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn) return EXTERNAL_OBJ(obj); } +void *_ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, char *tag, char *file, int line, const char *funcname) +{ + /* allocation */ + void *obj; + FILE *refo = fopen(REF_FILE,"a"); + + obj = __ao2_alloc(data_size, destructor_fn); + + if (obj == NULL) + return NULL; + + if (refo) { + fprintf(refo, "%p =1 %s:%d:%s (%s)\n", obj, file, line, funcname, tag); + fclose(refo); + } + + /* return a pointer to the user data */ + return obj; +} + +void *_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn) +{ + return __ao2_alloc(data_size, destructor_fn); +} + + /* internal callback to destroy a container. */ static void container_destruct(void *c); +/* internal callback to destroy a container. */ +static void container_destruct_debug(void *c); + /* each bucket in the container is a tailq. */ AST_LIST_HEAD_NOLOCK(bucket, bucket_list); @@ -291,15 +388,11 @@ static int hash_zero(const void *user_obj, const int flags) /* * A container is just an object, after all! */ -struct ao2_container * -ao2_container_alloc(const uint n_buckets, ao2_hash_fn *hash_fn, - ao2_callback_fn *cmp_fn) +static struct ao2_container *__ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn, + ao2_callback_fn *cmp_fn) { /* XXX maybe consistency check on arguments ? */ /* compute the container size */ - size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket); - - struct ao2_container *c = ao2_alloc(container_size, container_destruct); if (!c) return NULL; @@ -316,6 +409,30 @@ ao2_container_alloc(const uint n_buckets, ao2_hash_fn *hash_fn, return c; } +struct ao2_container *_ao2_container_alloc_debug(const uint n_buckets, ao2_hash_fn *hash_fn, + ao2_callback_fn *cmp_fn, char *tag, char *file, int line, const char *funcname) +{ + /* XXX maybe consistency check on arguments ? */ + /* compute the container size */ + size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket); + struct ao2_container *c = _ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname); + + return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn); +} + +struct ao2_container * +_ao2_container_alloc(const uint n_buckets, ao2_hash_fn *hash_fn, + ao2_callback_fn *cmp_fn) +{ + /* XXX maybe consistency check on arguments ? */ + /* compute the container size */ + + size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket); + struct ao2_container *c = _ao2_alloc(container_size, container_destruct); + + return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn); +} + /*! * return the number of elements in the container */ @@ -338,7 +455,8 @@ struct bucket_list { /* * link an object to a container */ -void *ao2_link(struct ao2_container *c, void *user_data) + +static struct bucket_list *__ao2_link(struct ao2_container *c, void *user_data) { int i; /* create a new list entry */ @@ -363,9 +481,30 @@ void *ao2_link(struct ao2_container *c, void *user_data) p->version = ast_atomic_fetchadd_int(&c->version, 1); AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry); ast_atomic_fetchadd_int(&c->elements, 1); - ao2_ref(user_data, +1); - ao2_unlock(c); + + /* the last two operations (ao2_ref, ao2_unlock) must be done by the calling func */ + return p; +} + +void *_ao2_link_debug(struct ao2_container *c, void *user_data, char *tag, char *file, int line, const char *funcname) +{ + struct bucket_list *p = __ao2_link(c, user_data); + if (p) { + _ao2_ref_debug(user_data, +1, tag, file, line, funcname); + ao2_unlock(c); + } + return p; +} + +void *_ao2_link(struct ao2_container *c, void *user_data) +{ + struct bucket_list *p = __ao2_link(c, user_data); + + if (p) { + _ao2_ref(user_data, +1); + ao2_unlock(c); + } return p; } @@ -381,12 +520,23 @@ int ao2_match_by_addr(void *user_data, void *arg, int flags) * Unlink an object from the container * and destroy the associated * ao2_bucket_list structure. */ -void *ao2_unlink(struct ao2_container *c, void *user_data) +void *_ao2_unlink_debug(struct ao2_container *c, void *user_data, char *tag, + char *file, int line, const char *funcname) { if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */ return NULL; - ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data); + _ao2_callback_debug(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data, tag, file, line, funcname); + + return NULL; +} + +void *_ao2_unlink(struct ao2_container *c, void *user_data) +{ + if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */ + return NULL; + + _ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data); return NULL; } @@ -396,6 +546,7 @@ void *ao2_unlink(struct ao2_container *c, void *user_data) */ static int cb_true(void *user_data, void *arg, int flags) { + ast_log(LOG_ERROR,"If you see this, something is strange!\n"); return CMP_MATCH; } @@ -403,10 +554,13 @@ static int cb_true(void *user_data, void *arg, int flags) * Browse the container using different stategies accoding the flags. * \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is * specified. + * Luckily, for debug purposes, the added args (tag, file, line, funcname) + * aren't an excessive load to the system, as the callback should not be + * called as often as, say, the ao2_ref func is called. */ -void *ao2_callback(struct ao2_container *c, - const enum search_flags flags, - ao2_callback_fn *cb_fn, void *arg) +static void *__ao2_callback(struct ao2_container *c, + const enum search_flags flags, ao2_callback_fn *cb_fn, void *arg, + char *tag, char *file, int line, const char *funcname) { int i, last; /* search boundaries */ void *ret = NULL; @@ -461,7 +615,10 @@ void *ao2_callback(struct ao2_container *c, if (!(flags & OBJ_NODATA)) { /* if must return the object, record the value */ /* it is important to handle this case before the unlink */ ret = EXTERNAL_OBJ(cur->astobj); - ao2_ref(ret, 1); + if (tag) + _ao2_ref_debug(ret, 1, tag, file, line, funcname); + else + _ao2_ref(ret, 1); } if (flags & OBJ_UNLINK) { /* must unlink */ @@ -472,7 +629,10 @@ void *ao2_callback(struct ao2_container *c, AST_LIST_REMOVE_CURRENT(entry); /* update number of elements and version */ ast_atomic_fetchadd_int(&c->elements, -1); - ao2_ref(EXTERNAL_OBJ(x->astobj), -1); + if (tag) + _ao2_ref_debug(EXTERNAL_OBJ(x->astobj), -1, tag, file, line, funcname); + else + _ao2_ref(EXTERNAL_OBJ(x->astobj), -1); free(x); /* free the link record */ } @@ -496,12 +656,31 @@ void *ao2_callback(struct ao2_container *c, return ret; } +void *_ao2_callback_debug(struct ao2_container *c, + const enum search_flags flags, + ao2_callback_fn *cb_fn, void *arg, + char *tag, char *file, int line, const char *funcname) +{ + return __ao2_callback(c,flags, cb_fn, arg, tag, file, line, funcname); +} + +void *_ao2_callback(struct ao2_container *c,const enum search_flags flags, + ao2_callback_fn *cb_fn, void *arg) +{ + return __ao2_callback(c,flags, cb_fn, arg, NULL, NULL, 0, NULL); +} + /*! * the find function just invokes the default callback with some reasonable flags. */ -void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags) +void *_ao2_find_debug(struct ao2_container *c, void *arg, enum search_flags flags, char *tag, char *file, int line, const char *funcname) { - return ao2_callback(c, flags, c->cmp_fn, arg); + return _ao2_callback_debug(c, flags, c->cmp_fn, arg, tag, file, line, funcname); +} + +void *_ao2_find(struct ao2_container *c, void *arg, enum search_flags flags) +{ + return _ao2_callback(c, flags, c->cmp_fn, arg); } /*! @@ -520,12 +699,14 @@ struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) /* * move to the next element in the container. */ -void * ao2_iterator_next(struct ao2_iterator *a) +static void * __ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q) { int lim; struct bucket_list *p = NULL; void *ret = NULL; + *q = NULL; + if (INTERNAL_OBJ(a->c) == NULL) return NULL; @@ -567,7 +748,40 @@ found: a->c_version = a->c->version; ret = EXTERNAL_OBJ(p->astobj); /* inc refcount of returned object */ - ao2_ref(ret, 1); + *q = p; + } + + return ret; +} + +void * _ao2_iterator_next_debug(struct ao2_iterator *a, char *tag, char *file, int line, const char *funcname) +{ + struct bucket_list *p; + void *ret = NULL; + + ret = __ao2_iterator_next(a, &p); + + if (p) { + /* inc refcount of returned object */ + _ao2_ref_debug(ret, 1, tag, file, line, funcname); + } + + if (!(a->flags & F_AO2I_DONTLOCK)) + ao2_unlock(a->c); + + return ret; +} + +void * _ao2_iterator_next(struct ao2_iterator *a) +{ + struct bucket_list *p = NULL; + void *ret = NULL; + + ret = __ao2_iterator_next(a, &p); + + if (p) { + /* inc refcount of returned object */ + _ao2_ref(ret, 1); } if (!(a->flags & F_AO2I_DONTLOCK)) @@ -581,7 +795,13 @@ found: */ static int cd_cb(void *obj, void *arg, int flag) { - ao2_ref(obj, -1); + _ao2_ref(obj, -1); + return 0; +} + +static int cd_cb_debug(void *obj, void *arg, int flag) +{ + _ao2_ref_debug(obj, -1, "deref object via container destroy", __FILE__, __LINE__, __PRETTY_FUNCTION__); return 0; } @@ -589,7 +809,18 @@ static void container_destruct(void *_c) { struct ao2_container *c = _c; - ao2_callback(c, OBJ_UNLINK, cd_cb, NULL); + _ao2_callback(c, OBJ_UNLINK, cd_cb, NULL); + +#ifdef AO2_DEBUG + ast_atomic_fetchadd_int(&ao2.total_containers, -1); +#endif +} + +static void container_destruct_debug(void *_c) +{ + struct ao2_container *c = _c; + + _ao2_callback_debug(c, OBJ_UNLINK, cd_cb_debug, NULL, "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__); #ifdef AO2_DEBUG ast_atomic_fetchadd_int(&ao2.total_containers, -1); @@ -666,7 +897,7 @@ static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cl * allocate a container with no default callback, and no hash function. * No hash means everything goes in the same bucket. */ - c1 = ao2_container_alloc(100, NULL /* no callback */, NULL /* no hash */); + c1 = ao2_t_container_alloc(100, NULL /* no callback */, NULL /* no hash */,"test"); ast_cli(a->fd, "container allocated as %p\n", c1); /* @@ -676,42 +907,41 @@ static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cl */ for (i = 0; i < lim; i++) { ast_mark(prof_id, 1 /* start */); - obj = ao2_alloc(80, NULL); + obj = ao2_t_alloc(80, NULL,"test"); ast_mark(prof_id, 0 /* stop */); ast_cli(a->fd, "object %d allocated as %p\n", i, obj); sprintf(obj, "-- this is obj %d --", i); ao2_link(c1, obj); } ast_cli(a->fd, "testing callbacks\n"); - ao2_callback(c1, 0, print_cb, &a->fd); - + ao2_t_callback(c1, 0, print_cb, &a->fd,"test callback"); ast_cli(a->fd, "testing iterators, remove every second object\n"); { struct ao2_iterator ai; int x = 0; ai = ao2_iterator_init(c1, 0); - while ( (obj = ao2_iterator_next(&ai)) ) { + while ( (obj = ao2_t_iterator_next(&ai,"test")) ) { ast_cli(a->fd, "iterator on <%s>\n", obj); if (x++ & 1) - ao2_unlink(c1, obj); - ao2_ref(obj, -1); + ao2_t_unlink(c1, obj,"test"); + ao2_t_ref(obj, -1,"test"); } ast_cli(a->fd, "testing iterators again\n"); ai = ao2_iterator_init(c1, 0); - while ( (obj = ao2_iterator_next(&ai)) ) { + while ( (obj = ao2_t_iterator_next(&ai,"test")) ) { ast_cli(a->fd, "iterator on <%s>\n", obj); - ao2_ref(obj, -1); + ao2_t_ref(obj, -1,"test"); } } ast_cli(a->fd, "testing callbacks again\n"); - ao2_callback(c1, 0, print_cb, &a->fd); + ao2_t_callback(c1, 0, print_cb, &a->fd,"test callback"); ast_verbose("now you should see an error message:\n"); - ao2_ref(&i, -1); /* i is not a valid object so we print an error here */ + ao2_t_ref(&i, -1, ""); /* i is not a valid object so we print an error here */ ast_cli(a->fd, "destroy container\n"); - ao2_ref(c1, -1); /* destroy container */ + ao2_t_ref(c1, -1, ""); /* destroy container */ handle_astobj2_stats(e, CLI_HANDLER, &fake_args); return CLI_SUCCESS; } |