[asterisk-commits] rmudgett: trunk r357145 - in /trunk: include/asterisk/ main/ tests/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Feb 27 18:42:42 CST 2012


Author: rmudgett
Date: Mon Feb 27 18:42:38 2012
New Revision: 357145

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=357145
Log:
Add ability to clone ao2 containers.

Occasionally there is a need to put all objects in one container also into
another container.

Some reasons you might need to do this:

1) You need to reconfigure a container.  You would do this by creating a
new container with the new configuration and ao2_container_dup the old
container into it.  Then replace the old container with the new.  Then
destroy the old container.

2) You need the contents of a container to remain stable while operating
on all of the objects.  You would do this by creating a cloned container
of the original with ao2_container_clone.  The cloned container is a
snapshot of the objects at the time of the cloning.  When done, just
destroy the cloned container.

Review: https://reviewboard.asterisk.org/r/1746/

Modified:
    trunk/include/asterisk/astobj2.h
    trunk/main/astobj2.c
    trunk/tests/test_astobj2.c

Modified: trunk/include/asterisk/astobj2.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/astobj2.h?view=diff&rev=357145&r1=357144&r2=357145
==============================================================================
--- trunk/include/asterisk/astobj2.h (original)
+++ trunk/include/asterisk/astobj2.h Mon Feb 27 18:42:38 2012
@@ -776,6 +776,58 @@
  */
 int ao2_container_count(struct ao2_container *c);
 
+/*!
+ * \brief Copy all object references in the src container into the dest container.
+ * \since 11.0
+ *
+ * \param dest Container to copy src object references into.
+ * \param src Container to copy all object references from.
+ * \param flags OBJ_NOLOCK if a lock is already held on both containers.
+ *    Otherwise, the src container is locked first.
+ *
+ * \pre The dest container must be empty.  If the duplication fails, the
+ * dest container will be returned empty.
+ *
+ * \note This can potentially be expensive because a malloc is
+ * needed for every object in the src container.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags);
+
+/*!
+ * \brief Create a clone/copy of the given container.
+ * \since 11.0
+ *
+ * \param orig Container to copy all object references from.
+ * \param flags OBJ_NOLOCK if a lock is already held on the container.
+ *
+ * \note This can potentially be expensive because a malloc is
+ * needed for every object in the orig container.
+ *
+ * \retval Clone container on success.
+ * \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, char *file, int line, const char *funcname, 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)
+#define ao2_container_clone(orig, flags)		__ao2_container_clone_debug(orig, flags, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 1)
+
+#elif defined(__AST_DEBUG_MALLOC)
+
+#define ao2_t_container_clone(orig, flags, tag)	__ao2_container_clone_debug(orig, flags, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+#define ao2_container_clone(orig, flags)		__ao2_container_clone_debug(orig, flags, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, 0)
+
+#else
+
+#define ao2_t_container_clone(orig, flags, tag)	__ao2_container_clone(orig, flags)
+#define ao2_container_clone(orig, flags)		__ao2_container_clone(orig, flags)
+
+#endif
+
 /*@} */
 
 /*! \name Object Management

Modified: trunk/main/astobj2.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/astobj2.c?view=diff&rev=357145&r1=357144&r2=357145
==============================================================================
--- trunk/main/astobj2.c (original)
+++ trunk/main/astobj2.c Mon Feb 27 18:42:38 2012
@@ -26,6 +26,11 @@
 #include "asterisk/utils.h"
 #include "asterisk/cli.h"
 #define REF_FILE "/tmp/refs"
+
+#if defined(TEST_FRAMEWORK)
+/* We are building with the test framework enabled so enable AO2 debug tests as well. */
+#define AO2_DEBUG 1
+#endif	/* defined(TEST_FRAMEWORK) */
 
 /*!
  * astobj2 objects are always preceded by this data structure,
@@ -1007,6 +1012,109 @@
 #endif
 }
 
+/*!
+ * \internal
+ * \brief Put obj into the arg container.
+ * \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_lock(src);
+		ao2_lock(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. */
+	clone = __ao2_container_alloc(orig->n_buckets, orig->hash_fn, orig->cmp_fn);
+	if (!clone) {
+		return NULL;
+	}
+
+	if (flags & OBJ_NOLOCK) {
+		ao2_lock(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, char *file, int line, const char *funcname, int ref_debug)
+{
+	struct ao2_container *clone;
+	int failed;
+
+	/* Create the clone container with the same properties as the original. */
+	clone = __ao2_container_alloc_debug(orig->n_buckets, orig->hash_fn, orig->cmp_fn, tag,
+		file, line, funcname, ref_debug);
+	if (!clone) {
+		return NULL;
+	}
+
+	if (flags & OBJ_NOLOCK) {
+		ao2_lock(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, funcname);
+		} else {
+			__ao2_ref(clone, -1);
+		}
+		clone = NULL;
+	}
+	return clone;
+}
+
 #ifdef AO2_DEBUG
 static int print_cb(void *obj, void *arg, int flag)
 {
@@ -1045,6 +1153,7 @@
 static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct ao2_container *c1;
+	struct ao2_container *c2;
 	int i, lim;
 	char *obj;
 	static int prof_id = -1;
@@ -1099,8 +1208,17 @@
 		 */
 		ao2_t_ref(obj, -1, "test");
 	}
