[asterisk-commits] rmudgett: branch rmudgett/ao2_enhancements r371233 - in /team/rmudgett/ao2_en...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Aug 13 16:18:03 CDT 2012


Author: rmudgett
Date: Mon Aug 13 16:17:59 2012
New Revision: 371233

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=371233
Log:
Add hash container integrity checks to the container unit tests.

Modified:
    team/rmudgett/ao2_enhancements/main/astobj2.c
    team/rmudgett/ao2_enhancements/tests/test_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=371233&r1=371232&r2=371233
==============================================================================
--- team/rmudgett/ao2_enhancements/main/astobj2.c (original)
+++ team/rmudgett/ao2_enhancements/main/astobj2.c Mon Aug 13 16:17:59 2012
@@ -2030,7 +2030,8 @@
 	 */
 	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;
+		bucket_cur = abs(self->hash_fn(arg, flags & (OBJ_POINTER | OBJ_KEY)));
+		bucket_cur %= self->n_buckets;
 		state->sort_fn = self->common.sort_fn;
 	} else {
 		/* don't know, let's scan all buckets */
@@ -2602,6 +2603,126 @@
 }
 #endif	/* defined(AST_DEVMODE) */
 
+#if defined(AST_DEVMODE)
+/*!
+ * \internal
+ * \brief Perform an integrity check on the specified container.
+ * \since 12.0
+ *
+ * \param self Container to check integrity.
+ *
+ * \note The container is already locked for reading.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int hash_ao2_integrity(struct ao2_container_hash *self)
+{
+	int bucket_exp;
+	int bucket;
+	int count_node_forward;
+	int count_node_backward;
+	int count_obj_forward;
+	int count_obj_backward;
+	struct hash_bucket_node *node;
+	struct hash_bucket_node *last;
+
+	/* For each bucket in the container. */
+	for (bucket = 0; bucket < self->n_buckets; ++bucket) {
+		count_node_forward = 0;
+		count_node_backward = 0;
+		count_obj_forward = 0;
+		count_obj_backward = 0;
+
+		/* Check forward list. */
+		last = NULL;
+		for (node = AST_DLLIST_FIRST(&self->buckets[bucket].list);
+			node;
+			node = AST_DLLIST_NEXT(node, links)) {
+			++count_node_forward;
+
+			if (bucket != node->my_bucket) {
+				ast_log(LOG_ERROR, "Node not in correct bucket!\n");
+				return -1;
+			}
+
+			if (!node->common.obj) {
+				/* Node is empty. */
+				continue;
+			}
+			++count_obj_forward;
+
+			/* Check container hash key for expected bucket. */
+			bucket_exp = abs(self->hash_fn(node->common.obj, OBJ_POINTER));
+			bucket_exp %= self->n_buckets;
+			if (bucket != bucket_exp) {
+				ast_log(LOG_ERROR, "Hash does not match bucket!\n");
+				return -1;
+			}
+
+			/* Check sort if configured. */
+			if (last && self->common.sort_fn) {
+				if (self->common.sort_fn(last->common.obj, node->common.obj, OBJ_POINTER) > 0) {
+					ast_log(LOG_ERROR, "Bucket nodes out of order!\n");
+					return -1;
+				}
+			}
+			last = node;
+		}
+
+		/* Check backward list. */
+		last = NULL;
+		for (node = AST_DLLIST_LAST(&self->buckets[bucket].list);
+			node;
+			node = AST_DLLIST_PREV(node, links)) {
+			++count_node_backward;
+
+			if (bucket != node->my_bucket) {
+				ast_log(LOG_ERROR, "Node not in correct bucket!\n");
+				return -1;
+			}
+
+			if (!node->common.obj) {
+				/* Node is empty. */
+				continue;
+			}
+			++count_obj_backward;
+
+			/* Check container hash key for expected bucket. */
+			bucket_exp = abs(self->hash_fn(node->common.obj, OBJ_POINTER));
+			bucket_exp %= self->n_buckets;
+			if (bucket != bucket_exp) {
+				ast_log(LOG_ERROR, "Hash does not match bucket!\n");
+				return -1;
+			}
+
+			/* Check sort if configured. */
+			if (last && self->common.sort_fn) {
+				if (self->common.sort_fn(node->common.obj, last->common.obj, OBJ_POINTER) > 0) {
+					ast_log(LOG_ERROR, "Bucket nodes out of order!\n");
+					return -1;
+				}
+			}
+			last = node;
+		}
+
+		/* Check bucket forward/backward node count. */
+		if (count_node_forward != count_node_backward) {
+			ast_log(LOG_ERROR, "Forward/backward node count does not match!\n");
+			return -1;
+		}
+
+		/* Check bucket forward/backward obj count. */
+		if (count_obj_forward != count_obj_backward) {
+			ast_log(LOG_ERROR, "Forward/backward object count does not match!\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+#endif	/* defined(AST_DEVMODE) */
+
 /*! Hash container virtual method table. */
 static const struct ao2_container_methods v_table_hash = {
 	.type = AO2_CONTAINER_RTTI_HASH,
@@ -2616,6 +2737,7 @@
 #if defined(AST_DEVMODE)
 	.destroy = (ao2_container_destroy_fn) hash_ao2_destroy,
 	.stats = (ao2_container_statistics) hash_ao2_stats,
+	.integrity = (ao2_container_integrity) hash_ao2_integrity,
 #endif	/* defined(AST_DEVMODE) */
 };
 

Modified: team/rmudgett/ao2_enhancements/tests/test_astobj2.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/ao2_enhancements/tests/test_astobj2.c?view=diff&rev=371233&r1=371232&r2=371233
==============================================================================
--- team/rmudgett/ao2_enhancements/tests/test_astobj2.c (original)
+++ team/rmudgett/ao2_enhancements/tests/test_astobj2.c Mon Aug 13 16:17:59 2012
@@ -153,6 +153,11 @@
 			res = AST_TEST_FAIL;
 		}
 	}
+	if (ao2_container_check(c1, 0)) {
+		ast_test_status_update(test, "container integrity check failed\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
 
 	ast_test_status_update(test, "Container created: random bucket size %d: number of items: %d\n", bucket_size, lim);
 
@@ -160,6 +165,11 @@
 	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_check(c3, 0)) {
+		ast_test_status_update(test, "container integrity check failed\n");
 		res = AST_TEST_FAIL;
 		goto cleanup;
 	}
@@ -190,6 +200,10 @@
 			ast_test_status_update(test, "Cloned container still has objects.\n");
 			res = AST_TEST_FAIL;
 		}
+		if (ao2_container_check(c3, 0)) {
+			ast_test_status_update(test, "container integrity check failed\n");
+			res = AST_TEST_FAIL;
+		}
 	}
 	ao2_t_ref(c3, -1, "bye c3");
 	c3 = NULL;
