[svn-commits] rmudgett: branch rmudgett/ao2_enhancements r360760 - in /team/rmudgett/ao2_en...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Mar 28 22:13:20 CDT 2012


Author: rmudgett
Date: Wed Mar 28 22:13:16 2012
New Revision: 360760

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=360760
Log:
* Extracted hash container specific code into a virtual method table.

* Made hash container node store the external object pointer instead of
the internal pointer.  The stored internal pointer was only used to
convert it back to the external pointer.

* Made the list container a formal API call instead of a macro.

* Made __ao2_link() and __ao2_link_debug() return 0/1 instead of
NULL/!NULL.

* Rename astobj2 API parameter funcname to func.

* Rename astobj2 API iterator parameter to iter.

* Update some documentation for OBJ_MULTIPLE.

Modified:
    team/rmudgett/ao2_enhancements/include/asterisk/astobj2.h
    team/rmudgett/ao2_enhancements/main/astobj2.c

Modified: team/rmudgett/ao2_enhancements/include/asterisk/astobj2.h
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/ao2_enhancements/include/asterisk/astobj2.h?view=diff&rev=360760&r1=360759&r2=360760
==============================================================================
--- team/rmudgett/ao2_enhancements/include/asterisk/astobj2.h (original)
+++ team/rmudgett/ao2_enhancements/include/asterisk/astobj2.h Wed Mar 28 22:13:16 2012
@@ -462,7 +462,7 @@
 #endif
 
 void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag,
-	const char *file, int line, const char *funcname, int ref_debug);
+	const char *file, int line, const char *func, int ref_debug);
 void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options);
 
 /*! @} */
@@ -503,7 +503,7 @@
 
 #endif
 
-int __ao2_ref_debug(void *o, int delta, const char *tag, const char *file, int line, const char *funcname);
+int __ao2_ref_debug(void *o, int delta, const char *tag, const char *file, int line, const char *func);
 int __ao2_ref(void *o, int delta);
 
 /*! @} */
@@ -1020,7 +1020,7 @@
 	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn);
 struct ao2_container *__ao2_container_alloc_debug(unsigned int options,
 	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
-	const char *tag, const char *file, int line, const char *funcname, int ref_debug);
+	const char *tag, const char *file, int line, const char *func, int ref_debug);
 
 /*!
  * \brief Allocate and initialize a hash container with the desired number of buckets.
@@ -1065,11 +1065,13 @@
 
 #endif
 
-struct ao2_container *__ao2_container_alloc_hash(unsigned int ao2_options, unsigned int container_options,
-	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn);
-struct ao2_container *__ao2_container_alloc_hash_debug(unsigned int ao2_options, unsigned int container_options,
-	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn,
-	const char *tag, const char *file, int line, const char *funcname, int ref_debug);
+struct ao2_container *__ao2_container_alloc_hash(unsigned int ao2_options,
+	unsigned int container_options, unsigned int n_buckets, ao2_hash_fn *hash_fn,
+	ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn);
+struct ao2_container *__ao2_container_alloc_hash_debug(unsigned int ao2_options,
+	unsigned int container_options, unsigned int n_buckets, ao2_hash_fn *hash_fn,
+	ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn,
+	const char *tag, const char *file, int line, const char *func, int ref_debug);
 
 /*!
  * \brief Allocate and initialize a list container.
@@ -1085,10 +1087,35 @@
  * \note Destructor is set implicitly.
  * \note Implemented as a degenerate hash table.
  */
+
+#if defined(REF_DEBUG)
+
 #define ao2_t_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn, tag) \
