summaryrefslogtreecommitdiff
path: root/main/astobj2.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/astobj2.c')
-rw-r--r--main/astobj2.c308
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;
}