@@ -247,13 +261,15 @@
 		}
 	}
 
-	/* Testing ao2_find with OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE.
+	/*
+	 * Testing ao2_find with OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE.
 	 * In this test items are unlinked from c1 and placed in c2.  Then
 	 * unlinked from c2 and placed back into c1.
 	 *
 	 * For this module and set of custom hash/cmp functions, an object
 	 * should only be found if the astobj2 default cmp function is used.
-	 * This test is designed to mimic the chan_iax.c call number use case. */
+	 * This test is designed to mimic the chan_iax.c call number use case.
+	 */
 	num = lim < 25 ? lim : 25;
 	for (; num; num--) {
 		if (!(obj = ao2_find(c1, NULL, OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE))) {
@@ -270,6 +286,16 @@
 			ao2_t_ref(obj, -1, "test");
 		}
 	}
+	if (ao2_container_check(c1, 0)) {
+		ast_test_status_update(test, "container integrity check failed\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	if (ao2_container_check(c2, 0)) {
+		ast_test_status_update(test, "container integrity check failed\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
 	it = ao2_iterator_init(c2, 0);
 	while ((obj = ao2_t_iterator_next(&it, "test"))) {
 		ao2_t_unlink(c2, obj, "test");
@@ -277,6 +303,16 @@
 		ao2_t_ref(obj, -1, "test");
 	}
 	ao2_iterator_destroy(&it);
+	if (ao2_container_check(c1, 0)) {
+		ast_test_status_update(test, "container integrity check failed\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	if (ao2_container_check(c2, 0)) {
+		ast_test_status_update(test, "container integrity check failed\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
 
 	/* Test Callback with no flags. */
 	increment = 0;
@@ -297,7 +333,7 @@
 	/* Test OBJ_MULTIPLE with OBJ_UNLINK*/
 	num = lim < 25 ? lim : 25;
 	if (!(mult_it = ao2_t_callback(c1, OBJ_MULTIPLE | OBJ_UNLINK, multiple_cb, &num, "test multiple"))) {
-		ast_test_status_update(test, "OBJ_MULTIPLE iwth OBJ_UNLINK test failed.\n");
+		ast_test_status_update(test, "OBJ_MULTIPLE with OBJ_UNLINK test failed.\n");
 		res = AST_TEST_FAIL;
 	} else {
 		/* make sure num items unlinked is as expected */
@@ -305,6 +341,11 @@
 			ast_test_status_update(test, "OBJ_MULTIPLE | OBJ_UNLINK test failed, did not unlink correct number of objects.\n");
 			res = AST_TEST_FAIL;
 		}
+		if (ao2_container_check(c1, 0)) {
+			ast_test_status_update(test, "container integrity check failed\n");
+			res = AST_TEST_FAIL;
+			goto cleanup;
+		}
 
 		/* link what was unlinked back into c1 */
 		while ((obj = ao2_t_iterator_next(mult_it, "test"))) {
@@ -312,6 +353,11 @@
 			ao2_t_ref(obj, -1, "test"); /* remove ref from iterator */
 		}
 		ao2_iterator_destroy(mult_it);
+		if (ao2_container_check(c1, 0)) {
+			ast_test_status_update(test, "container integrity check failed\n");
+			res = AST_TEST_FAIL;
+			goto cleanup;
+		}
 	}
 
 	/* Test OBJ_MULTIPLE without unlink, add items back afterwards */
@@ -359,12 +405,21 @@
 		ast_test_status_update(test, "unlink during iterator failed. Number %d was not removed.\n", num);
 		res = AST_TEST_FAIL;
 	}
+	if (ao2_container_check(c1, 0)) {
+		ast_test_status_update(test, "container integrity check failed\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
 
 	/* Test unlink all with OBJ_MULTIPLE, leave a single object for the container to destroy */
 	ao2_t_callback(c1, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, all_but_one_cb, NULL, "test multiple");
 	/* check to make sure all test_obj destructors were called except for 1 */
 	if (destructor_count != 1) {
 		ast_test_status_update(test, "OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA failed. destructor count %d\n", destructor_count);
+		res = AST_TEST_FAIL;
+	}
+	if (ao2_container_check(c1, 0)) {
+		ast_test_status_update(test, "container integrity check failed\n");
 		res = AST_TEST_FAIL;
 	}
 
@@ -483,6 +538,11 @@
 			ast_test_status_update(test, "container did not link correctly\n");
 			res = AST_TEST_FAIL;
 		}
+	}
+	if (ao2_container_check(c, 0)) {
+		ast_test_status_update(test, "container integrity check failed\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
 	}
 
 	/*




More information about the asterisk-commits mailing list