aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--astobj2.c722
-rw-r--r--channels/chan_iax2.c389
-rw-r--r--include/asterisk/astobj2.h541
-rw-r--r--include/asterisk/dlinkedlists.h974
5 files changed, 2493 insertions, 135 deletions
diff --git a/Makefile b/Makefile
index 1295d4085..b6c013c42 100644
--- a/Makefile
+++ b/Makefile
@@ -354,7 +354,7 @@ OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \
astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \
netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
- cryptostub.o
+ cryptostub.o astobj2.o
ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/include/sys/poll.h),)
OBJS+= poll.o
diff --git a/astobj2.c b/astobj2.c
new file mode 100644
index 000000000..66c5a2cc3
--- /dev/null
+++ b/astobj2.c
@@ -0,0 +1,722 @@
+/*
+ * astobj2 - replacement containers for asterisk data structures.
+ *
+ * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*
+ * Function implementing astobj2 objects.
+ */
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+
+#include "asterisk/astobj2.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+
+/*!
+ * astobj2 objects are always prepended this data structure,
+ * which contains a lock, a reference counter,
+ * the flags and a pointer to a destructor.
+ * The refcount is used to decide when it is time to
+ * invoke the destructor.
+ * The magic number is used for consistency check.
+ * XXX the lock is not always needed, and its initialization may be
+ * expensive. Consider making it external.
+ */
+struct __priv_data {
+ ast_mutex_t lock;
+ int ref_counter;
+ ao2_destructor_fn destructor_fn;
+ /*! for stats */
+ size_t data_size;
+ /*! magic number. This is used to verify that a pointer passed in is a
+ * valid astobj2 */
+ uint32_t magic;
+};
+
+#define AO2_MAGIC 0xa570b123
+
+/*!
+ * What an astobj2 object looks like: fixed-size private data
+ * followed by variable-size user data.
+ */
+struct astobj2 {
+ struct __priv_data priv_data;
+ void *user_data[0];
+};
+
+#ifdef AST_DEVMODE
+#define AO2_DEBUG 1
+#endif
+
+#ifdef AO2_DEBUG
+struct ao2_stats {
+ volatile int total_objects;
+ volatile int total_mem;
+ volatile int total_containers;
+ volatile int total_refs;
+ volatile int total_locked;
+};
+
+static struct ao2_stats ao2;
+#endif
+
+#ifndef HAVE_BKTR /* backtrace support */
+void ao2_bt(void) {}
+#else
+#include <execinfo.h> /* for backtrace */
+
+void ao2_bt(void)
+{
+ int c, i;
+#define N1 20
+ void *addresses[N1];
+ char **strings;
+
+ c = backtrace(addresses, N1);
+ strings = backtrace_symbols(addresses,c);
+ ast_verbose("backtrace returned: %d\n", c);
+ for(i = 0; i < c; i++) {
+ ast_verbose("%d: %p %s\n", i, addresses[i], strings[i]);
+ }
+ free(strings);
+}
+#endif
+
+/*!
+ * \brief convert from a pointer _p to a user-defined object
+ *
+ * \return the pointer to the astobj2 structure
+ */
+static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
+{
+ struct astobj2 *p;
+
+ if (!user_data) {
+ ast_log(LOG_ERROR, "user_data is NULL\n");
+ return NULL;
+ }
+
+ p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
+ if (AO2_MAGIC != (p->priv_data.magic) ) {
+ ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
+ p = NULL;
+ }
+
+ return p;
+}
+
+/*!
+ * \brief convert from a pointer _p to an astobj2 object
+ *
+ * \return the pointer to the user-defined portion.
+ */
+#define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
+
+int ao2_lock(void *user_data)
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+
+ if (p == NULL)
+ return -1;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+
+ return ast_mutex_lock(&p->priv_data.lock);
+}
+
+int ao2_unlock(void *user_data)
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+
+ if (p == NULL)
+ return -1;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+#endif
+
+ return ast_mutex_unlock(&p->priv_data.lock);
+}
+
+/*
+ * The argument is a pointer to the user portion.
+ */
+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;
+
+ /* if delta is 0, just return the refcount */
+ if (delta == 0)
+ return (obj->priv_data.ref_counter);
+
+ /* we modify with an atomic operation the reference counter */
+ ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
+ current_value = ret + delta;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_refs, delta);
+#endif
+
+ /* this case must never happen */
+ if (current_value < 0)
+ 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)
+ obj->priv_data.destructor_fn(user_data);
+
+ ast_mutex_destroy(&obj->priv_data.lock);
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
+ ast_atomic_fetchadd_int(&ao2.total_objects, -1);
+#endif
+ /* for safety, zero-out the astobj2 header and also the
+ * first word of the user-data, which we make sure is always
+ * allocated. */
+ bzero(obj, sizeof(struct astobj2 *) + sizeof(void *) );
+ free(obj);
+ }
+
+ return ret;
+}
+
+/*
+ * 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)
+{
+ /* allocation */
+ struct astobj2 *obj;
+
+ if (data_size < sizeof(void *))
+ data_size = sizeof(void *);
+
+ obj = calloc(1, sizeof(*obj) + data_size);
+
+ if (obj == NULL)
+ return NULL;
+
+ ast_mutex_init(&obj->priv_data.lock);
+ obj->priv_data.magic = AO2_MAGIC;
+ obj->priv_data.data_size = data_size;
+ obj->priv_data.ref_counter = 1;
+ obj->priv_data.destructor_fn = destructor_fn; /* can be NULL */
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_objects, 1);
+ ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
+ ast_atomic_fetchadd_int(&ao2.total_refs, 1);
+#endif
+
+ /* return a pointer to the user data */
+ return EXTERNAL_OBJ(obj);
+}
+
+/* internal callback to destroy a container. */
+static void container_destruct(void *c);
+
+/* each bucket in the container is a tailq. */
+AST_LIST_HEAD_NOLOCK(bucket, bucket_list);
+
+/*!
+ * A container; stores the hash and callback functions, information on
+ * the size, the hash bucket heads, and a version number, starting at 0
+ * (for a newly created, empty container)
+ * and incremented every time an object is inserted or deleted.
+ * The assumption is that an object is never moved in a container,
+ * but removed and readded with the new number.
+ * The version number is especially useful when implementing iterators.
+ * In fact, we can associate a unique, monotonically increasing number to
+ * each object, which means that, within an iterator, we can store the
+ * version number of the current object, and easily look for the next one,
+ * which is the next one in the list with a higher number.
+ * Since all objects have a version >0, we can use 0 as a marker for
+ * 'we need the first object in the bucket'.
+ *
+ * \todo Linking and unlink objects is typically expensive, as it
+ * involves a malloc() of a small object which is very inefficient.
+ * To optimize this, we allocate larger arrays of bucket_list's
+ * when we run out of them, and then manage our own freelist.
+ * This will be more efficient as we can do the freelist management while
+ * we hold the lock (that we need anyways).
+ */
+struct ao2_container {
+ ao2_hash_fn hash_fn;
+ ao2_callback_fn cmp_fn;
+ int n_buckets;
+ /*! Number of elements in the container */
+ int elements;
+ /*! described above */
+ int version;
+ /*! variable size */
+ struct bucket buckets[0];
+};
+
+/*!
+ * \brief always zero hash function
+ *
+ * it is convenient to have a hash function that always returns 0.
+ * This is basically used when we want to have a container that is
+ * a simple linked list.
+ *
+ * \returns 0
+ */
+static int hash_zero(const void *user_obj, const int flags)
+{
+ return 0;
+}
+
+/*
+ * 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)
+{
+ /* 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;
+
+ c->version = 1; /* 0 is a reserved value here */
+ c->n_buckets = n_buckets;
+ c->hash_fn = hash_fn ? hash_fn : hash_zero;
+ c->cmp_fn = cmp_fn;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_containers, 1);
+#endif
+
+ return c;
+}
+
+/*!
+ * return the number of elements in the container
+ */
+int ao2_container_count(struct ao2_container *c)
+{
+ return c->elements;
+}
+
+/*!
+ * A structure to create a linked list of entries,
+ * used within a bucket.
+ * XXX \todo this should be private to the container code
+ */
+struct bucket_list {
+ AST_LIST_ENTRY(bucket_list) entry;
+ int version;
+ struct astobj2 *astobj; /* pointer to internal data */
+};
+
+/*
+ * link an object to a container
+ */
+void *__ao2_link(struct ao2_container *c, void *user_data, int iax2_hack)
+{
+ int i;
+ /* create a new list entry */
+ struct bucket_list *p;
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+ if (!obj)
+ return NULL;
+
+ if (INTERNAL_OBJ(c) == NULL)
+ return NULL;
+
+ p = calloc(1, sizeof(*p));
+ if (!p)
+ return NULL;
+
+ i = c->hash_fn(user_data, OBJ_POINTER);
+
+ ao2_lock(c);
+ i %= c->n_buckets;
+ p->astobj = obj;
+ p->version = ast_atomic_fetchadd_int(&c->version, 1);
+ if (iax2_hack)
+ AST_LIST_INSERT_HEAD(&c->buckets[i], p, entry);
+ else
+ AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
+ ast_atomic_fetchadd_int(&c->elements, 1);
+ ao2_ref(user_data, +1);
+ ao2_unlock(c);
+
+ return p;
+}
+
+/*!
+ * \brief another convenience function is a callback that matches on address
+ */
+int ao2_match_by_addr(void *user_data, void *arg, int flags)
+{
+ return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
+}
+
+/*
+ * Unlink an object from the container
+ * and destroy the associated * ao2_bucket_list structure.
+ */
+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;
+}
+
+/*!
+ * \brief special callback that matches all
+ */
+static int cb_true(void *user_data, void *arg, int flags)
+{
+ return CMP_MATCH;
+}
+
+/*!
+ * 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.
+ */
+void *ao2_callback(struct ao2_container *c,
+ const enum search_flags flags,
+ ao2_callback_fn cb_fn, void *arg)
+{
+ int i, last; /* search boundaries */
+ void *ret = NULL;
+
+ if (INTERNAL_OBJ(c) == NULL) /* safety check on the argument */
+ return NULL;
+
+ if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
+ ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
+ return NULL;
+ }
+
+ /* override the match function if necessary */
+#if 0
+ /* Removing this slightly changes the meaning of OBJ_POINTER, but makes it
+ * do what I want it to. I'd like to hint to ao2_callback that the arg is
+ * of the same object type, so it can be passed to the hash function.
+ * However, I don't want to imply that this is the object being searched for. */
+ if (flags & OBJ_POINTER)
+ cb_fn = match_by_addr;
+ else
+#endif
+ if (cb_fn == NULL) /* if NULL, match everything */
+ cb_fn = cb_true;
+ /*
+ * XXX this can be optimized.
+ * If we have a hash function and lookup by pointer,
+ * run the hash function. Otherwise, scan the whole container
+ * (this only for the time being. We need to optimize this.)
+ */
+ if ((flags & OBJ_POINTER)) /* we know hash can handle this case */
+ i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
+ else /* don't know, let's scan all buckets */
+ i = -1; /* XXX this must be fixed later. */
+
+ /* determine the search boundaries: i..last-1 */
+ if (i < 0) {
+ i = 0;
+ last = c->n_buckets;
+ } else {
+ last = i + 1;
+ }
+
+ ao2_lock(c); /* avoid modifications to the content */
+
+ for (; i < last ; i++) {
+ /* scan the list with prev-cur pointers */
+ struct bucket_list *cur;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
+ int match = cb_fn(EXTERNAL_OBJ(cur->astobj), arg, flags) & (CMP_MATCH | CMP_STOP);
+
+ /* we found the object, performing operations according flags */
+ if (match == 0) { /* no match, no stop, continue */
+ continue;
+ } else if (match == CMP_STOP) { /* no match but stop, we are done */
+ i = last;
+ break;
+ }
+ /* we have a match (CMP_MATCH) here */
+ 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 (flags & OBJ_UNLINK) { /* must unlink */
+ struct bucket_list *x = cur;
+
+ /* we are going to modify the container, so update version */
+ ast_atomic_fetchadd_int(&c->version, 1);
+ AST_LIST_REMOVE_CURRENT(&c->buckets[i], entry);
+ /* update number of elements and version */
+ ast_atomic_fetchadd_int(&c->elements, -1);
+ ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
+ free(x); /* free the link record */
+ }
+
+ if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
+ /* We found the only match we need */
+ i = last; /* force exit from outer loop */
+ break;
+ }
+ if (!(flags & OBJ_NODATA)) {
+#if 0 /* XXX to be completed */
+ /*
+ * This is the multiple-return case. We need to link
+ * the object in a list. The refcount is already increased.
+ */
+#endif
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ }
+ ao2_unlock(c);
+ return ret;
+}
+
+/*!
+ * 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)
+{
+ return ao2_callback(c, flags, c->cmp_fn, arg);
+}
+
+/*!
+ * initialize an iterator so we start from the first object
+ */
+struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
+{
+ struct ao2_iterator a = {
+ .c = c,
+ .flags = flags
+ };
+
+ return a;
+}
+
+/*
+ * move to the next element in the container.
+ */
+void * ao2_iterator_next(struct ao2_iterator *a)
+{
+ int lim;
+ struct bucket_list *p = NULL;
+ void *ret = NULL;
+
+ if (INTERNAL_OBJ(a->c) == NULL)
+ return NULL;
+
+ if (!(a->flags & F_AO2I_DONTLOCK))
+ ao2_lock(a->c);
+
+ /* optimization. If the container is unchanged and
+ * we have a pointer, try follow it
+ */
+ if (a->c->version == a->c_version && (p = a->obj) ) {
+ if ( (p = AST_LIST_NEXT(p, entry)) )
+ goto found;
+ /* nope, start from the next bucket */
+ a->bucket++;
+ a->version = 0;
+ a->obj = NULL;
+ }
+
+ lim = a->c->n_buckets;
+
+ /* Browse the buckets array, moving to the next
+ * buckets if we don't find the entry in the current one.
+ * Stop when we find an element with version number greater
+ * than the current one (we reset the version to 0 when we
+ * switch buckets).
+ */
+ for (; a->bucket < lim; a->bucket++, a->version = 0) {
+ /* scan the current bucket */
+ AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
+ if (p->version > a->version)
+ goto found;
+ }
+ }
+
+found:
+ if (p) {
+ a->version = p->version;
+ a->obj = p;
+ a->c_version = a->c->version;
+ ret = EXTERNAL_OBJ(p->astobj);
+ /* inc refcount of returned object */
+ ao2_ref(ret, 1);
+ }
+
+ if (!(a->flags & F_AO2I_DONTLOCK))
+ ao2_unlock(a->c);
+
+ return ret;
+}
+
+/* callback for destroying container.
+ * we can make it simple as we know what it does
+ */
+static int cd_cb(void *obj, void *arg, int flag)
+{
+ ao2_ref(obj, -1);
+ return 0;
+}
+
+static void container_destruct(void *_c)
+{
+ struct ao2_container *c = _c;
+
+ ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_containers, -1);
+#endif
+}
+
+#ifdef AO2_DEBUG
+static int print_cb(void *obj, void *arg, int flag)
+{
+ int *fd = arg;
+ char *s = (char *)obj;
+
+ ast_cli(*fd, "string <%s>\n", s);
+ return 0;
+}
+
+/*
+ * Print stats
+ */
+static int handle_astobj2_stats(int fd, int argc, char *argv[])
+{
+ ast_cli(fd, "Objects : %d\n", ao2.total_objects);
+ ast_cli(fd, "Containers : %d\n", ao2.total_containers);
+ ast_cli(fd, "Memory : %d\n", ao2.total_mem);
+ ast_cli(fd, "Locked : %d\n", ao2.total_locked);
+ ast_cli(fd, "Refs : %d\n", ao2.total_refs);
+ return 0;
+}
+
+/*
+ * This is testing code for astobj
+ */
+static int handle_astobj2_test(int fd, int argc, char *argv[])
+{
+ struct ao2_container *c1;
+ int i, lim;
+ char *obj;
+ static int prof_id = -1;
+
+ if (prof_id == -1)
+ prof_id = ast_add_profile("ao2_alloc", 0);
+
+ ast_cli(fd, "argc %d argv %s %s %s\n", argc, argv[0], argv[1], argv[2]);
+ lim = atoi(argv[2]);
+ ast_cli(fd, "called astobj_test\n");
+
+ handle_astobj2_stats(fd, 0, NULL);
+ /*
+ * 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 */);
+ ast_cli(fd, "container allocated as %p\n", c1);
+
+ /*
+ * fill the container with objects.
+ * ao2_alloc() gives us a reference which we pass to the
+ * container when we do the insert.
+ */
+ for (i = 0; i < lim; i++) {
+ ast_mark(prof_id, 1 /* start */);
+ obj = ao2_alloc(80, NULL);
+ ast_mark(prof_id, 0 /* stop */);
+ ast_cli(fd, "object %d allocated as %p\n", i, obj);
+ sprintf(obj, "-- this is obj %d --", i);
+ ao2_link(c1, obj);
+ }
+ ast_cli(fd, "testing callbacks\n");
+ ao2_callback(c1, 0, print_cb, &fd);
+
+ ast_cli(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)) ) {
+ ast_cli(fd, "iterator on <%s>\n", obj);
+ if (x++ & 1)
+ ao2_unlink(c1, obj);
+ ao2_ref(obj, -1);
+ }
+ ast_cli(fd, "testing iterators again\n");
+ ai = ao2_iterator_init(c1, 0);
+ while ( (obj = ao2_iterator_next(&ai)) ) {
+ ast_cli(fd, "iterator on <%s>\n", obj);
+ ao2_ref(obj, -1);
+ }
+ }
+ ast_cli(fd, "testing callbacks again\n");
+ ao2_callback(c1, 0, print_cb, &fd);
+
+ 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 */
+
+ ast_cli(fd, "destroy container\n");
+ ao2_ref(c1, -1); /* destroy container */
+ handle_astobj2_stats(fd, 0, NULL);
+ return 0;
+}
+
+static struct ast_cli_entry cli_astobj2[] = {
+ { { "astobj2", "stats", NULL },
+ handle_astobj2_stats, "Print astobj2 statistics", },
+ { { "astobj2", "test", NULL } , handle_astobj2_test, "Test astobj2", },
+};
+#endif /* AO2_DEBUG */
+
+int astobj2_init(void)
+{
+#ifdef AO2_DEBUG
+ ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
+#endif
+
+ return 0;
+}
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 8d031f7ea..454c4ee86 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -89,6 +89,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/dnsmgr.h"
#include "asterisk/devicestate.h"
#include "asterisk/netsock.h"
+#include "asterisk/dlinkedlists.h"
+#include "asterisk/astobj2.h"
#include "iax2.h"
#include "iax2-parser.h"
@@ -611,6 +613,8 @@ struct chan_iax2_pvt {
int frames_dropped;
/*! received frame count: (just for stats) */
int frames_received;
+
+ AST_DLLIST_ENTRY(chan_iax2_pvt) entry;
};
static struct ast_iax2_queue {
@@ -736,6 +740,16 @@ static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS];
static ast_mutex_t iaxsl[IAX_MAX_CALLS];
static struct timeval lastused[IAX_MAX_CALLS];
+/*!
+ * \brief Another container of iax2_pvt structures
+ *
+ * Active IAX2 pvt structs are also stored in this container, if they are a part
+ * of an active call where we know the remote side's call number. The reason
+ * for this is that incoming media frames do not contain our call number. So,
+ * instead of having to iterate the entire iaxs array, we use this container to
+ * look up calls where the remote side is using a given call number.
+ */
+static struct ao2_container *iax_peercallno_pvts;
static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
static int send_command_locked(unsigned short callno, char, int, unsigned int, const unsigned char *, int, int);
@@ -913,17 +927,188 @@ static int iax2_getpeername(struct sockaddr_in sin, char *host, int len, int loc
return res;
}
+static void update_max_trunk(void)
+{
+ int max = TRUNK_CALL_START;
+ int x;
+ /* XXX Prolly don't need locks here XXX */
+ for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
+ if (iaxs[x])
+ max = x + 1;
+ }
+ maxtrunkcall = max;
+ if (option_debug && iaxdebug)
+ ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max);
+}
+
+static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
+{
+ if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) {
+ struct iax2_user *user;
+
+ ast_mutex_lock(&userl.lock);
+ user = userl.users;
+ while (user) {
+ if (!strcmp(user->name, pvt->username)) {
+ user->curauthreq--;
+ break;
+ }
+ user = user->next;
+ }
+ ast_mutex_unlock(&userl.lock);
+ }
+
+ /* No more pings or lagrq's */
+ if (pvt->pingid > -1)
+ ast_sched_del(sched, pvt->pingid);
+ if (pvt->lagid > -1)
+ ast_sched_del(sched, pvt->lagid);
+ if (pvt->autoid > -1)
+ ast_sched_del(sched, pvt->autoid);
+ if (pvt->authid > -1)
+ ast_sched_del(sched, pvt->authid);
+ if (pvt->initid > -1)
+ ast_sched_del(sched, pvt->initid);
+#ifdef NEWJB
+ if (pvt->jbid > -1)
+ ast_sched_del(sched, pvt->jbid);
+ pvt->jbid = -1;
+#endif
+ pvt->pingid = -1;
+ pvt->lagid = -1;
+ pvt->autoid = -1;
+ pvt->authid = -1;
+ pvt->initid = -1;
+}
+
+static void store_by_peercallno(struct chan_iax2_pvt *pvt)
+{
+ if (!pvt->peercallno) {
+ ast_log(LOG_ERROR, "This should not be called without a peer call number.\n");
+ return;
+ }
+
+ ao2_link(iax_peercallno_pvts, pvt);
+}
+
+static void remove_by_peercallno(struct chan_iax2_pvt *pvt)
+{
+ if (!pvt->peercallno) {
+ ast_log(LOG_ERROR, "This should not be called without a peer call number.\n");
+ return;
+ }
+
+ ao2_unlink(iax_peercallno_pvts, pvt);
+}
+
+
+
+static void iax2_destroy(int callno)
+{
+ struct chan_iax2_pvt *pvt;
+ struct ast_channel *owner;
+
+retry:
+ ast_mutex_lock(&iaxsl[callno]);
+ pvt = iaxs[callno];
+ gettimeofday(&lastused[callno], NULL);
+
+ if (pvt)
+ owner = pvt->owner;
+ else
+ owner = NULL;
+ if (owner) {
+ if (ast_mutex_trylock(&owner->lock)) {
+ ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n");
+ ast_mutex_unlock(&iaxsl[callno]);
+ usleep(1);
+ goto retry;
+ }
+ }
+ if (!owner)
+ iaxs[callno] = NULL;
+ if (pvt) {
+ if (owner) {
+ /* If there's an owner, prod it to give up */
+ owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_queue_hangup(owner);
+ } else {
+ pvt->owner = NULL;
+ }
+
+ if (pvt->peercallno) {
+ remove_by_peercallno(pvt);
+ }
+
+ if (!owner) {
+ ao2_ref(pvt, -1);
+ pvt = NULL;
+ }
+ }
+
+ if (owner) {
+ ast_mutex_unlock(&owner->lock);
+ }
+
+ ast_mutex_unlock(&iaxsl[callno]);
+
+ if (callno & 0x4000) {
+ update_max_trunk();
+ }
+}
+
+static void iax2_frame_free(struct iax_frame *fr)
+{
+ if (fr->retrans > -1)
+ ast_sched_del(sched, fr->retrans);
+ iax_frame_free(fr);
+}
+
+static void pvt_destructor(void *obj)
+{
+ struct chan_iax2_pvt *pvt = obj;
+ struct iax_frame *cur;
+
+ iax2_destroy_helper(pvt);
+
+ if (pvt->bridgetrans)
+ ast_translator_free_path(pvt->bridgetrans);
+ pvt->bridgetrans = NULL;
+
+ /* Already gone */
+ ast_set_flag(pvt, IAX_ALREADYGONE);
+
+ for (cur = iaxq.head; cur ; cur = cur->next) {
+ /* Cancel any pending transmissions */
+ if (cur->callno == pvt->callno)
+ cur->retries = -1;
+ }
+ if (pvt->reg) {
+ pvt->reg->callno = 0;
+ }
+ if (!pvt->owner) {
+ if (pvt->vars) {
+ ast_variables_destroy(pvt->vars);
+ pvt->vars = NULL;
+ }
+#ifdef NEWJB
+ {
+ jb_frame frame;
+ while (jb_getall(pvt->jb,&frame) == JB_OK)
+ iax2_frame_free(frame.data);
+ jb_destroy(pvt->jb);
+ }
+#endif
+ }
+}
+
static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, const char *host)
{
struct chan_iax2_pvt *tmp;
- tmp = malloc(sizeof(struct chan_iax2_pvt));
+
+ tmp = ao2_alloc(sizeof(*tmp), pvt_destructor);
if (tmp) {
- memset(tmp, 0, sizeof(struct chan_iax2_pvt));
tmp->prefs = prefs;
- tmp->callno = 0;
- tmp->peercallno = 0;
- tmp->transfercallno = 0;
- tmp->bridgecallno = 0;
tmp->pingid = -1;
tmp->lagid = -1;
tmp->autoid = -1;
@@ -987,20 +1172,6 @@ static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short
return 0;
}
-static void update_max_trunk(void)
-{
- int max = TRUNK_CALL_START;
- int x;
- /* XXX Prolly don't need locks here XXX */
- for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
- if (iaxs[x])
- max = x + 1;
- }
- maxtrunkcall = max;
- if (option_debug && iaxdebug)
- ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max);
-}
-
static void update_max_nontrunk(void)
{
int max = 1;
@@ -1070,6 +1241,25 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
char iabuf[INET_ADDRSTRLEN];
char host[80];
if (new <= NEW_ALLOW) {
+ if (callno) {
+ struct chan_iax2_pvt *pvt;
+ struct chan_iax2_pvt tmp_pvt = {
+ .callno = dcallno,
+ .peercallno = callno,
+ /* hack!! */
+ .frames_received = full_frame,
+ };
+
+ memcpy(&tmp_pvt.addr, sin, sizeof(tmp_pvt.addr));
+
+ if ((pvt = ao2_find(iax_peercallno_pvts, &tmp_pvt, OBJ_POINTER))) {
+ res = pvt->callno;
+ ao2_ref(pvt, -1);
+ pvt = NULL;
+ return res;
+ }
+ }
+
/* Look for an existing connection first */
for (x=1;(res < 1) && (x<maxnontrunkcall);x++) {
ast_mutex_lock(&iaxsl[x]);
@@ -1148,6 +1338,9 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
iaxs[x]->amaflags = amaflags;
ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
ast_copy_string(iaxs[x]->accountcode, accountcode, sizeof(iaxs[x]->accountcode));
+ if (iaxs[x]->peercallno) {
+ store_by_peercallno(iaxs[x]);
+ }
} else {
ast_log(LOG_WARNING, "Out of resources\n");
ast_mutex_unlock(&iaxsl[x]);
@@ -1159,13 +1352,6 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc
return res;
}
-static void iax2_frame_free(struct iax_frame *fr)
-{
- if (fr->retrans > -1)
- ast_sched_del(sched, fr->retrans);
- iax_frame_free(fr);
-}
-
static int iax2_queue_frame(int callno, struct ast_frame *f)
{
/* Assumes lock for callno is already held... */
@@ -1639,112 +1825,7 @@ static int iax2_predestroy_nolock(int callno)
return res;
}
-static void iax2_destroy(int callno)
-{
- struct chan_iax2_pvt *pvt;
- struct iax_frame *cur;
- struct ast_channel *owner;
- struct iax2_user *user;
-retry:
- ast_mutex_lock(&iaxsl[callno]);
- pvt = iaxs[callno];
- gettimeofday(&lastused[callno], NULL);
-
- if (pvt)
- owner = pvt->owner;
- else
- owner = NULL;
- if (owner) {
- if (ast_mutex_trylock(&owner->lock)) {
- ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n");
- ast_mutex_unlock(&iaxsl[callno]);
- usleep(1);
- goto retry;
- }
- }
- if (!owner)
- iaxs[callno] = NULL;
- if (pvt) {
- if (!owner)
- pvt->owner = NULL;
- if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) {
- ast_mutex_lock(&userl.lock);
- user = userl.users;
- while (user) {
- if (!strcmp(user->name, pvt->username)) {
- user->curauthreq--;
- break;
- }
- user = user->next;
- }
- ast_mutex_unlock(&userl.lock);
- }
- /* No more pings or lagrq's */
- if (pvt->pingid > -1)
- ast_sched_del(sched, pvt->pingid);
- if (pvt->lagid > -1)
- ast_sched_del(sched, pvt->lagid);
- if (pvt->autoid > -1)
- ast_sched_del(sched, pvt->autoid);
- if (pvt->authid > -1)
- ast_sched_del(sched, pvt->authid);
- if (pvt->initid > -1)
- ast_sched_del(sched, pvt->initid);
-#ifdef NEWJB
- if (pvt->jbid > -1)
- ast_sched_del(sched, pvt->jbid);
- pvt->jbid = -1;
-#endif
- pvt->pingid = -1;
- pvt->lagid = -1;
- pvt->autoid = -1;
- pvt->authid = -1;
- pvt->initid = -1;
- if (pvt->bridgetrans)
- ast_translator_free_path(pvt->bridgetrans);
- pvt->bridgetrans = NULL;
-
- /* Already gone */
- ast_set_flag(pvt, IAX_ALREADYGONE);
-
- if (owner) {
- /* If there's an owner, prod it to give up */
- owner->_softhangup |= AST_SOFTHANGUP_DEV;
- ast_queue_hangup(owner);
- }
-
- for (cur = iaxq.head; cur ; cur = cur->next) {
- /* Cancel any pending transmissions */
- if (cur->callno == pvt->callno)
- cur->retries = -1;
- }
- if (pvt->reg) {
- pvt->reg->callno = 0;
- }
- if (!owner) {
- if (pvt->vars) {
- ast_variables_destroy(pvt->vars);
- pvt->vars = NULL;
- }
-#ifdef NEWJB
- {
- jb_frame frame;
- while(jb_getall(pvt->jb,&frame) == JB_OK)
- iax2_frame_free(frame.data);
- jb_destroy(pvt->jb);
- }
-#endif
- free(pvt);
- }
- }
- if (owner) {
- ast_mutex_unlock(&owner->lock);
- }
- ast_mutex_unlock(&iaxsl[callno]);
- if (callno & 0x4000)
- update_max_trunk();
-}
static void iax2_destroy_nolock(int callno)
{
/* Actually it's easier to unlock, kill it, and relock */
@@ -5578,7 +5659,13 @@ static int complete_transfer(int callno, struct iax_ies *ies)
pvt->rseqno = 0;
pvt->iseqno = 0;
pvt->aseqno = 0;
+
+ if (pvt->peercallno) {
+ remove_by_peercallno(pvt);
+ }
pvt->peercallno = peercallno;
+ store_by_peercallno(pvt);
+
pvt->transferring = TRANSFER_NONE;
pvt->svoiceformat = -1;
pvt->voiceformat = 0;
@@ -6738,8 +6825,18 @@ static int socket_read(int *id, int fd, short events, void *cbdata)
if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) && !minivid &&
f.subclass != IAX_COMMAND_TXCNT && /* for attended transfer */
- f.subclass != IAX_COMMAND_TXACC) /* for attended transfer */
- iaxs[fr->callno]->peercallno = (unsigned short)(ntohs(mh->callno) & ~IAX_FLAG_FULL);
+ f.subclass != IAX_COMMAND_TXACC) { /* for attended transfer */
+ unsigned short new_peercallno;
+
+ new_peercallno = (unsigned short) (ntohs(mh->callno) & ~IAX_FLAG_FULL);
+ if (new_peercallno && new_peercallno != iaxs[fr->callno]->peercallno) {
+ if (iaxs[fr->callno]->peercallno) {
+ remove_by_peercallno(iaxs[fr->callno]);
+ }
+ iaxs[fr->callno]->peercallno = new_peercallno;
+ store_by_peercallno(iaxs[fr->callno]);
+ }
+ }
if (ntohs(mh->callno) & IAX_FLAG_FULL) {
if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "Received packet %d, (%d, %d)\n", fh->oseqno, f.frametype, f.subclass);
@@ -9858,6 +9955,8 @@ static int __unload_module(void)
for (x = 0; x < IAX_MAX_CALLS; x++)
ast_mutex_destroy(&iaxsl[x]);
+ ao2_ref(iax_peercallno_pvts, -1);
+
return 0;
}
@@ -9867,6 +9966,23 @@ int unload_module()
return __unload_module();
}
+static int pvt_hash_cb(const void *obj, const int flags)
+{
+ const struct chan_iax2_pvt *pvt = obj;
+
+ return pvt->peercallno;
+}
+
+static int pvt_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct chan_iax2_pvt *pvt = obj, *pvt2 = arg;
+
+ /* The frames_received field is used to hold whether we're matching
+ * against a full frame or not ... */
+
+ return match(&pvt2->addr, pvt2->peercallno, pvt2->callno, pvt,
+ pvt2->frames_received) ? CMP_MATCH : 0;
+}
/*--- load_module: Load IAX2 module, load configuraiton ---*/
int load_module(void)
@@ -9955,6 +10071,11 @@ int load_module(void)
ast_netsock_release(outsock);
}
+ iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb);
+ if (!iax_peercallno_pvts) {
+ res = -1;
+ }
+
ast_mutex_lock(&reg_lock);
for (reg = registrations; reg; reg = reg->next)
iax2_do_register(reg);
diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h
new file mode 100644
index 000000000..bcb8addda
--- /dev/null
+++ b/include/asterisk/astobj2.h
@@ -0,0 +1,541 @@
+/*
+ * astobj2 - replacement containers for asterisk data structures.
+ *
+ * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#ifndef _ASTERISK_ASTOBJ2_H
+#define _ASTERISK_ASTOBJ2_H
+
+/*! \file
+ *
+ * \brief Object Model implementing objects and containers.
+
+These functions implement an abstraction for objects (with
+locks and reference counts) and containers for these user-defined objects,
+supporting locking, reference counting and callbacks.
+
+The internal implementation of the container is opaque to the user,
+so we can use different data structures as needs arise.
+
+At the moment, however, the only internal data structure is a hash
+table. When other structures will be implemented, the initialization
+function may change.
+
+USAGE - OBJECTS
+
+An object is a block of memory that must be allocated with the
+function ao2_alloc(), and for which the system keeps track (with
+abit of help from the programmer) of the number of references around.
+When an object has no more references, it is destroyed, by first
+invoking whatever 'destructor' function the programmer specifies
+(it can be NULL), and then freeing the memory.
+This way objects can be shared without worrying who is in charge
+of freeing them.
+
+Basically, creating an object requires the size of the object and
+and a pointer to the destructor function:
+
+ struct foo *o;
+
+ o = ao2_alloc(sizeof(struct foo), my_destructor_fn);
+
+The object returned has a refcount = 1.
+Note that the memory for the object is allocated and zeroed.
+- We cannot realloc() the object itself.
+- We cannot call free(o) to dispose of the object; rather we
+ tell the system that we do not need the reference anymore:
+
+ ao2_ref(o, -1)
+
+ causing the destructor to be called (and then memory freed) when
+ the refcount goes to 0. This is also available as ao2_unref(o),
+ and returns NULL as a convenience, so you can do things like
+ o = ao2_unref(o);
+ and clean the original pointer to prevent errors.
+
+- ao2_ref(o, +1) can be used to modify the refcount on the
+ object in case we want to pass it around.
+
+
+- other calls on the object are ao2_lock(obj), ao2_unlock(),
+ ao2_trylock(), to manipulate the lock.
+
+
+USAGE - CONTAINERS
+
+A containers is an abstract data structure where we can store
+objects, search them (hopefully in an efficient way), and iterate
+or apply a callback function to them. A container is just an object
+itself.
+
+A container must first be allocated, specifying the initial
+parameters. At the moment, this is done as follows:
+
+ <b>Sample Usage:</b>
+ \code
+
+ struct ao2_container *c;
+
+ c = ao2_container_alloc(MAX_BUCKETS, my_hash_fn, my_cmp_fn);
+
+where
+- MAX_BUCKETS is the number of buckets in the hash table,
+- my_hash_fn() is the (user-supplied) function that returns a
+ hash key for the object (further reduced moduly MAX_BUCKETS
+ by the container's code);
+- my_cmp_fn() is the default comparison function used when doing
+ searches on the container,
+
+A container knows little or nothing about the object itself,
+other than the fact that it has been created by ao2_alloc()
+All knowledge of the (user-defined) internals of the object
+is left to the (user-supplied) functions passed as arguments
+to ao2_container_alloc().
+
+If we want to insert the object in the container, we should
+initialize its fields -- especially, those used by my_hash_fn() --
+to compute the bucket to use.
+Once done, we can link an object to a container with
+
+ ao2_link(c, o);
+
+The function returns NULL in case of errors (and the object
+is not inserted in the container). Other values mean success
+(we are not supposed to use the value as a pointer to anything).
+
+\note While an object o is in a container, we expect that
+my_hash_fn(o) will always return the same value. The function
+does not lock the object to be computed, so modifications of
+those fields that affect the computation of the hash should
+be done by extractiong the object from the container, and
+reinserting it after the change (this is not terribly expensive).
+
+\note A container with a single buckets is effectively a linked
+list. However there is no ordering among elements.
+
+Objects implement a reference counter keeping the count
+of the number of references that reference an object.
+
+When this number becomes zero the destructor will be
+called and the object will be free'd.
+ */
+
+/*!
+ * Invoked just before freeing the memory for the object.
+ * It is passed a pointer to user data.
+ */
+typedef void (*ao2_destructor_fn)(void *);
+
+void ao2_bt(void); /* backtrace */
+/*!
+ * Allocate and initialize an object.
+ *
+ * \param data_size The sizeof() of user-defined structure.
+ * \param destructor_fn The function destructor (can be NULL)
+ * \return A pointer to user data.
+ *
+ * Allocates a struct astobj2 with sufficient space for the
+ * user-defined structure.
+ * \notes:
+ * - storage is zeroed; XXX maybe we want a flag to enable/disable this.
+ * - the refcount of the object just created is 1
+ * - the returned pointer cannot be free()'d or realloc()'ed;
+ * rather, we just call ao2_ref(o, -1);
+ */
+void *ao2_alloc(const size_t data_size, ao2_destructor_fn destructor_fn);
+
+/*!
+ * Reference/unreference an object and return the old refcount.
+ *
+ * \param o A pointer to the object
+ * \param delta Value to add to the reference counter.
+ * \return The value of the reference counter before the operation.
+ *
+ * Increase/decrease the reference counter according
+ * the value of delta.
+ *
+ * If the refcount goes to zero, the object is destroyed.
+ *
+ * \note The object must not be locked by the caller of this function, as
+ * it is invalid to try to unlock it after releasing the reference.
+ *
+ * \note if we know the pointer to an object, it is because we
+ * have a reference count to it, so the only case when the object
+ * can go away is when we release our reference, and it is
+ * the last one in existence.
+ */
+int ao2_ref(void *o, int delta);
+
+/*!
+ * Lock an object.
+ *
+ * \param a A pointer to the object we want lock.
+ * \return 0 on success, other values on error.
+ */
+int ao2_lock(void *a);
+
+/*!
+ * Unlock an object.
+ *
+ * \param a A pointer to the object we want unlock.
+ * \return 0 on success, other values on error.
+ */
+int ao2_unlock(void *a);
+
+/*!
+ *
+ * Containers
+
+containers are data structures meant to store several objects,
+and perform various operations on them.
+Internally, objects are stored in lists, hash tables or other
+data structures depending on the needs.
+
+NOTA BENE: at the moment the only container we support is the
+hash table and its degenerate form, the list.
+
+Operations on container include:
+
+ c = ao2_container_alloc(size, cmp_fn, hash_fn)
+ allocate a container with desired size and default compare
+ and hash function
+
+ ao2_find(c, arg, flags)
+ returns zero or more element matching a given criteria
+ (specified as arg). Flags indicate how many results we
+ want (only one or all matching entries), and whether we
+ should unlink the object from the container.
+
+ ao2_callback(c, flags, fn, arg)
+ apply fn(obj, arg) to all objects in the container.
+ Similar to find. fn() can tell when to stop, and
+ do anything with the object including unlinking it.
+ Note that the entire operation is run with the container
+ locked, so noone else can change its content while we work on it.
+ However, we pay this with the fact that doing
+ anything blocking in the callback keeps the container
+ blocked.
+ The mechanism is very flexible because the callback function fn()
+ can do basically anything e.g. counting, deleting records, etc.
+ possibly using arg to store the results.
+
+ iterate on a container
+ this is done with the following sequence
+
+ struct ao2_container *c = ... // our container
+ struct ao2_iterator i;
+ void *o;
+
+ i = ao2_iterator_init(c, flags);
+
+ while ( (o = ao2_iterator_next(&i)) ) {
+ ... do something on o ...
+ ao2_ref(o, -1);
+ }
+
+ The difference with the callback is that the control
+ on how to iterate is left to us.
+
+ ao2_ref(c, -1)
+ dropping a reference to a container destroys it, very simple!
+
+Containers are astobj2 object themselves, and this is why their
+implementation is simple too.
+
+ */
+
+/*!
+ * We can perform different operation on an object. We do this
+ * according the following flags.
+ */
+enum search_flags {
+ /*! unlink the object found */
+ OBJ_UNLINK = (1 << 0),
+ /*! on match, don't return the object or increase its reference count. */
+ OBJ_NODATA = (1 << 1),
+ /*! don't stop at the first match
+ * \note This is not fully implemented. */
+ OBJ_MULTIPLE = (1 << 2),
+ /*! obj is an object of the same type as the one being searched for.
+ * This implies that it can be passed to the object's hash function
+ * for optimized searching. */
+ OBJ_POINTER = (1 << 3),
+};
+
+/*!
+ * Type of a generic function to generate a hash value from an object.
+ *
+ */
+typedef int (*ao2_hash_fn)(const void *obj, const int flags);
+
+/*!
+ * valid callback results:
+ * We return a combination of
+ * CMP_MATCH when the object matches the request,
+ * and CMP_STOP when we should not continue the search further.
+ */
+enum _cb_results {
+ CMP_MATCH = 0x1,
+ CMP_STOP = 0x2,
+};
+
+/*!
+ * generic function to compare objects.
+ * This, as other callbacks, should return a combination of
+ * _cb_results as described above.
+ *
+ * \param o object from container
+ * \param arg search parameters (directly from ao2_find)
+ * \param flags passed directly from ao2_find
+ * XXX explain.
+ */
+
+/*!
+ * Type of a generic callback function
+ * \param obj pointer to the (user-defined part) of an object.
+ * \param arg callback argument from ao2_callback()
+ * \param flags flags from ao2_callback()
+ * The return values are the same as a compare function.
+ * In fact, they are the same thing.
+ */
+typedef int (*ao2_callback_fn)(void *obj, void *arg, int flags);
+
+/*!
+ * Here start declarations of containers.
+ */
+struct ao2_container;
+
+/*!
+ * Allocate and initialize a container
+ * with the desired number of buckets.
+ *
+ * We allocate space for a struct astobj_container, struct container
+ * and the buckets[] array.
+ *
+ * \param my_hash_fn Pointer to a function computing a hash value.
+ * \param my_cmp_fn Pointer to a function comparating key-value
+ * with a string. (can be NULL)
+ * \return A pointer to a struct container.
+ *
+ * destructor is set implicitly.
+ */
+struct ao2_container *ao2_container_alloc(const uint n_buckets,
+ ao2_hash_fn hash_fn, ao2_callback_fn cmp_fn);
+
+/*!
+ * Returns the number of elements in a container.
+ */
+int ao2_container_count(struct ao2_container *c);
+
+/*
+ * Here we have functions to manage objects.
+ *
+ * We can use the functions below on any kind of
+ * object defined by the user.
+ */
+
+/*!
+ * \brief Add an object to a container.
+ *
+ * \param c the container to operate on.
+ * \param newobj the object to be added.
+ *
+ * \return NULL on errors, other values on success.
+ *
+ * This function inserts an object in a container according its key.
+ *
+ * \note Remember to set the key before calling this function.
+ *
+ * \note This function automatically increases the reference count to
+ * account for the reference to the object that the container now holds.
+ *
+ * For Asterisk 1.4 only, there is a dirty hack here to ensure that chan_iax2
+ * can have objects linked in to the container at the head instead of tail
+ * when it is just a linked list. This is to maintain some existing behavior
+ * where the order must be maintained as it was before this conversion so that
+ * matching behavior doesn't change.
+ */
+#define ao2_link(c, o) __ao2_link(c, o, 0)
+void *__ao2_link(struct ao2_container *c, void *newobj, int iax2_hack);
+
+/*!
+ * \brief Remove an object from the container
+ *
+ * \arg c the container
+ * \arg obj the object to unlink
+ *
+ * \retval NULL, always
+ *
+ * \note The object requested to be unlinked must be valid. However, if it turns
+ * out that it is not in the container, this function is still safe to
+ * be called.
+ *
+ * \note If the object gets unlinked from the container, the container's
+ * reference to the object will be automatically released.
+ */
+void *ao2_unlink(struct ao2_container *c, void *obj);
+
+/*! \struct Used as return value if the flag OBJ_MULTIPLE is set */
+struct ao2_list {
+ struct ao2_list *next;
+ void *obj; /* pointer to the user portion of the object */
+};
+
+/*!
+ * ao2_callback() and astob2_find() are the same thing with only one difference:
+ * the latter uses as a callback the function passed as my_cmp_f() at
+ * the time of the creation of the container.
+ *
+ * \param c A pointer to the container to operate on.
+ * \param arg passed to the callback.
+ * \param flags A set of flags specifying the operation to perform,
+ partially used by the container code, but also passed to
+ the callback.
+ * \return A pointer to the object found/marked,
+ * a pointer to a list of objects matching comparison function,
+ * NULL if not found.
+ * If the function returns any objects, their refcount is incremented,
+ * and the caller is in charge of decrementing them once done.
+ * Also, in case of multiple values returned, the list used
+ * to store the objects must be freed by the caller.
+ *
+ * This function searches through a container and performs operations
+ * on objects according on flags passed.
+ * XXX describe better
+ * The comparison is done calling the compare function set implicitly.
+ * The p pointer can be a pointer to an object or to a key,
+ * we can say this looking at flags value.
+ * If p points to an object we will search for the object pointed
+ * by this value, otherwise we serch for a key value.
+ * If the key is not uniq we only find the first matching valued.
+ * If we use the OBJ_MARK flags, we mark all the objects matching
+ * the condition.
+ *
+ * The use of flags argument is the follow:
+ *
+ * OBJ_UNLINK unlinks the object found
+ * OBJ_NODATA on match, do return an object
+ * Callbacks use OBJ_NODATA as a default
+ * functions such as find() do
+ * OBJ_MULTIPLE return multiple matches
+ * Default for _find() is no.
+ * to a key (not yet supported)
+ * OBJ_POINTER the pointer is an object pointer
+ *
+ * In case we return a list, the callee must take care to destroy
+ * that list when no longer used.
+ *
+ * \note When the returned object is no longer in use, ao2_ref() should
+ * be used to free the additional reference possibly created by this function.
+ */
+/* XXX order of arguments to find */
+void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags);
+void *ao2_callback(struct ao2_container *c,
+ enum search_flags flags,
+ ao2_callback_fn cb_fn, void *arg);
+
+int ao2_match_by_addr(void *user_data, void *arg, int flags);
+/*!
+ *
+ *
+ * When we need to walk through a container, we use
+ * ao2_iterator to keep track of the current position.
+ *
+ * Because the navigation is typically done without holding the
+ * lock on the container across the loop,
+ * objects can be inserted or deleted or moved
+ * while we work. As a consequence, there is no guarantee that
+ * the we manage to touch all the elements on the list, or it
+ * is possible that we touch the same object multiple times.
+ * However, within the current hash table container, the following is true:
+ * - It is not possible to miss an object in the container while iterating
+ * unless it gets added after the iteration begins and is added to a bucket
+ * that is before the one the current object is in. In this case, even if
+ * you locked the container around the entire iteration loop, you still would
+ * not see this object, because it would still be waiting on the container
+ * lock so that it can be added.
+ * - It would be extremely rare to see an object twice. The only way this can
+ * happen is if an object got unlinked from the container and added again
+ * during the same iteration. Furthermore, when the object gets added back,
+ * it has to be in the current or later bucket for it to be seen again.
+ *
+ * An iterator must be first initialized with ao2_iterator_init(),
+ * then we can use o = ao2_iterator_next() to move from one
+ * element to the next. Remember that the object returned by
+ * ao2_iterator_next() has its refcount incremented,
+ * and the reference must be explicitly released when done with it.
+ *
+ * Example:
+ *
+ * \code
+ *
+ * struct ao2_container *c = ... // the container we want to iterate on
+ * struct ao2_iterator i;
+ * struct my_obj *o;
+ *
+ * i = ao2_iterator_init(c, flags);
+ *
+ * while ( (o = ao2_iterator_next(&i)) ) {
+ * ... do something on o ...
+ * ao2_ref(o, -1);
+ * }
+ *
+ * \endcode
+ *
+ */
+
+/*!
+ * You are not supposed to know the internals of an iterator!
+ * We would like the iterator to be opaque, unfortunately
+ * its size needs to be known if we want to store it around
+ * without too much trouble.
+ * Anyways...
+ * The iterator has a pointer to the container, and a flags
+ * field specifying various things e.g. whether the container
+ * should be locked or not while navigating on it.
+ * The iterator "points" to the current object, which is identified
+ * by three values:
+ * - a bucket number;
+ * - the object_id, which is also the container version number
+ * when the object was inserted. This identifies the object
+ * univoquely, however reaching the desired object requires
+ * scanning a list.
+ * - a pointer, and a container version when we saved the pointer.
+ * If the container has not changed its version number, then we
+ * can safely follow the pointer to reach the object in constant time.
+ * Details are in the implementation of ao2_iterator_next()
+ * A freshly-initialized iterator has bucket=0, version = 0.
+ */
+
+struct ao2_iterator {
+ /*! the container */
+ struct ao2_container *c;
+ /*! operation flags */
+ int flags;
+#define F_AO2I_DONTLOCK 1 /*!< don't lock when iterating */
+ /*! current bucket */
+ int bucket;
+ /*! container version */
+ uint c_version;
+ /*! pointer to the current object */
+ void *obj;
+ /*! container version when the object was created */
+ uint version;
+};
+
+struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags);
+
+void *ao2_iterator_next(struct ao2_iterator *a);
+
+#endif /* _ASTERISK_ASTOBJ2_H */
diff --git a/include/asterisk/dlinkedlists.h b/include/asterisk/dlinkedlists.h
new file mode 100644
index 000000000..2f42fdd39
--- /dev/null
+++ b/include/asterisk/dlinkedlists.h
@@ -0,0 +1,974 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Steve Murphy <murf@digium.com>
+ *
+ * Doubly-Linked List Macros--
+ * Based on linkedlists.h (to the point of plagiarism!), which is by:
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#ifndef ASTERISK_DLINKEDLISTS_H
+#define ASTERISK_DLINKEDLISTS_H
+
+#include "asterisk/lock.h"
+
+/*!
+ \file dlinkedlists.h
+ \brief A set of macros to manage doubly-linked lists.
+*/
+
+/*!
+ \brief Locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_DLLIST_LOCK(head) \
+ ast_mutex_lock(&(head)->lock)
+
+/*!
+ \brief Write locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive write lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_RWDLLIST_WRLOCK(head) \
+ ast_rwlock_wrlock(&(head)->lock)
+
+/*!
+ \brief Read locks a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place a read lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_RWDLLIST_RDLOCK(head) \
+ ast_rwlock_rdlock(&(head)->lock)
+
+/*!
+ \brief Locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_DLLIST_TRYLOCK(head) \
+ ast_mutex_trylock(&(head)->lock)
+
+/*!
+ \brief Write locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place an exclusive write lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_RWDLLIST_TRYWRLOCK(head) \
+ ast_rwlock_trywrlock(&(head)->lock)
+
+/*!
+ \brief Read locks a list, without blocking if the list is locked.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to place a read lock in the
+ list head structure pointed to by head.
+ \retval 0 on success
+ \retval non-zero on failure
+*/
+#define AST_RWDLLIST_TRYRDLOCK(head) \
+ ast_rwlock_tryrdlock(&(head)->lock)
+
+/*!
+ \brief Attempts to unlock a list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to remove an exclusive lock from the
+ list head structure pointed to by head. If the list
+ was not locked by this thread, this macro has no effect.
+*/
+#define AST_DLLIST_UNLOCK(head) \
+ ast_mutex_unlock(&(head)->lock)
+
+/*!
+ \brief Attempts to unlock a read/write based list.
+ \param head This is a pointer to the list head structure
+
+ This macro attempts to remove a read or write lock from the
+ list head structure pointed to by head. If the list
+ was not locked by this thread, this macro has no effect.
+*/
+#define AST_RWDLLIST_UNLOCK(head) \
+ ast_rwlock_unlock(&(head)->lock)
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_DLLIST_HEAD(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_DLLIST_HEAD(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+}
+
+/*!
+ \brief Defines a structure to be used to hold a read/write list of specified type.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_RWDLLIST_HEAD(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_RWDLLIST_HEAD(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+}
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type (with no lock).
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type. It does not actually
+ declare (allocate) a structure; to do that, either follow this
+ macro with the desired name of the instance you wish to declare,
+ or use the specified \a name to declare instances elsewhere.
+
+ Example usage:
+ \code
+ static AST_DLLIST_HEAD_NOLOCK(entry_list, entry) entries;
+ \endcode
+
+ This would define \c struct \c entry_list, and declare an instance of it named
+ \a entries, all intended to hold a list of type \c struct \c entry.
+*/
+#define AST_DLLIST_HEAD_NOLOCK(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+}
+
+/*!
+ \brief Defines initial values for a declaration of AST_DLLIST_HEAD
+*/
+#define AST_DLLIST_HEAD_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ .lock = AST_MUTEX_INIT_VALUE, \
+ }
+
+/*!
+ \brief Defines initial values for a declaration of AST_RWDLLIST_HEAD
+*/
+#define AST_RWDLLIST_HEAD_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ .lock = AST_RWLOCK_INIT_VALUE, \
+ }
+
+/*!
+ \brief Defines initial values for a declaration of AST_DLLIST_HEAD_NOLOCK
+*/
+#define AST_DLLIST_HEAD_NOLOCK_INIT_VALUE { \
+ .first = NULL, \
+ .last = NULL, \
+ }
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type, and allocates an instance
+ of it, initialized to be empty.
+
+ Example usage:
+ \code
+ static AST_DLLIST_HEAD_STATIC(entry_list, entry);
+ \endcode
+
+ This would define \c struct \c entry_list, intended to hold a list of
+ type \c struct \c entry.
+*/
+#if defined(AST_MUTEX_INIT_W_CONSTRUCTORS)
+#define AST_DLLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+} name; \
+static void __attribute__ ((constructor)) __init_##name(void) \
+{ \
+ AST_DLLIST_HEAD_INIT(&name); \
+} \
+static void __attribute__ ((destructor)) __fini_##name(void) \
+{ \
+ AST_DLLIST_HEAD_DESTROY(&name); \
+} \
+struct __dummy_##name
+#else
+#define AST_DLLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_mutex_t lock; \
+} name = AST_DLLIST_HEAD_INIT_VALUE
+#endif
+
+/*!
+ \brief Defines a structure to be used to hold a read/write list of specified type, statically initialized.
+ \param name This will be the name of the defined structure.
+ \param type This is the type of each list entry.
+
+ This macro creates a structure definition that can be used
+ to hold a list of the entries of type \a type, and allocates an instance
+ of it, initialized to be empty.
+
+ Example usage:
+ \code
+ static AST_RWDLLIST_HEAD_STATIC(entry_list, entry);
+ \endcode
+
+ This would define \c struct \c entry_list, intended to hold a list of
+ type \c struct \c entry.
+*/
+#ifndef AST_RWLOCK_INIT_VALUE
+#define AST_RWDLLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+} name; \
+static void __attribute__ ((constructor)) __init_##name(void) \
+{ \
+ AST_RWDLLIST_HEAD_INIT(&name); \
+} \
+static void __attribute__ ((destructor)) __fini_##name(void) \
+{ \
+ AST_RWDLLIST_HEAD_DESTROY(&name); \
+} \
+struct __dummy_##name
+#else
+#define AST_RWDLLIST_HEAD_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+ ast_rwlock_t lock; \
+} name = AST_RWDLLIST_HEAD_INIT_VALUE
+#endif
+
+/*!
+ \brief Defines a structure to be used to hold a list of specified type, statically initialized.
+
+ This is the same as AST_DLLIST_HEAD_STATIC, except without the lock included.
+*/
+#define AST_DLLIST_HEAD_NOLOCK_STATIC(name, type) \
+struct name { \
+ struct type *first; \
+ struct type *last; \
+} name = AST_DLLIST_HEAD_NOLOCK_INIT_VALUE
+
+/*!
+ \brief Initializes a list head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value and recreating the embedded lock.
+*/
+#define AST_DLLIST_HEAD_SET(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+ ast_mutex_init(&(head)->lock); \
+} while (0)
+
+/*!
+ \brief Initializes an rwlist head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value and recreating the embedded lock.
+*/
+#define AST_RWDLLIST_HEAD_SET(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+ ast_rwlock_init(&(head)->lock); \
+} while (0)
+
+/*!
+ \brief Initializes a list head structure with a specified first entry.
+ \param head This is a pointer to the list head structure
+ \param entry pointer to the list entry that will become the head of the list
+
+ This macro initializes a list head structure by setting the head
+ entry to the supplied value.
+*/
+#define AST_DLLIST_HEAD_SET_NOLOCK(head, entry) do { \
+ (head)->first = (entry); \
+ (head)->last = (entry); \
+} while (0)
+
+/*!
+ \brief Declare previous/forward links inside a list entry.
+ \param type This is the type of each list entry.
+
+ This macro declares a structure to be used to doubly link list entries together.
+ It must be used inside the definition of the structure named in
+ \a type, as follows:
+
+ \code
+ struct list_entry {
+ ...
+ AST_DLLIST_ENTRY(list_entry) list;
+ }
+ \endcode
+
+ The field name \a list here is arbitrary, and can be anything you wish.
+*/
+#define AST_DLLIST_ENTRY(type) \
+struct { \
+ struct type *prev; \
+ struct type *next; \
+}
+
+#define AST_RWDLLIST_ENTRY AST_DLLIST_ENTRY
+
+/*!
+ \brief Returns the first entry contained in a list.
+ \param head This is a pointer to the list head structure
+ */
+#define AST_DLLIST_FIRST(head) ((head)->first)
+
+#define AST_RWDLLIST_FIRST AST_DLLIST_FIRST
+
+/*!
+ \brief Returns the last entry contained in a list.
+ \param head This is a pointer to the list head structure
+ */
+#define AST_DLLIST_LAST(head) ((head)->last)
+
+#define AST_RWDLLIST_LAST AST_DLLIST_LAST
+
+/*!
+ \brief Returns the next entry in the list after the given entry.
+ \param elm This is a pointer to the current entry.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+*/
+#define AST_DLLIST_NEXT(elm, field) ((elm)->field.next)
+
+#define AST_RWDLLIST_NEXT AST_DLLIST_NEXT
+
+/*!
+ \brief Returns the previous entry in the list before the given entry.
+ \param elm This is a pointer to the current entry.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+*/
+#define AST_DLLIST_PREV(elm, field) ((elm)->field.prev)
+
+#define AST_RWDLLIST_PREV AST_DLLIST_PREV
+
+/*!
+ \brief Checks whether the specified list contains any entries.
+ \param head This is a pointer to the list head structure
+
+ \return non-zero if the list has entries
+ \return zero if not.
+ */
+#define AST_DLLIST_EMPTY(head) (AST_DLLIST_FIRST(head) == NULL)
+
+#define AST_RWDLLIST_EMPTY AST_DLLIST_EMPTY
+
+/*!
+ \brief Loops over (traverses) the entries in a list.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is use to loop over (traverse) the entries in a list. It uses a
+ \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+ \code
+ static AST_DLLIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_DLLIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_DLLIST_TRAVERSE(&entries, current, list) {
+ (do something with current here)
+ }
+ \endcode
+ \warning If you modify the forward-link pointer contained in the \a current entry while
+ inside the loop, the behavior will be unpredictable. At a minimum, the following
+ macros will modify the forward-link pointer, and should not be used inside
+ AST_DLLIST_TRAVERSE() against the entry pointed to by the \a current pointer without
+ careful consideration of their consequences:
+ \li AST_DLLIST_NEXT() (when used as an lvalue)
+ \li AST_DLLIST_INSERT_AFTER()
+ \li AST_DLLIST_INSERT_HEAD()
+ \li AST_DLLIST_INSERT_TAIL()
+*/
+#define AST_DLLIST_TRAVERSE(head,var,field) \
+ for((var) = (head)->first; (var); (var) = (var)->field.next)
+
+#define AST_RWDLLIST_TRAVERSE AST_DLLIST_TRAVERSE
+
+/*!
+ \brief Loops over (traverses) the entries in a list in reverse order, starting at the end.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is use to loop over (traverse) the entries in a list in reverse order. It uses a
+ \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+ \code
+ static AST_DLLIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_DLLIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_DLLIST_TRAVERSE_BACKWARDS(&entries, current, list) {
+ (do something with current here)
+ }
+ \endcode
+ \warning If you modify the forward-link pointer contained in the \a current entry while
+ inside the loop, the behavior will be unpredictable. At a minimum, the following
+ macros will modify the forward-link pointer, and should not be used inside
+ AST_DLLIST_TRAVERSE() against the entry pointed to by the \a current pointer without
+ careful consideration of their consequences:
+ \li AST_DLLIST_PREV() (when used as an lvalue)
+ \li AST_DLLIST_INSERT_BEFORE()
+ \li AST_DLLIST_INSERT_HEAD()
+ \li AST_DLLIST_INSERT_TAIL()
+*/
+#define AST_DLLIST_TRAVERSE_BACKWARDS(head,var,field) \
+ for((var) = (head)->last; (var); (var) = (var)->field.prev)
+
+#define AST_RWDLLIST_TRAVERSE_BACKWARDS AST_DLLIST_TRAVERSE_BACKWARDS
+
+/*!
+ \brief Loops safely over (traverses) the entries in a list.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is used to safely loop over (traverse) the entries in a list. It
+ uses a \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+
+ \code
+ static AST_DLLIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_DLLIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_DLLIST_TRAVERSE_SAFE_BEGIN(&entries, current, list) {
+ (do something with current here)
+ }
+ AST_DLLIST_TRAVERSE_SAFE_END;
+ \endcode
+
+ It differs from AST_DLLIST_TRAVERSE() in that the code inside the loop can modify
+ (or even free, after calling AST_DLLIST_REMOVE_CURRENT()) the entry pointed to by
+ the \a current pointer without affecting the loop traversal.
+*/
+#define AST_DLLIST_TRAVERSE_SAFE_BEGIN(head, var, field) { \
+ typeof((head)) __list_head = head; \
+ typeof(__list_head->first) __list_next; \
+ typeof(__list_head->first) __list_prev = NULL; \
+ typeof(__list_head->first) __new_prev = NULL; \
+ for ((var) = __list_head->first, __new_prev = (var), \
+ __list_next = (var) ? (var)->field.next : NULL; \
+ (var); \
+ __list_prev = __new_prev, (var) = __list_next, \
+ __new_prev = (var), \
+ __list_next = (var) ? (var)->field.next : NULL \
+ )
+
+#define AST_RWDLLIST_TRAVERSE_SAFE_BEGIN AST_DLLIST_TRAVERSE_SAFE_BEGIN
+
+/*!
+ \brief Loops safely over (traverses) the entries in a list.
+ \param head This is a pointer to the list head structure
+ \param var This is the name of the variable that will hold a pointer to the
+ current list entry on each iteration. It must be declared before calling
+ this macro.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ This macro is used to safely loop over (traverse) the entries in a list. It
+ uses a \a for loop, and supplies the enclosed code with a pointer to each list
+ entry as it loops. It is typically used as follows:
+
+ \code
+ static AST_DLLIST_HEAD(entry_list, list_entry) entries;
+ ...
+ struct list_entry {
+ ...
+ AST_DLLIST_ENTRY(list_entry) list;
+ }
+ ...
+ struct list_entry *current;
+ ...
+ AST_DLLIST_TRAVERSE_SAFE_BEGIN(&entries, current, list) {
+ (do something with current here)
+ }
+ AST_DLLIST_TRAVERSE_SAFE_END;
+ \endcode
+
+ It differs from AST_DLLIST_TRAVERSE() in that the code inside the loop can modify
+ (or even free, after calling AST_DLLIST_REMOVE_CURRENT()) the entry pointed to by
+ the \a current pointer without affecting the loop traversal.
+*/
+#define AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN(head, var, field) { \
+ typeof((head)) __list_head = head; \
+ typeof(__list_head->first) __list_next; \
+ typeof(__list_head->first) __list_prev = NULL; \
+ typeof(__list_head->first) __new_prev = NULL; \
+ for ((var) = __list_head->last, __new_prev = (var), \
+ __list_next = NULL, __list_prev = (var) ? (var)->field.prev : NULL; \
+ (var); \
+ __list_next = __new_prev, (var) = __list_prev, \
+ __new_prev = (var), \
+ __list_prev = (var) ? (var)->field.prev : NULL \
+ )
+
+#define AST_RWDLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN
+
+/*!
+ \brief Removes the \a current entry from a list during a traversal.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ \note This macro can \b only be used inside an AST_DLLIST_TRAVERSE_SAFE_BEGIN()
+ block; it is used to unlink the current entry from the list without affecting
+ the list traversal (and without having to re-traverse the list to modify the
+ previous entry, if any).
+ */
+#define AST_DLLIST_REMOVE_CURRENT(field) do { \
+ __new_prev->field.next = NULL; \
+ __new_prev->field.prev = NULL; \
+ if (__list_next) \
+ __new_prev = __list_prev; \
+ else \
+ __new_prev = NULL; \
+ if (__list_prev) { \
+ if (__list_next) \
+ __list_next->field.prev = __list_prev; \
+ __list_prev->field.next = __list_next; \
+ } else { \
+ __list_head->first = __list_next; \
+ if (__list_next) \
+ __list_next->field.prev = NULL; \
+ } \
+ if (!__list_next) \
+ __list_head->last = __list_prev; \
+ } while (0)
+
+#define AST_RWDLLIST_REMOVE_CURRENT AST_DLLIST_REMOVE_CURRENT
+
+#define AST_DLLIST_MOVE_CURRENT(newhead, field) do { \
+ typeof ((newhead)->first) __list_cur = __new_prev; \
+ AST_DLLIST_REMOVE_CURRENT(field); \
+ AST_DLLIST_INSERT_TAIL((newhead), __list_cur, field); \
+ } while (0)
+
+#define AST_RWDLLIST_MOVE_CURRENT AST_DLLIST_MOVE_CURRENT
+
+#define AST_DLLIST_MOVE_CURRENT_BACKWARDS(newhead, field) do { \
+ typeof ((newhead)->first) __list_cur = __new_prev; \
+ if (!__list_next) { \
+ AST_DLLIST_REMOVE_CURRENT(field); \
+ __new_prev = NULL; \
+ AST_DLLIST_INSERT_HEAD((newhead), __list_cur, field); \
+ } else { \
+ AST_DLLIST_REMOVE_CURRENT(field); \
+ AST_DLLIST_INSERT_HEAD((newhead), __list_cur, field); \
+ }} while (0)
+
+#define AST_RWDLLIST_MOVE_CURRENT_BACKWARDS AST_DLLIST_MOVE_CURRENT
+
+/*!
+ \brief Inserts a list entry before the current entry during a traversal.
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ \note This macro can \b only be used inside an AST_DLLIST_TRAVERSE_SAFE_BEGIN()
+ block.
+ */
+#define AST_DLLIST_INSERT_BEFORE_CURRENT(elm, field) do { \
+ if (__list_prev) { \
+ (elm)->field.next = __list_prev->field.next; \
+ (elm)->field.prev = __list_prev; \
+ if (__list_prev->field.next) \
+ __list_prev->field.next->field.prev = (elm); \
+ __list_prev->field.next = (elm); \
+ } else { \
+ (elm)->field.next = __list_head->first; \
+ __list_head->first->field.prev = (elm); \
+ (elm)->field.prev = NULL; \
+ __list_head->first = (elm); \
+ } \
+} while (0)
+
+#define AST_RWDLLIST_INSERT_BEFORE_CURRENT AST_DLLIST_INSERT_BEFORE_CURRENT
+
+/*!
+ \brief Inserts a list entry after the current entry during a backwards traversal. Since
+ this is a backwards traversal, this will insert the entry AFTER the current
+ element. Since this is a backwards traveral, though, this would be BEFORE
+ the current entry in traversal order. Confusing?
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ \note This macro can \b only be used inside an AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN()
+ block. If you use this with the AST_DLLIST_TRAVERSE_SAFE_BEGIN(), be prepared for
+ strange things!
+ */
+#define AST_DLLIST_INSERT_BEFORE_CURRENT_BACKWARDS(elm, field) do { \
+ if (__list_next) { \
+ (elm)->field.next = __list_next; \
+ (elm)->field.prev = __new_prev; \
+ __new_prev->field.next = (elm); \
+ __list_next->field.prev = (elm); \
+ } else { \
+ (elm)->field.prev = __list_head->last; \
+ (elm)->field.next = NULL; \
+ __list_head->last->field.next = (elm); \
+ __list_head->last = (elm); \
+ } \
+} while (0)
+
+#define AST_RWDLLIST_INSERT_BEFORE_CURRENT_BACKWARDS AST_DLLIST_INSERT_BEFORE_CURRENT_BACKWARDS
+
+/*!
+ \brief Closes a safe loop traversal block.
+ */
+#define AST_DLLIST_TRAVERSE_SAFE_END }
+
+#define AST_RWDLLIST_TRAVERSE_SAFE_END AST_DLLIST_TRAVERSE_SAFE_END
+
+/*!
+ \brief Closes a safe loop traversal block.
+ */
+#define AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END }
+
+#define AST_RWDLLIST_TRAVERSE_BACKWARDS_SAFE_END AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END
+
+/*!
+ \brief Initializes a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list) and recreating the embedded lock.
+*/
+#define AST_DLLIST_HEAD_INIT(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_mutex_init(&(head)->lock); \
+}
+
+/*!
+ \brief Initializes an rwlist head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list) and recreating the embedded lock.
+*/
+#define AST_RWDLLIST_HEAD_INIT(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_rwlock_init(&(head)->lock); \
+}
+
+/*!
+ \brief Destroys a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro destroys a list head structure by setting the head
+ entry to \a NULL (empty list) and destroying the embedded lock.
+ It does not free the structure from memory.
+*/
+#define AST_DLLIST_HEAD_DESTROY(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_mutex_destroy(&(head)->lock); \
+}
+
+/*!
+ \brief Destroys an rwlist head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro destroys a list head structure by setting the head
+ entry to \a NULL (empty list) and destroying the embedded lock.
+ It does not free the structure from memory.
+*/
+#define AST_RWDLLIST_HEAD_DESTROY(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+ ast_rwlock_destroy(&(head)->lock); \
+}
+
+/*!
+ \brief Initializes a list head structure.
+ \param head This is a pointer to the list head structure
+
+ This macro initializes a list head structure by setting the head
+ entry to \a NULL (empty list). There is no embedded lock handling
+ with this macro.
+*/
+#define AST_DLLIST_HEAD_INIT_NOLOCK(head) { \
+ (head)->first = NULL; \
+ (head)->last = NULL; \
+}
+
+/*!
+ \brief Inserts a list entry after a given entry.
+ \param head This is a pointer to the list head structure
+ \param listelm This is a pointer to the entry after which the new entry should
+ be inserted.
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_DLLIST_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.next = (listelm)->field.next; \
+ (elm)->field.prev = (listelm); \
+ if ((listelm)->field.next) \
+ (listelm)->field.next->field.prev = (elm); \
+ (listelm)->field.next = (elm); \
+ if ((head)->last == (listelm)) \
+ (head)->last = (elm); \
+} while (0)
+
+#define AST_RWDLLIST_INSERT_AFTER AST_DLLIST_INSERT_AFTER
+
+/*!
+ \brief Inserts a list entry before a given entry.
+ \param head This is a pointer to the list head structure
+ \param listelm This is a pointer to the entry before which the new entry should
+ be inserted.
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_DLLIST_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.next = (listelm); \
+ (elm)->field.prev = (listelm)->field.prev; \
+ if ((listelm)->field.prev) \
+ (listelm)->field.prev->field.next = (elm); \
+ (listelm)->field.prev = (elm); \
+ if ((head)->first == (listelm)) \
+ (head)->first = (elm); \
+} while (0)
+
+#define AST_RWDLLIST_INSERT_BEFORE AST_DLLIST_INSERT_BEFORE
+
+/*!
+ \brief Inserts a list entry at the head of a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be inserted.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+ */
+#define AST_DLLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.next = (head)->first; \
+ if ((head)->first) \
+ (head)->first->field.prev = (elm); \
+ (elm)->field.prev = NULL; \
+ (head)->first = (elm); \
+ if (!(head)->last) \
+ (head)->last = (elm); \
+} while (0)
+
+#define AST_RWDLLIST_INSERT_HEAD AST_DLLIST_INSERT_HEAD
+
+/*!
+ \brief Appends a list entry to the tail of a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be appended.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ Note: The link field in the appended entry is \b not modified, so if it is
+ actually the head of a list itself, the entire list will be appended
+ temporarily (until the next AST_DLLIST_INSERT_TAIL is performed).
+ */
+#define AST_DLLIST_INSERT_TAIL(head, elm, field) do { \
+ if (!(head)->first) { \
+ (head)->first = (elm); \
+ (head)->last = (elm); \
+ (elm)->field.next = NULL; \
+ (elm)->field.prev = NULL; \
+ } else { \
+ (head)->last->field.next = (elm); \
+ (elm)->field.prev = (head)->last; \
+ (elm)->field.next = NULL; \
+ (head)->last = (elm); \
+ } \
+} while (0)
+
+#define AST_RWDLLIST_INSERT_TAIL AST_DLLIST_INSERT_TAIL
+
+/*!
+ \brief Appends a whole list to the tail of a list.
+ \param head This is a pointer to the list head structure
+ \param list This is a pointer to the list to be appended.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ Note: The source list (the \a list parameter) will be empty after
+ calling this macro (the list entries are \b moved to the target list).
+ */
+#define AST_DLLIST_APPEND_DLLIST(head, list, field) do { \
+ if (!(head)->first) { \
+ (head)->first = (list)->first; \
+ (head)->last = (list)->last; \
+ } else { \
+ (head)->last->field.next = (list)->first; \
+ (list)->first->field.prev = (head)->last; \
+ (head)->last = (list)->last; \
+ } \
+ (list)->first = NULL; \
+ (list)->last = NULL; \
+} while (0)
+
+#define AST_RWDLLIST_APPEND_DLLIST AST_DLLIST_APPEND_DLLIST
+
+/*!
+ \brief Removes and returns the head entry from a list.
+ \param head This is a pointer to the list head structure
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+
+ Removes the head entry from the list, and returns a pointer to it.
+ This macro is safe to call on an empty list.
+ */
+#define AST_DLLIST_REMOVE_HEAD(head, field) ({ \
+ typeof((head)->first) cur = (head)->first; \
+ if (cur) { \
+ (head)->first = cur->field.next; \
+ if (cur->field.next) \
+ cur->field.next->field.prev = NULL; \
+ cur->field.next = NULL; \
+ if ((head)->last == cur) \
+ (head)->last = NULL; \
+ } \
+ cur; \
+ })
+
+#define AST_RWDLLIST_REMOVE_HEAD AST_DLLIST_REMOVE_HEAD
+
+/*!
+ \brief Removes a specific entry from a list.
+ \param head This is a pointer to the list head structure
+ \param elm This is a pointer to the entry to be removed.
+ \param field This is the name of the field (declared using AST_DLLIST_ENTRY())
+ used to link entries of this list together.
+ \warning The removed entry is \b not freed nor modified in any way.
+ */
+#define AST_DLLIST_REMOVE(head, elm, field) ({ \
+ __typeof(elm) __res = (elm); \
+ if ((head)->first == (elm)) { \
+ (head)->first = (elm)->field.next; \
+ if ((elm)->field.next) \
+ (elm)->field.next->field.prev = NULL; \
+ if ((head)->last == (elm)) \
+ (head)->last = NULL; \
+ } else { \
+ (elm)->field.prev->field.next = (elm)->field.next; \
+ if ((elm)->field.next) \
+ (elm)->field.next->field.prev = (elm)->field.prev; \
+ if ((head)->last == (elm)) \
+ (head)->last = (elm)->field.prev; \
+ } \
+ (elm)->field.next = NULL; \
+ (elm)->field.prev = NULL; \
+ (__res); \
+})
+
+#define AST_RWDLLIST_REMOVE AST_DLLIST_REMOVE
+
+#endif /* _ASTERISK_DLINKEDLISTS_H */