-	ao2_t_container_alloc_hash((ao2_options), (container_options), 1, NULL, (sort_fn), (cmp_fn), (tag))
+	__ao2_container_alloc_list_debug((ao2_options), (container_options), (sort_fn), (cmp_fn), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
 #define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn) \
-	ao2_container_alloc_hash((ao2_options), (container_options), 1, NULL, (sort_fn), (cmp_fn))
+	__ao2_container_alloc_list_debug((ao2_options), (container_options), (sort_fn), (cmp_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+
+#elif defined(__AST_DEBUG_MALLOC)
+
+#define ao2_t_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn, tag) \
+	__ao2_container_alloc_list_debug((ao2_options), (container_options), (sort_fn), (cmp_fn), (tag),  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn) \
+	__ao2_container_alloc_list_debug((ao2_options), (container_options), (sort_fn), (cmp_fn), "",  __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+
+#else
+
+#define ao2_t_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn, tag) \
+	__ao2_container_alloc_list((ao2_options), (container_options), (sort_fn), (cmp_fn))
+#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn) \
+	__ao2_container_alloc_list((ao2_options), (container_options), (sort_fn), (cmp_fn))
+
+#endif
+
+struct ao2_container *__ao2_container_alloc_list(unsigned int ao2_options,
+	unsigned int container_options, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn);
+struct ao2_container *__ao2_container_alloc_list_debug(unsigned int ao2_options,
+	unsigned int container_options, ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn,
+	const char *tag, const char *file, int line, const char *func, int ref_debug);
 
 /*!
  * \brief Allocate and initialize a red-black tree container.
@@ -1131,7 +1158,7 @@
 	ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn);
 struct ao2_container *__ao2_container_alloc_tree_debug(unsigned int ao2_options, unsigned int container_options,
 	ao2_sort_fn *sort_fn, ao2_callback_fn *cmp_fn,
-	const char *tag, const char *file, int line, const char *funcname, int ref_debug);
+	const char *tag, const char *file, int line, const char *func, int ref_debug);
 
 /*! \brief
  * Returns the number of elements in a container.
@@ -1172,7 +1199,7 @@
  * \retval NULL on error.
  */
 struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags);
-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 *funcname, int ref_debug);
+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);
 #if defined(REF_DEBUG)
 
 #define ao2_t_container_clone(orig, flags, tag)	__ao2_container_clone_debug(orig, flags, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
@@ -1208,8 +1235,8 @@
  * \param flags search_flags to control linking the object.  (OBJ_NOLOCK)
  * \param tag used for debugging.
  *
- * \retval NULL on errors.
- * \retval newobj on success.
+ * \retval 0 on errors.
+ * \retval 1 on success.
  *
  * This function inserts an object in a container according its key.
  *
@@ -1236,8 +1263,8 @@
 
 #endif
 
-void *__ao2_link_debug(struct ao2_container *c, void *new_obj, int flags, const char *tag, const char *file, int line, const char *funcname);
-void *__ao2_link(struct ao2_container *c, void *newobj, int flags);
+int __ao2_link_debug(struct ao2_container *c, void *obj_new, int flags, const char *tag, const char *file, int line, const char *func);
+int __ao2_link(struct ao2_container *c, void *obj_new, int flags);
 
 /*!
  * \brief Remove an object from a container
@@ -1275,7 +1302,7 @@
 
 #endif
 
-void *__ao2_unlink_debug(struct ao2_container *c, void *obj, int flags, const char *tag, const char *file, int line, const char *funcname);
+void *__ao2_unlink_debug(struct ao2_container *c, void *obj, int flags, const char *tag, const char *file, int line, const char *func);
 void *__ao2_unlink(struct ao2_container *c, void *obj, int flags);
 
 
@@ -1311,12 +1338,15 @@
  *         also used by ao2_callback).
  * \param arg passed to the callback.
  * \param tag used for debugging.
- * \return when OBJ_MULTIPLE is not included in the flags parameter,
- *         the return value will be either the object found or NULL if no
- *         no matching object was found.  If OBJ_MULTIPLE is included,
- *         the return value will be a pointer to an ao2_iterator object,
- *         which must be destroyed with ao2_iterator_destroy() when the
- *         caller no longer needs it.
+ *
+ * \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.
  *
  * If the function returns any objects, their refcount is incremented,
  * and the caller is in charge of decrementing them once done.
@@ -1375,7 +1405,7 @@
 
 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 *funcname);
+	const char *func);
 void *__ao2_callback(struct ao2_container *c, enum search_flags flags, ao2_callback_fn *cb_fn, void *arg);
 
 /*! @} */
@@ -1413,7 +1443,7 @@
 
 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 *funcname);
