]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
automerge commit
authorAutomerge script <automerge@asterisk.org>
Tue, 6 May 2008 19:20:05 +0000 (19:20 +0000)
committerAutomerge script <automerge@asterisk.org>
Tue, 6 May 2008 19:20:05 +0000 (19:20 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.2-netsec@115414 65c4cc65-6c06-0410-ace0-fbb531ad65f3

Makefile
astobj2.c [new file with mode: 0644]
channels/chan_iax2.c
include/asterisk/astobj2.h [new file with mode: 0644]
include/asterisk/dlinkedlists.h [new file with mode: 0644]

index bbef57878c561414505be7f9111bbc809fbfe898..e2e09628385026205394e62dbc8a6bf2df7e4903 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -358,7 +358,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 (file)
index 0000000..66c5a2c
--- /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;
+}
index 8d031f7eac368a81c7998085fc13fdb949d13414..454c4ee86ef66ba6c8bcf99f352df9a7e8edde71 100644 (file)
@@ -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 (file)
index 0000000..bcb8add
--- /dev/null
@@ -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 (file)
index 0000000..2f42fdd
--- /dev/null
@@ -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 */