+
 	ast_cli(a->fd, "testing callbacks\n");
 	ao2_t_callback(c1, 0, print_cb, a, "test callback");
+
+	ast_cli(a->fd, "testing container cloning\n");
+	c2 = ao2_container_clone(c1, 0);
+	if (ao2_container_count(c1) != ao2_container_count(c2)) {
+		ast_cli(a->fd, "Cloned container does not have the same number of objects!\n");
+	}
+	ao2_t_callback(c2, 0, print_cb, a, "test callback");
+
 	ast_cli(a->fd, "testing iterators, remove every second object\n");
 	{
 		struct ao2_iterator ai;
@@ -1122,6 +1240,7 @@
 		}
 		ao2_iterator_destroy(&ai);
 	}
+
 	ast_cli(a->fd, "testing callbacks again\n");
 	ao2_t_callback(c1, 0, print_cb, a, "test callback");
 
@@ -1130,6 +1249,7 @@
 
 	ast_cli(a->fd, "destroy container\n");
 	ao2_t_ref(c1, -1, "");	/* destroy container */
+	ao2_t_ref(c2, -1, "");	/* destroy container */
 	handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
 	return CLI_SUCCESS;
 }

Modified: trunk/tests/test_astobj2.c
URL: http://svnview.digium.com/svn/asterisk/trunk/tests/test_astobj2.c?view=diff&rev=357145&r1=357144&r2=357145
==============================================================================
--- trunk/tests/test_astobj2.c (original)
+++ trunk/tests/test_astobj2.c Mon Feb 27 18:42:38 2012
@@ -109,9 +109,11 @@
 {
 	struct ao2_container *c1;
 	struct ao2_container *c2;
+	struct ao2_container *c3 = NULL;
 	struct ao2_iterator it;
 	struct ao2_iterator *mult_it;
 	struct test_obj *obj;
+	struct test_obj *obj2;
 	struct test_obj tmp_obj;
 	int bucket_size;
 	int increment = 0;
@@ -153,6 +155,44 @@
 	}
 
 	ast_test_status_update(test, "Container created: random bucket size %d: number of items: %d\n", bucket_size, lim);
+
+	/* Testing ao2_container_clone */
+	c3 = ao2_container_clone(c1, 0);
+	if (!c3) {
+		ast_test_status_update(test, "ao2_container_clone failed.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	if (ao2_container_count(c1) != ao2_container_count(c3)) {
+		ast_test_status_update(test, "Cloned container does not have the same number of objects.\n");
+		res = AST_TEST_FAIL;
+	} else {
+		it = ao2_iterator_init(c1, 0);
+		for (; (obj = ao2_t_iterator_next(&it, "test orig")); ao2_t_ref(obj, -1, "test orig")) {
+			/*
+			 * Unlink the matching object from the cloned container to make
+			 * the next search faster.  This is a big speed optimization!
+			 * It reduces the container with 100000 objects test time from
+			 * 18 seconds to 200 ms.
+			 */
+			obj2 = ao2_t_callback(c3, OBJ_POINTER | OBJ_UNLINK, ao2_match_by_addr, obj,
+				"test clone");
+			if (obj2) {
+				ao2_t_ref(obj2, -1, "test clone");
+				continue;
+			}
+			ast_test_status_update(test,
+				"Orig container has an object %p not in the clone container.\n", obj);
+			res = AST_TEST_FAIL;
+		}
+		ao2_iterator_destroy(&it);
+		if (ao2_container_count(c3)) {
+			ast_test_status_update(test, "Cloned container still has objects.\n");
+			res = AST_TEST_FAIL;
+		}
+	}
+	ao2_t_ref(c3, -1, "bye c3");
+	c3 = NULL;
 
 	/* Testing ao2_find with no flags */
 	num = 100;
@@ -335,6 +375,9 @@
 	}
 	if (c2) {
 		ao2_t_ref(c2, -1, "bye c2");
+	}
+	if (c3) {
+		ao2_t_ref(c3, -1, "bye c3");
 	}
 
 	if (destructor_count > 0) {




More information about the asterisk-commits mailing list