+	int line, const char *func);
 void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
 	ao2_callback_data_fn *cb_fn, void *arg, void *data);
 
@@ -1437,7 +1467,7 @@
 #endif
 
 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 *funcname);
+	const char *tag, const char *file, int line, const char *func);
 void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags);
 
 /*! \brief
@@ -1593,7 +1623,7 @@
 /*!
  * \brief Destroy a container iterator
  *
- * \param i the iterator to destroy
+ * \param iter the iterator to destroy
  *
  * \retval none
  *
@@ -1602,9 +1632,9 @@
  *
  */
 #if defined(TEST_FRAMEWORK)
-void ao2_iterator_destroy(struct ao2_iterator *i) __attribute__((noinline));
+void ao2_iterator_destroy(struct ao2_iterator *iter) __attribute__((noinline));
 #else
-void ao2_iterator_destroy(struct ao2_iterator *i);
+void ao2_iterator_destroy(struct ao2_iterator *iter);
 #endif
 #ifdef REF_DEBUG
 
@@ -1618,8 +1648,8 @@
 
 #endif
 
-void *__ao2_iterator_next_debug(struct ao2_iterator *a, const char *tag, const char *file, int line, const char *funcname);
-void *__ao2_iterator_next(struct ao2_iterator *a);
+void *__ao2_iterator_next_debug(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func);
+void *__ao2_iterator_next(struct ao2_iterator *iter);
 
 /* extra functions */
 void ao2_bt(void);	/* backtrace */

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=360760&r1=360759&r2=360760
==============================================================================
--- team/rmudgett/ao2_enhancements/main/astobj2.c (original)
+++ team/rmudgett/ao2_enhancements/main/astobj2.c Wed Mar 28 22:13:16 2012
@@ -154,11 +154,6 @@
 
 	return p;
 }
-
-enum ao2_callback_type {
-	DEFAULT,
-	WITH_DATA,
-};
 
 /*!
  * \brief convert from a pointer _p to an astobj2 object
@@ -414,7 +409,7 @@
 	return NULL;
 }
 
-static int internal_ao2_ref(void *user_data, int delta, const char *file, int line, const char *funcname)
+static int internal_ao2_ref(void *user_data, int delta, const char *file, int line, const char *func)
 {
 	struct astobj2 *obj = INTERNAL_OBJ(user_data);
 	struct astobj2_lock *obj_mutex;
@@ -446,7 +441,7 @@
 
 	/* this case must never happen */
 	if (current_value < 0) {
-		ast_log(__LOG_ERROR, file, line, funcname,
+		ast_log(__LOG_ERROR, file, line, func,
 			"Invalid refcount %d on ao2 object %p\n", current_value, user_data);
 	}
 
@@ -495,7 +490,7 @@
 		ast_free(obj);
 		break;
 	default:
-		ast_log(__LOG_ERROR, file, line, funcname,
+		ast_log(__LOG_ERROR, file, line, func,
 			"Invalid lock option on ao2 object %p\n", user_data);
 		break;
 	}
@@ -503,7 +498,7 @@
 	return ret;
 }
 
-int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *file, int line, const char *funcname)
+int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *file, int line, const char *func)
 {
 	struct astobj2 *obj = INTERNAL_OBJ(user_data);
 
@@ -514,18 +509,18 @@
 		FILE *refo = fopen(REF_FILE, "a");
 		if (refo) {
 			fprintf(refo, "%p %s%d   %s:%d:%s (%s) [@%d]\n", user_data, (delta < 0 ? "" : "+"),
-				delta, file, line, funcname, tag, obj ? obj->priv_data.ref_counter : -1);
+				delta, file, line, func, tag, obj ? obj->priv_data.ref_counter : -1);
 			fclose(refo);
 		}
 	}
 	if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */
 		FILE *refo = fopen(REF_FILE, "a");
 		if (refo) {
-			fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag);
+			fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, func, tag);
 			fclose(refo);
 		}
 	}
