[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