[asterisk-commits] rmudgett: branch rmudgett/ao2_enhancements r371192 - /team/rmudgett/ao2_enhan...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Aug 13 13:23:16 CDT 2012
Author: rmudgett
Date: Mon Aug 13 13:23:12 2012
New Revision: 371192
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=371192
Log:
Reworked ao2_callback() to use a find_first/find_next strategy.
Modified:
team/rmudgett/ao2_enhancements/main/astobj2.c
Modified: team/rmudgett/ao2_enhancements/main/astobj2.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/ao2_enhancements/main/astobj2.c?view=diff&rev=371192&r1=371191&r2=371192
==============================================================================
--- team/rmudgett/ao2_enhancements/main/astobj2.c (original)
+++ team/rmudgett/ao2_enhancements/main/astobj2.c Mon Aug 13 13:23:12 2012
@@ -725,6 +725,11 @@
return obj;
}
+enum ao2_callback_type {
+ AO2_CALLBACK_DEFAULT,
+ AO2_CALLBACK_WITH_DATA,
+};
+
enum ao2_container_insert {
/*! The node was inserted into the container. */
AO2_CONTAINER_INSERT_NODE_INSERTED,
@@ -734,9 +739,22 @@
AO2_CONTAINER_INSERT_NODE_REJECTED,
};
-enum ao2_callback_type {
- DEFAULT,
- WITH_DATA,
+enum ao2_container_rtti {
+ /*! This is a hash container */
+ AO2_CONTAINER_RTTI_HASH,
+};
+
+/*!
+ * \brief Generic container node.
+ *
+ * \details This is the base container node type that contains
+ * values common to all container nodes.
+ */
+struct ao2_container_node {
+ /*! Stored object in node. */
+ void *obj;
+ /*! Container holding the node. (Does not hold a reference.) */
+ struct ao2_container *my_container;
};
/*!
@@ -790,32 +808,44 @@
typedef int (*ao2_container_link_fn)(struct ao2_container *self, void *obj_new, enum search_flags flags, const char *tag, const char *file, int line, const char *func);
/*!
- * \brief Traverse the container.
+ * \brief Find the first container node in a traversal.
+ * \since 11.0
*
* \param self Container to operate upon.
* \param flags search_flags to control traversing the container
- * \param cb_fn Comparison callback function.
* \param arg Comparison callback arg parameter.
- * \param data Data comparison callback data parameter.
- * \param type Type of comparison callback cb_fn.
- * \param tag used for debugging.
- * \param file Debug file name invoked from
- * \param line Debug line invoked from
- * \param func Debug function name invoked from
- *
- * \retval NULL on failure or no matching object found.
- *
- * \retval object found if OBJ_MULTIPLE is not set in the flags
- * parameter.
- *
- * \retval ao2_iterator pointer if OBJ_MULTIPLE is set in the
- * flags parameter. The iterator must be destroyed with
- * ao2_iterator_destroy() when the caller no longer needs it.
- */
-typedef void *(*ao2_container_traverse_fn)(struct ao2_container *self, enum search_flags flags, void *cb_fn, void *arg, void *data, enum ao2_callback_type type, const char *tag, const char *file, int line, const char *func);
-
-/*!
- * \internal
+ * \param v_state Traversal state to restart container traversal.
+ *
+ * \retval node-ptr of found node (Reffed).
+ * \retval NULL when no node found.
+ */
+typedef struct ao2_container_node *(*ao2_container_find_first_fn)(struct ao2_container *self, enum search_flags flags, void *arg, void *v_state);
+
+/*!
+ * \brief Find the next container node in a traversal.
+ * \since 11.0
+ *
+ * \param self Container to operate upon.
+ * \param v_state Traversal state to restart container traversal.
+ * \param prev Previous node returned by the traversal search functions.
+ * The ref ownership is passed back to this function.
+ *
+ * \retval node-ptr of found node (Reffed).
+ * \retval NULL when no node found.
+ */
+typedef struct ao2_container_node *(*ao2_container_find_next_fn)(struct ao2_container *self, void *v_state, struct ao2_container_node *prev);
+
+/*!
+ * \brief Cleanup the container traversal state.
+ * \since 11.0
+ *
+ * \param v_state Traversal state to cleanup.
+ *
+ * \return Nothing
+ */
+typedef void (*ao2_container_find_cleanup_fn)(void *v_state);
+
+/*!
* \brief Find the next iteration element in the container.
*
* \param self Container to operate upon.
@@ -833,7 +863,6 @@
typedef void *(*ao2_iterator_next_fn)(struct ao2_container *self, struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func);
/*!
- * \internal
* \brief Display statistics of the specified container.
*
* \param self Container to display statistics.
@@ -847,7 +876,6 @@
typedef void (*ao2_container_statistics)(struct ao2_container *self, int fd, void (*prnt)(int fd, const char *fmt, ...) __attribute__((format(printf, 2, 3))));
/*!
- * \internal
* \brief Perform an integrity check on the specified container.
*
* \param self Container to check integrity.
@@ -860,7 +888,9 @@
typedef int (*ao2_container_integrity)(struct ao2_container *self);
/*! Container virtual methods template. */
-struct container_methods {
+struct ao2_container_methods {
+ /*! Run Time Type Identification */
+ enum ao2_container_rtti type;
/*! Destroy this container. */
ao2_container_destroy_fn destroy;
/*! \brief Create an empty copy of this container. */
@@ -869,8 +899,12 @@
ao2_container_alloc_empty_clone_debug_fn alloc_empty_clone_debug;
/*! Link an object into this container. */
ao2_container_link_fn link;
- /*! Traverse the container. */
- ao2_container_traverse_fn traverse;
+ /*! Traverse the container, find the first node. */
+ ao2_container_find_first_fn traverse_first;
+ /*! Traverse the container, find the next node. */
+ ao2_container_find_next_fn traverse_next;
+ /*! Traverse the container, cleanup state. */
+ ao2_container_find_cleanup_fn traverse_cleanup;
/*! Find the next iteration element in the container. */
ao2_iterator_next_fn iterator_next;
#if defined(AST_DEVMODE)
@@ -897,7 +931,7 @@
*/
struct ao2_container {
/*! Container virtual method table. */
- const struct container_methods *v_table;
+ const struct ao2_container_methods *v_table;
/*! Container sort function if the container is sorted. */
ao2_sort_fn *sort_fn;
/*! Container traversal matching function for ao2_find. */
@@ -1001,1123 +1035,17 @@
return CMP_MATCH;
}
-void *__ao2_callback_debug(struct ao2_container *c, enum search_flags flags,
- ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line,
- const char *func)
-{
- if (!INTERNAL_OBJ(c) || !c->v_table || !c->v_table->traverse) {
- /* Sanity checks. */
- return NULL;
- }
- return c->v_table->traverse(c, flags, cb_fn, arg, NULL, DEFAULT, tag, file, line, func);
-}
-
-void *__ao2_callback(struct ao2_container *c, enum search_flags flags,
- ao2_callback_fn *cb_fn, void *arg)
-{
- if (!INTERNAL_OBJ(c) || !c->v_table || !c->v_table->traverse) {
- /* Sanity checks. */
- return NULL;
- }
- return c->v_table->traverse(c, flags, cb_fn, arg, NULL, DEFAULT, NULL, NULL, 0, NULL);
-}
-
-void *__ao2_callback_data_debug(struct ao2_container *c, enum search_flags flags,
- ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file,
- int line, const char *func)
-{
- if (!INTERNAL_OBJ(c) || !c->v_table || !c->v_table->traverse) {
- /* Sanity checks. */
- return NULL;
- }
- return c->v_table->traverse(c, flags, cb_fn, arg, data, WITH_DATA, tag, file, line, func);
-}
-
-void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
- ao2_callback_data_fn *cb_fn, void *arg, void *data)
-{
- if (!INTERNAL_OBJ(c) || !c->v_table || !c->v_table->traverse) {
- /* Sanity checks. */
- return NULL;
- }
- return c->v_table->traverse(c, flags, cb_fn, arg, data, WITH_DATA, NULL, NULL, 0, NULL);
-}
-
-/*!
- * the find function just invokes the default callback with some reasonable flags.
- */
-void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags,
- const char *tag, const char *file, int line, const char *func)
-{
- void *arged = (void *) arg;/* Done to avoid compiler const warning */
-
- return __ao2_callback_debug(c, flags, c->cmp_fn, arged, tag, file, line, func);
-}
-
-void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags)
-{
- void *arged = (void *) arg;/* Done to avoid compiler const warning */
-
- return __ao2_callback(c, flags, c->cmp_fn, arged);
-}
-
-/*!
- * 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
- };
-
- ao2_ref(c, +1);
-
- return a;
-}
-
-/*!
- * destroy an iterator
- */
-void ao2_iterator_destroy(struct ao2_iterator *iter)
-{
- /* Release the last container node reference if we have one. */
- if (iter->last_node) {
- enum ao2_lock_req orig_lock;
-
- /*
- * Do a read lock in case the container node unref does not
- * destroy the node. If the container node is destroyed then
- * the lock will be upgraded to a write lock.
- */
- if (iter->flags & AO2_ITERATOR_DONTLOCK) {
- orig_lock = adjust_lock(iter->c, AO2_LOCK_REQ_RDLOCK, 1);
- } else {
- orig_lock = AO2_LOCK_REQ_MUTEX;
- ao2_rdlock(iter->c);
- }
-
- ao2_ref(iter->last_node, -1);
- iter->last_node = NULL;
-
- if (iter->flags & AO2_ITERATOR_DONTLOCK) {
- adjust_lock(iter->c, orig_lock, 0);
- } else {
- ao2_unlock(iter->c);
- }
- }
-
- /* Release the iterated container reference. */
- ao2_ref(iter->c, -1);
- iter->c = NULL;
-
- /* Free the malloced iterator. */
- if (iter->flags & AO2_ITERATOR_MALLOCD) {
- ast_free(iter);
- }
-}
-
-void ao2_iterator_cleanup(struct ao2_iterator *iter)
-{
- if (iter) {
- ao2_iterator_destroy(iter);
- }
-}
-
-/*
- * move to the next element in the container.
- */
-static void *internal_ao2_iterator_next(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func)
-{
- enum ao2_lock_req orig_lock;
- void *ret;
-
- if (!INTERNAL_OBJ(iter->c) || !iter->c->v_table || !iter->c->v_table->iterator_next) {
- /* Sanity checks. */
- return NULL;
- }
-
- if (iter->flags & AO2_ITERATOR_DONTLOCK) {
- if (iter->flags & AO2_ITERATOR_UNLINK) {
- orig_lock = adjust_lock(iter->c, AO2_LOCK_REQ_WRLOCK, 1);
- } else {
- orig_lock = adjust_lock(iter->c, AO2_LOCK_REQ_RDLOCK, 1);
- }
- } else {
- orig_lock = AO2_LOCK_REQ_MUTEX;
- if (iter->flags & AO2_ITERATOR_UNLINK) {
- ao2_wrlock(iter->c);
- } else {
- ao2_rdlock(iter->c);
- }
- }
-
- ret = iter->c->v_table->iterator_next(iter->c, iter, tag, file, line, func);
-
- if (iter->flags & AO2_ITERATOR_DONTLOCK) {
- adjust_lock(iter->c, orig_lock, 0);
- } else {
- ao2_unlock(iter->c);
- }
-
- return ret;
-}
-
-void *__ao2_iterator_next_debug(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func)
-{
- return internal_ao2_iterator_next(iter, tag, file, line, func);
-}
-
-void *__ao2_iterator_next(struct ao2_iterator *iter)
-{
- return internal_ao2_iterator_next(iter, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
-}
-
-static void container_destruct(void *_c)
-{
- struct ao2_container *c = _c;
-
- /* Unlink any stored objects in the container. */
- c->destroying = 1;
- __ao2_callback(c, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
-
- /* Perform any extra container cleanup. */
- if (c->v_table && c->v_table->destroy) {
- c->v_table->destroy(c);
- }
-
-#ifdef AO2_DEBUG
- ast_atomic_fetchadd_int(&ao2.total_containers, -1);
-#endif
-}
-
-static void container_destruct_debug(void *_c)
-{
- struct ao2_container *c = _c;
-
- /* Unlink any stored objects in the container. */
- c->destroying = 1;
- __ao2_callback_debug(c, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL,
- "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__);
-
- /* Perform any extra container cleanup. */
- if (c->v_table && c->v_table->destroy) {
- c->v_table->destroy(c);
- }
-
-#ifdef AO2_DEBUG
- ast_atomic_fetchadd_int(&ao2.total_containers, -1);
-#endif
-}
+#if defined(AST_DEVMODE)
+static void hash_ao2_traverse_unlink_node_stat(struct ao2_container *self, struct ao2_container_node *node);
+#endif /* defined(AST_DEVMODE) */
+
+/*! Allow enough room for container specific traversal state structs */
+#define AO2_TRAVERSAL_STATE_SIZE 100
/*!
* \internal
- * \brief Put obj into the arg container.
+ * \brief Traverse the container. (internal)
* \since 11.0
- *
- * \param obj pointer to the (user-defined part) of an object.
- * \param arg callback argument from ao2_callback()
- * \param flags flags from ao2_callback()
- *
- * \retval 0 on success.
- * \retval CMP_STOP|CMP_MATCH on error.
- */
-static int dup_obj_cb(void *obj, void *arg, int flags)
-{
- struct ao2_container *dest = arg;
-
- return __ao2_link(dest, obj, OBJ_NOLOCK) ? 0 : (CMP_MATCH | CMP_STOP);
-}
-
-int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
-{
- void *obj;
- int res = 0;
-
- if (!(flags & OBJ_NOLOCK)) {
- ao2_rdlock(src);
- ao2_wrlock(dest);
- }
- obj = __ao2_callback(src, OBJ_NOLOCK, dup_obj_cb, dest);
- if (obj) {
- /* Failed to put this obj into the dest container. */
- __ao2_ref(obj, -1);
-
- /* Remove all items from the dest container. */
- __ao2_callback(dest, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL,
- NULL);
- res = -1;
- }
- if (!(flags & OBJ_NOLOCK)) {
- ao2_unlock(dest);
- ao2_unlock(src);
- }
-
- return res;
-}
-
-struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags)
-{
- struct ao2_container *clone;
- int failed;
-
- /* Create the clone container with the same properties as the original. */
- if (!INTERNAL_OBJ(orig) || !orig->v_table || !orig->v_table->alloc_empty_clone) {
- /* Sanity checks. */
- return NULL;
- }
- clone = orig->v_table->alloc_empty_clone(orig);
- if (!clone) {
- return NULL;
- }
-
- if (flags & OBJ_NOLOCK) {
- ao2_wrlock(clone);
- }
- failed = ao2_container_dup(clone, orig, flags);
- if (flags & OBJ_NOLOCK) {
- ao2_unlock(clone);
- }
- if (failed) {
- /* Object copy into the clone container failed. */
- __ao2_ref(clone, -1);
- clone = NULL;
- }
- return clone;
-}
-
-struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *func, int ref_debug)
-{
- struct ao2_container *clone;
- int failed;
-
- /* Create the clone container with the same properties as the original. */
- if (!INTERNAL_OBJ(orig) || !orig->v_table || !orig->v_table->alloc_empty_clone_debug) {
- /* Sanity checks. */
- return NULL;
- }
- clone = orig->v_table->alloc_empty_clone_debug(orig, tag, file, line, func, ref_debug);
- if (!clone) {
- return NULL;
- }
-
- if (flags & OBJ_NOLOCK) {
- ao2_wrlock(clone);
- }
- failed = ao2_container_dup(clone, orig, flags);
- if (flags & OBJ_NOLOCK) {
- ao2_unlock(clone);
- }
- if (failed) {
- /* Object copy into the clone container failed. */
- if (ref_debug) {
- __ao2_ref_debug(clone, -1, tag, file, line, func);
- } else {
- __ao2_ref(clone, -1);
- }
- clone = NULL;
- }
- return clone;
-}
-
-#if defined(AST_DEVMODE)
-/*!
- * \internal
- * \brief Display statistics of the specified container.
- * \since 11.0
- *
- * \param self Container to display statistics.
- * \param fd File descriptor to send output.
- * \param prnt Print output callback function to use.
- *
- * \return Nothing
- */
-static void ao2_container_stats(struct ao2_container *self, int fd, void (*prnt)(int fd, const char *fmt, ...) __attribute__((format(printf, 2, 3))))
-{
- if (!INTERNAL_OBJ(self) || !self->v_table) {
- prnt(fd, "Invalid container\n");
- return;
- }
-
- ao2_rdlock(self);
- prnt(fd, "Number of objects: %d\n", self->elements);
- if (self->v_table->stats) {
- self->v_table->stats(self, fd, prnt);
- }
- ao2_unlock(self);
-}
-#endif /* defined(AST_DEVMODE) */
-
-int ao2_container_check(struct ao2_container *self, enum search_flags flags)
-{
- int res = 0;
-
- if (!INTERNAL_OBJ(self) || !self->v_table) {
- /* Sanity checks. */
- return -1;
- }
-#if defined(AST_DEVMODE)
- if (!self->v_table->integrity) {
- /* No ingetrigy check available. Assume container is ok. */
- return 0;
- }
-
- if (flags & OBJ_NOLOCK) {
- ao2_rdlock(self);
- }
- res = self->v_table->integrity(self);
- if (flags & OBJ_NOLOCK) {
- ao2_unlock(self);
- }
-#endif /* defined(AST_DEVMODE) */
- return res;
-}
-
-struct ao2_container_hash;
-
-/*!
- * A structure to create a linked list of entries,
- * used within a bucket.
- */
-struct hash_bucket_node {
- /*! Next node links in the list. */
- AST_DLLIST_ENTRY(hash_bucket_node) links;
- /*! Stored object in node. */
- void *obj;
- /*! Container holding the node. (Does not hold a reference.) */
- struct ao2_container_hash *my_container;
- /*! Hash bucket holding the node. */
- int my_bucket;
-};
-
-struct hash_bucket {
- /*! List of objects held in the bucket. */
- AST_DLLIST_HEAD_NOLOCK(, hash_bucket_node) list;
-#if defined(AST_DEVMODE)
- /*! Number of elements currently in the bucket. */
- int elements;
- /*! Maximum number of elements in the bucket. */
- int max_elements;
-#endif /* defined(AST_DEVMODE) */
-};
-
-/*!
- * A hash container in addition to values common to all
- * container types, stores the hash callback function, the
- * number of hash buckets, and the hash bucket heads.
- */
-struct ao2_container_hash {
- /*!
- * \brief Items common to all containers.
- * \note Must be first in the specific container struct.
- */
- struct ao2_container common;
- ao2_hash_fn *hash_fn;
- /*! Number of hash buckets in this container. */
- int n_buckets;
- /*! Hash bucket array of n_buckets. Variable size. */
- struct hash_bucket buckets[0];
-};
-
-/*!
- * \internal
- * \brief Create an empty copy of this container.
- * \since 11.0
- *
- * \param self Container to operate upon.
- *
- * \retval empty-clone-container on success.
- * \retval NULL on error.
- */
-static struct ao2_container *hash_ao2_alloc_empty_clone(struct ao2_container_hash *self)
-{
- struct astobj2 *orig_obj;
- unsigned int ao2_options;
-
- /* Get container ao2 options. */
- orig_obj = INTERNAL_OBJ(self);
- if (!orig_obj) {
- return NULL;
- }
- ao2_options = orig_obj->priv_data.options;
-
- return __ao2_container_alloc_hash(ao2_options, self->common.options, self->n_buckets,
- self->hash_fn, self->common.sort_fn, self->common.cmp_fn);
-}
-
-/*!
- * \internal
- * \brief Create an empty copy of this container. (Debug version)
- * \since 11.0
- *
- * \param self Container to operate upon.
- * \param tag used for debugging.
- * \param file Debug file name invoked from
- * \param line Debug line invoked from
- * \param func Debug function name invoked from
- * \param ref_debug TRUE if to output a debug reference message.
- *
- * \retval empty-clone-container on success.
- * \retval NULL on error.
- */
-static struct ao2_container *hash_ao2_alloc_empty_clone_debug(struct ao2_container_hash *self, const char *tag, const char *file, int line, const char *func, int ref_debug)
-{
- struct astobj2 *orig_obj;
- unsigned int ao2_options;
-
- /* Get container ao2 options. */
- orig_obj = INTERNAL_OBJ(self);
- if (!orig_obj) {
- return NULL;
- }
- ao2_options = orig_obj->priv_data.options;
-
- return __ao2_container_alloc_hash_debug(ao2_options, self->common.options,
- self->n_buckets, self->hash_fn, self->common.sort_fn, self->common.cmp_fn,
- tag, file, line, func, ref_debug);
-}
-
-/*!
- * \internal
- * \brief Destroy a hash container list node.
- * \since 11.0
- *
- * \param v_doomed Container node to destroy.
- *
- * \details
- * The container node unlinks itself from the container as part
- * of its destruction. The node must be destroyed while the
- * container is already locked.
- *
- * \return Nothing
- */
-static void hash_ao2_node_destructor(void *v_doomed)
-{
- struct hash_bucket_node *doomed = v_doomed;
- struct hash_bucket *bucket;
- struct ao2_container_hash *my_container;
-
- my_container = doomed->my_container;
- if (my_container) {
- /*
- * Promote to write lock if not already there. Since
- * adjust_lock() can potentially release and block waiting for a
- * write lock, care must be taken to ensure that node references
- * are released before releasing the container references.
- *
- * Node references held by an iterator can only be held while
- * the iterator also holds a reference to the container. These
- * node references must be unreferenced before the container can
- * be unreferenced to ensure that the node will not get a
- * negative reference and the destructor called twice for the
- * same node.
- */
- adjust_lock(my_container, AO2_LOCK_REQ_WRLOCK, 1);
-
- bucket = &my_container->buckets[doomed->my_bucket];
- AST_DLLIST_REMOVE(&bucket->list, doomed, links);
- }
-
- /*
- * We could have an object in the node if the container is being
- * destroyed or the node had not been linked in yet.
- */
- if (doomed->obj) {
- ao2_ref(doomed->obj, -1);
- doomed->obj = NULL;
- }
-}
-
-/*!
- * \internal
- * \brief Insert the given node into the specified bucket in the container.
- * \since 11.0
- *
- * \param self Container to operate upon.
- * \param bucket Hash bucket to insert the node.
- * \param node What to put in the bucket list.
- *
- * \return enum ao2_container_insert value.
- */
-static enum ao2_container_insert hash_ao2_link_insert(struct ao2_container_hash *self, struct hash_bucket *bucket, struct hash_bucket_node *node)
-{
- int cmp;
- struct hash_bucket_node *cur;
- ao2_sort_fn *sort_fn;
- uint32_t options;
-
- sort_fn = self->common.sort_fn;
- options = self->common.options;
-
- if (options & AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN) {
- if (sort_fn) {
- AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_BEGIN(&bucket->list, cur, links) {
- cmp = sort_fn(cur->obj, node->obj, OBJ_POINTER);
- if (cmp > 0) {
- continue;
- }
- if (cmp < 0) {
- AST_DLLIST_INSERT_AFTER_CURRENT(node, links);
- return AO2_CONTAINER_INSERT_NODE_INSERTED;
- }
- switch (options & AO2_CONTAINER_ALLOC_OPT_DUPS_MASK) {
- default:
- case AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW:
- break;
- case AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT:
- /* Reject all objects with the same key. */
- return AO2_CONTAINER_INSERT_NODE_REJECTED;
- case AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT:
- if (cur->obj == node->obj) {
- /* Reject inserting the same object */
- return AO2_CONTAINER_INSERT_NODE_REJECTED;
- }
- break;
- case AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE:
- SWAP(cur->obj, node->obj);
- return AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED;
- }
- }
- AST_DLLIST_TRAVERSE_BACKWARDS_SAFE_END;
- }
- AST_DLLIST_INSERT_HEAD(&bucket->list, node, links);
- } else {
- if (sort_fn) {
- AST_DLLIST_TRAVERSE_SAFE_BEGIN(&bucket->list, cur, links) {
- cmp = sort_fn(cur->obj, node->obj, OBJ_POINTER);
- if (cmp < 0) {
- continue;
- }
- if (cmp > 0) {
- AST_DLLIST_INSERT_BEFORE_CURRENT(node, links);
- return AO2_CONTAINER_INSERT_NODE_INSERTED;
- }
- switch (options & AO2_CONTAINER_ALLOC_OPT_DUPS_MASK) {
- default:
- case AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW:
- break;
- case AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT:
- /* Reject all objects with the same key. */
- return AO2_CONTAINER_INSERT_NODE_REJECTED;
- case AO2_CONTAINER_ALLOC_OPT_DUPS_OBJ_REJECT:
- if (cur->obj == node->obj) {
- /* Reject inserting the same object */
- return AO2_CONTAINER_INSERT_NODE_REJECTED;
- }
- break;
- case AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE:
- SWAP(cur->obj, node->obj);
- return AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED;
- }
- }
- AST_DLLIST_TRAVERSE_SAFE_END;
- }
- AST_DLLIST_INSERT_TAIL(&bucket->list, node, links);
- }
- return AO2_CONTAINER_INSERT_NODE_INSERTED;
-}
-
-/*!
- * \internal
- * \brief Link an object into this container.
- * \since 11.0
- *
- * \param self Container to operate upon.
- * \param obj_new Object to insert into the container.
- * \param flags search_flags to control linking the object. (OBJ_NOLOCK)
- * \param tag used for debugging.
- * \param file Debug file name invoked from
- * \param line Debug line invoked from
- * \param func Debug function name invoked from
- *
- * \retval 0 on errors.
- * \retval 1 on success.
- */
-static int hash_ao2_link(struct ao2_container_hash *self, void *obj_new, int flags, const char *tag, const char *file, int line, const char *func)
-{
- int i;
- int res;
- enum ao2_lock_req orig_lock;
- struct hash_bucket_node *node;
-
- node = __ao2_alloc(sizeof(*node), hash_ao2_node_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
- if (!node) {
- return 0;
- }
-
- i = abs(self->hash_fn(obj_new, OBJ_POINTER));
- i %= self->n_buckets;
-
- if (flags & OBJ_NOLOCK) {
- orig_lock = adjust_lock(self, AO2_LOCK_REQ_WRLOCK, 1);
- } else {
- ao2_wrlock(self);
- orig_lock = AO2_LOCK_REQ_MUTEX;
- }
-
- if (tag) {
- __ao2_ref_debug(obj_new, +1, tag, file, line, func);
- } else {
- __ao2_ref(obj_new, +1);
- }
- node->obj = obj_new;
- node->my_container = self;
- node->my_bucket = i;
-
- /* Insert the new node. */
- res = 0;
- switch (hash_ao2_link_insert(self, &self->buckets[i], node)) {
- case AO2_CONTAINER_INSERT_NODE_INSERTED:
-#if defined(AST_DEVMODE)
- ++self->buckets[i].elements;
- if (self->buckets[i].max_elements < self->buckets[i].elements) {
- self->buckets[i].max_elements = self->buckets[i].elements;
- }
-#endif /* defined(AST_DEVMODE) */
- ast_atomic_fetchadd_int(&self->common.elements, 1);
-
- res = 1;
- break;
- case AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED:
- res = 1;
- /* Fall through */
- case AO2_CONTAINER_INSERT_NODE_REJECTED:
- node->my_container = NULL;
- ao2_ref(node, -1);
- break;
- }
-
- if (flags & OBJ_NOLOCK) {
- adjust_lock(self, orig_lock, 0);
- } else {
- ao2_unlock(self);
- }
-
- return res;
-}
-
-/*! Traversal state to restart a hash container traversal. */
-struct hash_traversal_state {
- /*! Active sort function in the traversal if not NULL. */
- ao2_sort_fn *sort_fn;
- /*! Node returned in the sorted starting hash bucket if OBJ_CONTINUE flag set. (Reffed) */
- struct hash_bucket_node *first_node;
- /*! Saved comparison callback arg pointer. */
- void *arg;
- /*! Starting hash bucket */
- int bucket_start;
- /*! Stopping hash bucket */
- int bucket_last;
- /*! Saved search flags to control traversing the container. */
- enum search_flags flags;
- /*! TRUE if it is a descending search */
- unsigned int descending:1;
- /*! TRUE if the starting bucket needs to be rechecked because of sorting skips. */
- unsigned int recheck_starting_bucket:1;
-};
-
-/*!
- * \internal
- * \brief Find the first hash container node in a traversal.
- * \since 11.0
- *
- * \param self Container to operate upon.
- * \param flags search_flags to control traversing the container
- * \param arg Comparison callback arg parameter.
- * \param state Traversal state to restart hash container traversal.
- *
- * \retval node-ptr of found node (Reffed).
- * \retval NULL when no node found.
- */
-static struct hash_bucket_node *hash_find_first(struct ao2_container_hash *self, enum search_flags flags, void *arg, struct hash_traversal_state *state)
-{
- struct hash_bucket_node *node;
- int bucket_cur;
- int cmp;
-
- memset(state, 0, sizeof(*state));
- state->arg = arg;
- state->flags = flags;
-
- /* Determine traversal order. */
- switch (flags & OBJ_ORDER_MASK) {
- case OBJ_ORDER_POST:
- case OBJ_ORDER_DESCENDING:
- state->descending = 1;
- break;
- case OBJ_ORDER_PRE:
- case OBJ_ORDER_ASCENDING:
- default:
- break;
- }
-
- /*
- * If lookup by pointer or search key, run the hash and optional
- * sort functions. Otherwise, traverse the whole container.
- */
- if ((flags & (OBJ_POINTER | OBJ_KEY))) {
- /* we know hash can handle this case */
- bucket_cur = self->hash_fn(arg, flags & (OBJ_POINTER | OBJ_KEY)) % self->n_buckets;
- state->sort_fn = self->common.sort_fn;
- } else {
- /* don't know, let's scan all buckets */
- bucket_cur = -1;
- state->sort_fn = (flags & OBJ_PARTIAL_KEY) ? self->common.sort_fn : NULL;
- }
-
- if (state->descending) {
- /*
- * Determine the search boundaries of a descending traversal.
- *
- * bucket_cur downto state->bucket_last
- */
- if (bucket_cur < 0) {
- bucket_cur = self->n_buckets - 1;
- state->bucket_last = 0;
- } else {
- state->bucket_last = bucket_cur;
- }
- if (flags & OBJ_CONTINUE) {
- state->bucket_last = 0;
- if (state->sort_fn) {
- state->recheck_starting_bucket = 1;
- }
- }
- state->bucket_start = bucket_cur;
-
- /* For each bucket */
- for (; state->bucket_last <= bucket_cur; --bucket_cur) {
- /* For each node in the bucket. */
- for (node = AST_DLLIST_LAST(&self->buckets[bucket_cur].list);
- node;
- node = AST_DLLIST_PREV(node, links)) {
- if (!node->obj) {
- /* Node is empty */
- continue;
- }
-
- if (state->sort_fn) {
- /* Filter node through the sort_fn */
- cmp = state->sort_fn(node->obj, arg,
- flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY));
- if (cmp > 0) {
- continue;
- }
- if (flags & OBJ_CONTINUE) {
- /* Remember first node when we wrap around. */
- __ao2_ref(node, +1);
- state->first_node = node;
-
- /* From now on all nodes are matching */
- state->sort_fn = NULL;
- } else if (cmp < 0) {
- /* No more nodes in this bucket are possible to match. */
- break;
- }
- }
-
- /* We have the first traversal node */
- __ao2_ref(node, +1);
- return node;
- }
-
- /* Was this the starting bucket? */
- if (bucket_cur == state->bucket_start
- && (flags & OBJ_CONTINUE)
- && flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
- /* In case the bucket was empty or none of the nodes matched. */
- state->sort_fn = NULL;
- }
-
- /* Was this the first container bucket? */
- if (bucket_cur == 0
- && (flags & OBJ_CONTINUE)
- && flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
- /* Move to the end to ensure we check every bucket */
- bucket_cur = self->n_buckets;
- state->bucket_last = state->bucket_start + 1;
- if (state->recheck_starting_bucket) {
- /*
- * We have to recheck the first part of the starting bucket
- * because of sorting skips.
- */
- --state->bucket_last;
- }
- }
- }
- } else {
- /*
- * Determine the search boundaries of an ascending traversal.
- *
- * bucket_cur to state->bucket_last-1
- */
- if (bucket_cur < 0) {
- bucket_cur = 0;
- state->bucket_last = self->n_buckets;
- } else {
- state->bucket_last = bucket_cur + 1;
- }
- if (flags & OBJ_CONTINUE) {
- state->bucket_last = self->n_buckets;
- if (state->sort_fn) {
- state->recheck_starting_bucket = 1;
- }
- }
- state->bucket_start = bucket_cur;
-
- /* For each bucket */
- for (; bucket_cur < state->bucket_last; ++bucket_cur) {
- /* For each node in the bucket. */
- for (node = AST_DLLIST_FIRST(&self->buckets[bucket_cur].list);
- node;
- node = AST_DLLIST_NEXT(node, links)) {
- if (!node->obj) {
- /* Node is empty */
- continue;
- }
-
- if (state->sort_fn) {
- /* Filter node through the sort_fn */
- cmp = state->sort_fn(node->obj, arg,
- flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY));
- if (cmp < 0) {
- continue;
- }
- if (flags & OBJ_CONTINUE) {
- /* Remember first node when we wrap around. */
- __ao2_ref(node, +1);
- state->first_node = node;
-
- /* From now on all nodes are matching */
- state->sort_fn = NULL;
- } else if (cmp > 0) {
- /* No more nodes in this bucket are possible to match. */
- break;
- }
- }
-
- /* We have the first traversal node */
- __ao2_ref(node, +1);
- return node;
- }
-
- /* Was this the starting bucket? */
- if (bucket_cur == state->bucket_start
- && (flags & OBJ_CONTINUE)
- && flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
- /* In case the bucket was empty or none of the nodes matched. */
- state->sort_fn = NULL;
- }
-
- /* Was this the last container bucket? */
- if (bucket_cur == self->n_buckets - 1
- && (flags & OBJ_CONTINUE)
- && flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
- /* Move to the beginning to ensure we check every bucket */
- bucket_cur = -1;
- state->bucket_last = state->bucket_start;
- if (state->recheck_starting_bucket) {
- /*
- * We have to recheck the first part of the starting bucket
- * because of sorting skips.
- */
- ++state->bucket_last;
- }
- }
- }
- }
-
- return NULL;
-}
-
-/*!
- * \internal
- * \brief Find the next hash container node in a traversal.
- * \since 11.0
- *
- * \param self Container to operate upon.
- * \param state Traversal state to restart hash container traversal.
- * \param prev Previous node returned by the traversal search functions.
- * The ref ownership is passed back to this function.
- *
- * \retval node-ptr of found node (Reffed).
- * \retval NULL when no node found.
- */
-static struct hash_bucket_node *hash_find_next(struct ao2_container_hash *self, struct hash_traversal_state *state, struct hash_bucket_node *prev)
-{
- struct hash_bucket_node *node;
- void *arg;
- enum search_flags flags;
- int bucket_cur;
- int cmp;
-
- arg = state->arg;
- flags = state->flags;
- bucket_cur = prev->my_bucket;
- node = prev;
-
- if (state->descending) {
- goto hash_descending_start;
-
- /* For each bucket */
- for (; state->bucket_last <= bucket_cur; --bucket_cur) {
- /* For each node in the bucket. */
- for (node = AST_DLLIST_LAST(&self->buckets[bucket_cur].list);
- node;
- node = AST_DLLIST_PREV(node, links)) {
- if (node == state->first_node) {
- /* We have wrapped back to the starting point. */
- __ao2_ref(prev, -1);
- return NULL;
- }
- if (!node->obj) {
- /* Node is empty */
- continue;
- }
-
- if (state->sort_fn) {
- /* Filter node through the sort_fn */
- cmp = state->sort_fn(node->obj, arg,
- flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY));
- if (cmp > 0) {
- continue;
- }
- if (cmp < 0) {
- /* No more nodes in this bucket are possible to match. */
- break;
- }
- }
-
- /* We have the next traversal node */
- __ao2_ref(node, +1);
-
- /*
- * Dereferencing the prev node may result in our next node
- * object being removed by another thread. This could happen if
- * the container uses RW locks and the container was read
- * locked.
- */
- __ao2_ref(prev, -1);
- if (node->obj) {
- return node;
- }
- prev = node;
-
-hash_descending_start:;
- }
-
- /* Was this the first container bucket? */
- if (bucket_cur == 0
- && (flags & OBJ_CONTINUE)
- && flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
- /* Move to the end to ensure we check every bucket */
- bucket_cur = self->n_buckets;
- state->bucket_last = state->bucket_start + 1;
- if (state->recheck_starting_bucket) {
- /*
- * We have to recheck the first part of the starting bucket
- * because of sorting skips.
- */
- --state->bucket_last;
- }
- }
- }
- } else {
- goto hash_ascending_start;
-
- /* For each bucket */
- for (; bucket_cur < state->bucket_last; ++bucket_cur) {
- /* For each node in the bucket. */
- for (node = AST_DLLIST_FIRST(&self->buckets[bucket_cur].list);
- node;
- node = AST_DLLIST_NEXT(node, links)) {
- if (node == state->first_node) {
- /* We have wrapped back to the starting point. */
- __ao2_ref(prev, -1);
- return NULL;
- }
- if (!node->obj) {
- /* Node is empty */
- continue;
- }
-
- if (state->sort_fn) {
- /* Filter node through the sort_fn */
- cmp = state->sort_fn(node->obj, arg,
- flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY));
- if (cmp < 0) {
- continue;
- }
- if (cmp > 0) {
- /* No more nodes in this bucket are possible to match. */
- break;
- }
- }
-
- /* We have the next traversal node */
- __ao2_ref(node, +1);
-
- /*
- * Dereferencing the prev node may result in our next node
- * object being removed by another thread. This could happen if
- * the container uses RW locks and the container was read
- * locked.
- */
- __ao2_ref(prev, -1);
- if (node->obj) {
- return node;
- }
- prev = node;
-
-hash_ascending_start:;
- }
-
- /* Was this the last container bucket? */
- if (bucket_cur == self->n_buckets - 1
- && (flags & OBJ_CONTINUE)
- && flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
- /* Move to the beginning to ensure we check every bucket */
- bucket_cur = -1;
- state->bucket_last = state->bucket_start;
- if (state->recheck_starting_bucket) {
- /*
- * We have to recheck the first part of the starting bucket
- * because of sorting skips.
- */
- ++state->bucket_last;
- }
- }
- }
- }
-
- __ao2_ref(prev, -1);
- return NULL;
-}
-
-/*!
- * \internal
- * \brief Cleanup the hash container traversal state.
- * \since 11.0
- *
- * \param state Traversal state to cleanup.
- *
- * \return Nothing
- */
-static void hash_find_cleanup(struct hash_traversal_state *state)
-{
- if (state->first_node) {
- __ao2_ref(state->first_node, -1);
- }
-}
-
-/*!
- * \brief Traverse the container.
- *
- * \details
- * Browse the container using different stategies accoding the flags.
- * Luckily, for debug purposes, the added args (tag, file, line, func)
- * aren't an excessive load to the system, as the callback should not be
- * called as often as, say, the ao2_ref func is called.
*
* \param self Container to operate upon.
* \param flags search_flags to control traversing the container
@@ -2139,21 +1067,26 @@
* flags parameter. The iterator must be destroyed with
* ao2_iterator_destroy() when the caller no longer needs it.
*/
-static void *hash_ao2_callback(struct ao2_container_hash *self, enum search_flags flags,
- void *cb_fn, void *arg, void *data, enum ao2_callback_type type, const char *tag,
- const char *file, int line, const char *func)
+static void *__ao2_traverse_internal(struct ao2_container *self, enum search_flags flags,
+ void *cb_fn, void *arg, void *data, enum ao2_callback_type type,
+ const char *tag, const char *file, int line, const char *func)
{
void *ret;
ao2_callback_fn *cb_default = NULL;
ao2_callback_data_fn *cb_withdata = NULL;
- struct hash_bucket_node *node;
- struct hash_traversal_state traversal_state;
+ struct ao2_container_node *node;
+ void *traversal_state;
enum ao2_lock_req orig_lock;
struct ao2_container *multi_container = NULL;
struct ao2_iterator *multi_iterator = NULL;
-/* BUGBUG pull the multi_container and locking code up into the generic container code. */
+ if (!INTERNAL_OBJ(self) || !self->v_table || !self->v_table->traverse_first
+ || !self->v_table->traverse_next) {
+ /* Sanity checks. */
+ return NULL;
+ }
+
/*
* This logic is used so we can support OBJ_MULTIPLE with OBJ_NODATA
* turned off. This if statement checks for the special condition
@@ -2178,12 +1111,22 @@
}
}
- /* Match everything if no callback match function provided. */
if (!cb_fn) {
- if (type == WITH_DATA) {
- cb_fn = cb_true_data;
+ /* Match everything if no callback match function provided. */
+ if (type == AO2_CALLBACK_WITH_DATA) {
+ cb_withdata = cb_true_data;
} else {
- cb_fn = cb_true;
+ cb_default = cb_true;
+ }
+ } else {
+ /*
+ * We do this here to avoid the per object casting penalty (even
+ * though that is probably optimized away anyway).
+ */
+ if (type == AO2_CALLBACK_WITH_DATA) {
+ cb_withdata = cb_fn;
+ } else {
+ cb_default = cb_fn;
}
}
@@ -2203,23 +1146,18 @@
}
}
- /* We do this here to avoid the per object casting penalty (even though
- that is probably optimized away anyway). */
- if (type == WITH_DATA) {
- cb_withdata = cb_fn;
- } else {
- cb_default = cb_fn;
- }
[... 1311 lines stripped ...]
More information about the asterisk-commits
mailing list