-	return internal_ao2_ref(user_data, delta, file, line, funcname);
+	return internal_ao2_ref(user_data, delta, file, line, func);
 }
 
 int __ao2_ref(void *user_data, int delta)
@@ -538,7 +533,7 @@
 	return internal_ao2_ref(user_data, delta, __FILE__, __LINE__, __FUNCTION__);
 }
 
-static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *file, int line, const char *funcname)
+static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *file, int line, const char *func)
 {
 	/* allocation */
 	struct astobj2 *obj;
@@ -556,7 +551,7 @@
 	switch (options & AO2_ALLOC_OPT_LOCK_MASK) {
 	case AO2_ALLOC_OPT_LOCK_MUTEX:
 #if defined(__AST_DEBUG_MALLOC)
-		obj_mutex = __ast_calloc(1, sizeof(*obj_mutex) + data_size, file, line, funcname);
+		obj_mutex = __ast_calloc(1, sizeof(*obj_mutex) + data_size, file, line, func);
 #else
 		obj_mutex = ast_calloc(1, sizeof(*obj_mutex) + data_size);
 #endif
@@ -569,7 +564,7 @@
 		break;
 	case AO2_ALLOC_OPT_LOCK_RWLOCK:
 #if defined(__AST_DEBUG_MALLOC)
-		obj_rwlock = __ast_calloc(1, sizeof(*obj_rwlock) + data_size, file, line, funcname);
+		obj_rwlock = __ast_calloc(1, sizeof(*obj_rwlock) + data_size, file, line, func);
 #else
 		obj_rwlock = ast_calloc(1, sizeof(*obj_rwlock) + data_size);
 #endif
@@ -582,7 +577,7 @@
 		break;
 	case AO2_ALLOC_OPT_LOCK_NOLOCK:
 #if defined(__AST_DEBUG_MALLOC)
-		obj = __ast_calloc(1, sizeof(*obj) + data_size, file, line, funcname);
+		obj = __ast_calloc(1, sizeof(*obj) + data_size, file, line, func);
 #else
 		obj = ast_calloc(1, sizeof(*obj) + data_size);
 #endif
@@ -592,7 +587,7 @@
 		break;
 	default:
 		/* Invalid option value. */
-		ast_log(__LOG_DEBUG, file, line, funcname, "Invalid lock option requested\n");
+		ast_log(__LOG_DEBUG, file, line, func, "Invalid lock option requested\n");
 		return NULL;
 	}
 
@@ -614,18 +609,18 @@
 }
 
 void *__ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options, const char *tag,
-	const char *file, int line, const char *funcname, int ref_debug)
+	const char *file, int line, const char *func, int ref_debug)
 {
 	/* allocation */
 	void *obj;
 	FILE *refo;
 
-	if ((obj = internal_ao2_alloc(data_size, destructor_fn, options, file, line, funcname)) == NULL) {
+	if ((obj = internal_ao2_alloc(data_size, destructor_fn, options, file, line, func)) == NULL) {
 		return NULL;
 	}
 
 	if (ref_debug && (refo = fopen(REF_FILE, "a"))) {
-		fprintf(refo, "%p =1   %s:%d:%s (%s)\n", obj, file, line, funcname, tag);
+		fprintf(refo, "%p =1   %s:%d:%s (%s)\n", obj, file, line, func, tag);
 		fclose(refo);
 	}
 
@@ -712,11 +707,167 @@
 }
 
 
-/* internal callback to destroy a container. */
-static void container_destruct(void *c);
-
-/* internal callback to destroy a container. */
-static void container_destruct_debug(void *c);
+/* BUGBUG may not be needed */
+enum ao2_container_type {
+	AO2_CONTAINER_TYPE_HASH,
+	AO2_CONTAINER_TYPE_LIST = 0,/* This is a degenerate hash container. */
+	AO2_CONTAINER_TYPE_TREE,
+};
+
+enum ao2_callback_type {
+	DEFAULT,
+	WITH_DATA,
+};
+
+/*!
+ * \brief Destroy this container.
+ *
+ * \param self Container to operate upon.
+ *
+ * \return Nothing
+ */
+typedef void (*ao2_container_destroy_fn)(struct ao2_container *self);
+
+/*!
+ * \brief Create an empty copy of this container.
+ *
+ * \param self Container to operate upon.
+ *
+ * \retval empty-container on success.
+ * \retval NULL on error.
+ */
+typedef struct ao2_container *(*ao2_container_alloc_empty_clone_fn)(struct ao2_container *self);
+
+/*!
+ * \brief Create an empty copy of this container. (Debug version)
+ *
+ * \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-container on success.
+ * \retval NULL on error.
+ */
+typedef struct ao2_container *(*ao2_container_alloc_empty_clone_debug_fn)(struct ao2_container *self, const char *tag, const char *file, int line, const char *func, int ref_debug);
+
+/*!
+ * \brief Link an object into this container.
+ *
+ * \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.
+ */
+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.
+ *
+ * \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
+ * \brief Find the next iteration element in the container.
+ *
+ * \param self Container to operate upon.
+ * \param iter The iterator 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
+ *
+ * \note The iterator container is already locked.
+ *
+ * \retval next-object on success.
+ * \retval NULL on error or no more elements in the container.
+ */
+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);
+
+/*! Container virtual methods template. */
+struct container_methods {
+	/*! Destroy this container. */
+	ao2_container_destroy_fn destroy;
+	/*! \brief Create an empty copy of this container. */
+	ao2_container_alloc_empty_clone_fn alloc_empty_clone;
+	/*! \brief Create an empty copy of this container. (Debug version) */
+	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;
+	/*! Find the next iteration element in the container. */
+	ao2_iterator_next_fn iterator_next;
+};
+
+/*!
+ * \brief Generic container type.
+ *
+ * \details This is the base container type that contains values
+ * common to all container types.
+ *
+ * \todo Linking and unlinking container objects is typically
+ * expensive, as it involves a malloc()/free() of a small object
+ * which is very inefficient.  To optimize this, we can allocate
+ * larger arrays of container nodes 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 anyway).
+ */
+struct ao2_container {
+	/*! Container virtual method table. */
+	const struct container_methods *v_table;
+	/*! Container sort function if the container is sorted. */
+	ao2_sort_fn *sort_fn;
+	/*! Container traversal matching function for ao2_find. */
+	ao2_callback_fn *cmp_fn;
+	/*! The container option flags */
+	uint32_t options;
+	/*! Number of elements in the container. */
+	int elements;
+	/*! Type of container. BUGBUG may not be needed. */
+	enum ao2_container_type type;
+	/*!
+	 * \brief TRUE if the container is being destroyed.
+	 *
+	 * \note The destruction traversal should override any requested
+	 * search order to do the most efficient order for destruction.
+	 *
+	 * \note There should not be any empty nodes in the container
+	 * during destruction.  If there are then an error needs to be
+	 * issued about container node reference leaks.  Someone forgot
+	 * to call ao2_iterator_destroy() somewhere.
+	 */
+	unsigned int destroying:1;
+};
 
 /*!
  * A structure to create a linked list of entries,
@@ -725,41 +876,37 @@
  */
 struct bucket_entry {
 	AST_LIST_ENTRY(bucket_entry) entry;
+	/*! Stored object in node. */
+	void *obj;
 	int version;
-	struct astobj2 *astobj;/* pointer to internal data */
 };
 
+/*! BUGBUG change to a doubly linked list to support traverse order options and ref counted nodes. */
 /* each bucket in the container is a tailq. */
 AST_LIST_HEAD_NOLOCK(bucket, bucket_entry);
 
 /*!
- * 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_entry'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 {
+ * A hash container in addition to values common to all
+ * container types, stores the hash callback function,
+ * information on the number of hash buckets, 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'.
+ */
+struct ao2_container_hash {
+	struct ao2_container common;
+/*! BUGBUG struct ao2_container_hash need to convert to ref counted nodes */
 	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 */
@@ -767,70 +914,6 @@
 };
 
 /*!
- * \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!
- */
-static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *c,
-	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn)
-{
-	/* XXX maybe consistency check on arguments ? */
-	/* compute the container size */
-
-	if (!c) {
-		return NULL;
-	}
-
-	c->version = 1;	/* 0 is a reserved value here */
-	c->n_buckets = hash_fn ? n_buckets : 1;
-	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;
-}
-
-struct ao2_container *__ao2_container_alloc_debug(unsigned int options,
-	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn,
-	const char *tag, const char *file, int line, const char *funcname, int ref_debug)
-{
-	/* XXX maybe consistency check on arguments ? */
-	/* compute the container size */
-	unsigned int num_buckets = hash_fn ? n_buckets : 1;
-	size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
-	struct ao2_container *c = __ao2_alloc_debug(container_size, container_destruct_debug, options, tag, file, line, funcname, ref_debug);
-
-	return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
-}
-
-struct ao2_container *__ao2_container_alloc(unsigned int options,
-	unsigned int n_buckets, ao2_hash_fn *hash_fn, ao2_callback_fn *cmp_fn)
-{
-	/* XXX maybe consistency check on arguments ? */
-	/* compute the container size */
-	const unsigned int num_buckets = hash_fn ? n_buckets : 1;
-	size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
-	struct ao2_container *c = __ao2_alloc(container_size, container_destruct, options);
-
-	return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
-}
-
-/*!
  * return the number of elements in the container
  */
 int ao2_container_count(struct ao2_container *c)
@@ -841,25 +924,20 @@
 /*
  * link an object to a container
  */
-static struct bucket_entry *internal_ao2_link(struct ao2_container *c, void *user_data, int flags, const char *tag, const char *file, int line, const char *funcname)
+static int hash_ao2_link(struct ao2_container_hash *c, void *user_data, int flags, const char *tag, const char *file, int line, const char *func)
 {
 	int i;
 	enum ao2_lock_req orig_lock;
 	/* create a new list entry */
 	struct bucket_entry *p;
-	struct astobj2 *obj = INTERNAL_OBJ(user_data);
-
-	if (obj == NULL) {
-		return NULL;
-	}
-
-	if (INTERNAL_OBJ(c) == NULL) {
-		return NULL;
-	}
+/*! BUGBUG hash_ao2_link() need to convert to ref counted nodes */
+/*! BUGBUG hash_ao2_link() need to add sorting support */
+/*! BUGBUG hash_ao2_link() need to add insert option support */
+/*! BUGBUG hash_ao2_link() need to add duplicate handling option support */
 
 	p = ast_calloc(1, sizeof(*p));
 	if (!p) {
-		return NULL;
+		return 0;
 	}
 
 	i = abs(c->hash_fn(user_data, OBJ_POINTER));
@@ -872,13 +950,13 @@
 	}
 
 	i %= c->n_buckets;
-	p->astobj = obj;
+	p->obj = user_data;
 	p->version = ast_atomic_fetchadd_int(&c->version, 1);
 	AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
-	ast_atomic_fetchadd_int(&c->elements, 1);
+	ast_atomic_fetchadd_int(&c->common.elements, 1);
 
 	if (tag) {
-		__ao2_ref_debug(user_data, +1, tag, file, line, funcname);
+		__ao2_ref_debug(user_data, +1, tag, file, line, func);
 	} else {
 		__ao2_ref(user_data, +1);
 	}
@@ -889,17 +967,25 @@
 		ao2_unlock(c);
 	}
 
-	return p;
-}
-
-void *__ao2_link_debug(struct ao2_container *c, void *new_obj, int flags, const char *tag, const char *file, int line, const char *funcname)
-{
-	return internal_ao2_link(c, new_obj, flags, tag, file, line, funcname);
-}
-
-void *__ao2_link(struct ao2_container *c, void *new_obj, int flags)
-{
-	return internal_ao2_link(c, new_obj, flags, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
+	return 1;
+}
+
+int __ao2_link_debug(struct ao2_container *c, void *obj_new, int flags, const char *tag, const char *file, int line, const char *func)
+{
+	if (!INTERNAL_OBJ(obj_new) || !INTERNAL_OBJ(c) || !c->v_table || !c->v_table->link) {
+		/* Sanity checks. */
+		return 0;
+	}
+	return c->v_table->link(c, obj_new, flags, tag, file, line, func);
+}
+
+int __ao2_link(struct ao2_container *c, void *obj_new, int flags)
+{
+	if (!INTERNAL_OBJ(obj_new) || !INTERNAL_OBJ(c) || !c->v_table || !c->v_table->link) {
+		/* Sanity checks. */
+		return 0;
+	}
+	return c->v_table->link(c, obj_new, flags, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
 }
 
 /*!
@@ -915,21 +1001,23 @@
  * and destroy the associated * bucket_entry structure.
  */
 void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags,
-	const char *tag, const char *file, int line, const char *funcname)
-{
-	if (INTERNAL_OBJ(user_data) == NULL) {	/* safety check on the argument */
+	const char *tag, const char *file, int line, const char *func)
+{
+	if (!INTERNAL_OBJ(user_data)) {
+		/* Sanity checks. */
 		return NULL;
 	}
 
 	flags |= (OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA);
-	__ao2_callback_debug(c, flags, ao2_match_by_addr, user_data, tag, file, line, funcname);
+	__ao2_callback_debug(c, flags, ao2_match_by_addr, user_data, tag, file, line, func);
 
 	return NULL;
 }
 
 void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags)
 {
-	if (INTERNAL_OBJ(user_data) == NULL) {	/* safety check on the argument */
+	if (!INTERNAL_OBJ(user_data)) {
+		/* Sanity checks. */
 		return NULL;
 	}
 
@@ -959,13 +1047,13 @@
  * Browse the container using different stategies accoding the flags.
  * \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is
  * specified.
- * Luckily, for debug purposes, the added args (tag, file, line, funcname)
+ * 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.
  */
-static void *internal_ao2_callback(struct ao2_container *c, enum search_flags flags,
+static void *hash_ao2_callback(struct ao2_container_hash *c, 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 *funcname)
+	const char *file, int line, const char *func)
 {
 	int i, start, last;	/* search boundaries */
 	enum ao2_lock_req orig_lock;
@@ -975,10 +1063,9 @@
 	struct ao2_container *multi_container = NULL;
 	struct ao2_iterator *multi_iterator = NULL;
 
-	if (INTERNAL_OBJ(c) == NULL) {	/* safety check on the argument */
-		return NULL;
-	}
-
+/*! BUGBUG hash_ao2_callback() need to convert to ref counted nodes */
+/*! BUGBUG hash_ao2_callback() need to add sorting support */
+/*! BUGBUG hash_ao2_callback() need to add traverse order option support */
 	/*
 	 * This logic is used so we can support OBJ_MULTIPLE with OBJ_NODATA
 	 * turned off.  This if statement checks for the special condition
@@ -992,7 +1079,8 @@
 		 * is destroyed, the container will be automatically
 		 * destroyed as well.
 		 */
-		multi_container = __ao2_container_alloc(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
+		multi_container = __ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL,
+			NULL);
 		if (!multi_container) {
 			return NULL;
 		}
@@ -1067,9 +1155,9 @@
 			int match = (CMP_MATCH | CMP_STOP);
 
 			if (type == WITH_DATA) {
-				match &= cb_withdata(EXTERNAL_OBJ(cur->astobj), arg, data, flags);
+				match &= cb_withdata(cur->obj, arg, data, flags);
 			} else {
-				match &= cb_default(EXTERNAL_OBJ(cur->astobj), arg, flags);
+				match &= cb_default(cur->obj, arg, flags);
 			}
 
 			/* we found the object, performing operations according flags */
@@ -1083,12 +1171,13 @@
 			/* 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);
+				ret = cur->obj;
 				if (!(flags & (OBJ_UNLINK | OBJ_MULTIPLE))) {
-					if (tag)
-						__ao2_ref_debug(ret, 1, tag, file, line, funcname);
-					else
+					if (tag) {
+						__ao2_ref_debug(ret, 1, tag, file, line, func);
+					} else {
 						__ao2_ref(ret, 1);
+					}
 				}
 			}
 
@@ -1097,7 +1186,7 @@
 			 */
 			if (ret && (multi_container != NULL)) {
 				if (tag) {
-					__ao2_link_debug(multi_container, ret, flags, tag, file, line, funcname);
+					__ao2_link_debug(multi_container, ret, flags, tag, file, line, func);
 				} else {
 					__ao2_link(multi_container, ret, flags);
 				}
@@ -1109,7 +1198,7 @@
 				ast_atomic_fetchadd_int(&c->version, 1);
 				AST_LIST_REMOVE_CURRENT(entry);
 				/* update number of elements */
-				ast_atomic_fetchadd_int(&c->elements, -1);
+				ast_atomic_fetchadd_int(&c->common.elements, -1);
 
 				/* - When unlinking and not returning the result, (OBJ_NODATA), the ref from the container
 				 * must be decremented.
@@ -1118,10 +1207,11 @@
 				 * returned in a new container that already holds its own ref for the object. If the ref
 				 * from the original container is not accounted for here a memory leak occurs. */
 				if (flags & (OBJ_NODATA | OBJ_MULTIPLE)) {
-					if (tag)
-						__ao2_ref_debug(EXTERNAL_OBJ(cur->astobj), -1, tag, file, line, funcname);
-					else
-						__ao2_ref(EXTERNAL_OBJ(cur->astobj), -1);
+					if (tag) {
+						__ao2_ref_debug(cur->obj, -1, tag, file, line, func);
+					} else {
+						__ao2_ref(cur->obj, -1);
+					}
 				}
 				ast_free(cur);	/* free the link record */
 			}
@@ -1165,39 +1255,55 @@
 
 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 *funcname)
-{
-	return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, tag, file, line, funcname);
+	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)
 {
-	return internal_ao2_callback(c,flags, cb_fn, arg, NULL, DEFAULT, NULL, NULL, 0, NULL);
+	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 *funcname)
-{
-	return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, tag, file, line, funcname);
+	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)
 {
-	return internal_ao2_callback(c, flags, cb_fn, arg, data, WITH_DATA, NULL, NULL, 0, NULL);
+	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 *funcname)
+	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, funcname);
+	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)
@@ -1225,59 +1331,204 @@
 /*!
  * destroy an iterator
  */
-void ao2_iterator_destroy(struct ao2_iterator *i)
-{
-	ao2_ref(i->c, -1);
-	if (i->flags & AO2_ITERATOR_MALLOCD) {
-		ast_free(i);
+void ao2_iterator_destroy(struct ao2_iterator *iter)
+{
+	ao2_ref(iter->c, -1);
+	if (iter->flags & AO2_ITERATOR_MALLOCD) {
+		ast_free(iter);
 	} else {
-		i->c = NULL;
+		iter->c = NULL;
 	}
 }
 
 /*
  * move to the next element in the container.
  */
-static void *internal_ao2_iterator_next(struct ao2_iterator *a, const char *tag, const char *file, int line, const char *funcname)
+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);
+

[... 502 lines stripped ...]



More information about the svn-commits mailing list