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

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Mar 27 12:13:38 CDT 2012


Author: rmudgett
Date: Tue Mar 27 12:13:32 2012
New Revision: 360627

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=360627
Log:
Add global ao2 array container.

Global ao2 objects must always exist after initialization because there is
no access control to obtain another reference to the global object.

It is expected that module configuration could use these new API calls to
replace an active configuration parameter object with an updated
configuration parameter object.

With these new API calls, the global object could be replaced, removed, or
referenced without the risk of someone using a stale global object
pointer.

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

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=360627&r1=360626&r2=360627
==============================================================================
--- trunk/include/asterisk/astobj2.h (original)
+++ trunk/include/asterisk/astobj2.h Tue Mar 27 12:13:32 2012
@@ -563,6 +563,133 @@
  * \since 1.6.1
  */
 void *ao2_object_get_lockaddr(void *obj);
+
+
+/*! Global ao2 array container base structure. */
+struct ao2_global_obj {
+	/*! Access lock to the global ao2 array container. */
+	ast_rwlock_t lock;
+	/*! Number of elements in the global ao2 array container. */
+	unsigned int num_elements;
+	/*! Global ao2 array container array. */
+	void *obj[0];
+};
+
+/*!
+ * \brief Define a structure to be used to hold a global array of ao2 objects, statically initialized.
+ * \since 11.0
+ *
+ * \param name This will be the name of the defined structure.
+ * \param num_objects Number of ao2 objects to contain.
+ *
+ * \details
+ * This macro creates a structure definition that can be used to
+ * hold an array of ao2 objects accessible using an API.  The
+ * structure is allocated and initialized to be empty.
+ *
+ * Example usage:
+ * \code
+ * static AO2_GLOBAL_OBJ_STATIC(global_cfg, 10);
+ * \endcode
+ *
+ * This would define \c struct \c global_cfg, intended to hold
+ * an array of ao2 objects accessible using an API.
+ */
+#ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER
+#define AO2_GLOBAL_OBJ_STATIC(name, num_objects)						\
+	struct name {														\
+		struct ao2_global_obj global;									\
+		void *objs[num_objects];										\
+	} name;																\
+	static void  __attribute__((constructor)) __init_##name(void)		\
+	{																	\
+		unsigned int idx = (num_objects);								\
+		ast_rwlock_init(&name.global.lock);								\
+		name.global.num_elements = idx;									\
+		while (idx--) {													\
+			name.global.obj[idx] = NULL;								\
+		}																\
+	}																	\
+	static void  __attribute__((destructor)) __fini_##name(void)		\
+	{																	\
+		unsigned int idx = (num_objects);								\
+		while (idx--) {													\
+			if (name.global.obj[idx]) {									\
+				ao2_ref(name.global.obj[idx], -1);						\
+				name.global.obj[idx] = NULL;							\
+			}															\
+		}																\
+		ast_rwlock_destroy(&name.global.lock);							\
+	}																	\
+	struct __dummy_##name
+#else
+#define AO2_GLOBAL_OBJ_STATIC(name, num_objects)						\
+	struct name {														\
+		struct ao2_global_obj global;									\
+		void *objs[num_objects];										\
+	} name = {															\
+		.global.lock = AST_RWLOCK_INIT_VALUE,							\
+		.global.num_elements = (num_objects),							\
+	}
+#endif
+
+/*!
+ * \brief Release all global ao2 objects in the global array.
+ * \since 11.0
+ *
+ * \param array Global ao2 object array container.
+ * \param tag used for debugging
+ *
+ * \return Nothing
+ */
+#define ao2_t_global_obj_release(array, tag)	\
+	__ao2_global_obj_release(&array.global, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #array)
+#define ao2_global_obj_release(array)	\
+	__ao2_global_obj_release(&array.global, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #array)
+
+void __ao2_global_obj_release(struct ao2_global_obj *array, const char *tag, const char *file, int line, const char *func, const char *name);
+
+/*!
+ * \brief Replace a global ao2 object in the global array.
+ * \since 11.0
+ *
+ * \param array Global ao2 object array container.
+ * \param idx Index to replace in the array.
+ * \param obj Object to put into the array.  Can be NULL.
+ * \param tag used for debugging
+ *
+ * \note This function automatically increases the reference
+ * count to account for the reference that the global array now
+ * holds to the object.
+ *
+ * \retval Reference to previous global ao2 object stored at the index.
+ * \retval NULL if no object available.
+ */
+#define ao2_t_global_obj_replace(array, idx, obj, tag)	\
+	__ao2_global_obj_replace(&array.global, (idx), (obj), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #array)
+#define ao2_global_obj_replace(array, idx, obj)	\
+	__ao2_global_obj_replace(&array.global, (idx), (obj), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #array)
+
+void *__ao2_global_obj_replace(struct ao2_global_obj *array, unsigned int idx, void *obj, const char *tag, const char *file, int line, const char *func, const char *name);
+
+/*!
+ * \brief Get a reference to the object stored in the ao2 global array.
+ * \since 11.0
+ *
+ * \param array Global ao2 object array container.
+ * \param idx Index to get an object reference in the array.
+ * \param tag used for debugging
+ *
+ * \retval Reference to current global ao2 object stored at the index.
+ * \retval NULL if no object available.
+ */
+#define ao2_t_global_obj_ref(array, idx, tag)	\
+	__ao2_global_obj_ref(&array.global, (idx), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #array)
+#define ao2_global_obj_ref(array, idx)	\
+	__ao2_global_obj_ref(&array.global, (idx), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #array)
+
+void *__ao2_global_obj_ref(struct ao2_global_obj *array, unsigned int idx, const char *tag, const char *file, int line, const char *func, const char *name);
+
 
 /*!
  \page AstObj2_Containers AstObj2 Containers

Modified: trunk/main/astobj2.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/astobj2.c?view=diff&rev=360627&r1=360626&r2=360627
==============================================================================
--- trunk/main/astobj2.c (original)
+++ trunk/main/astobj2.c Tue Mar 27 12:13:32 2012
@@ -636,6 +636,79 @@
 void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options)
 {
 	return internal_ao2_alloc(data_size, destructor_fn, options, __FILE__, __LINE__, __FUNCTION__);
+}
+
+
+void __ao2_global_obj_release(struct ao2_global_obj *array, const char *tag, const char *file, int line, const char *func, const char *name)
+{
+	unsigned int idx;
+
+	if (!array) {
+		/* For sanity */
+		return;
+	}
+	if (__ast_rwlock_wrlock(file, line, func, &array->lock, name)) {
+		/* Could not get the write lock. */
+		return;
+	}
+
+	/* Release all contained ao2 objects. */
+	idx = array->num_elements;
+	while (idx--) {
+		if (array->obj[idx]) {
+			__ao2_ref_debug(array->obj[idx], -1, tag, file, line, func);
+			array->obj[idx] = NULL;
+		}
+	}
+
+	__ast_rwlock_unlock(file, line, func, &array->lock, name);
+}
+
+void *__ao2_global_obj_replace(struct ao2_global_obj *array, unsigned int idx, void *obj, const char *tag, const char *file, int line, const char *func, const char *name)
+{
+	void *obj_old;
+
+	if (!array || array->num_elements <= idx) {
+		/* For sanity */
+		return NULL;
+	}
+	if (__ast_rwlock_wrlock(file, line, func, &array->lock, name)) {
+		/* Could not get the write lock. */
+		return NULL;
+	}
+
+	if (obj) {
+		__ao2_ref_debug(obj, +1, tag, file, line, func);
+	}
+	obj_old = array->obj[idx];
+	array->obj[idx] = obj;
+
+	__ast_rwlock_unlock(file, line, func, &array->lock, name);
+
+	return obj_old;
+}
+
+void *__ao2_global_obj_ref(struct ao2_global_obj *array, unsigned int idx, const char *tag, const char *file, int line, const char *func, const char *name)
+{
+	void *obj;
+
+	if (!array || array->num_elements <= idx) {
+		/* For sanity */
+		return NULL;
+	}
+	if (__ast_rwlock_rdlock(file, line, func, &array->lock, name)) {
+		/* Could not get the read lock. */
+		return NULL;
+	}
+
+	obj = array->obj[idx];
+	if (obj) {
+		__ao2_ref_debug(obj, +1, tag, file, line, func);
+	}
+
+	__ast_rwlock_unlock(file, line, func, &array->lock, name);
+
+	return obj;
 }
 
 

Modified: trunk/tests/test_astobj2.c
URL: http://svnview.digium.com/svn/asterisk/trunk/tests/test_astobj2.c?view=diff&rev=360627&r1=360626&r2=360627
==============================================================================
--- trunk/tests/test_astobj2.c (original)
+++ trunk/tests/test_astobj2.c Tue Mar 27 12:13:32 2012
@@ -335,7 +335,7 @@
 		ao2_iterator_destroy(mult_it);
 	}
 
-	/* Is the container count what we expect after all the finds and unlinks?*/
+	/* Is the container count what we expect after all the finds and unlinks? */
 	if (ao2_container_count(c1) != lim) {
 		ast_test_status_update(test, "container count does not match what is expected after ao2_find tests.\n");
 		res = AST_TEST_FAIL;
@@ -567,10 +567,144 @@
 	return res;
 }
 
+static AO2_GLOBAL_OBJ_STATIC(astobj2_array, 2);
+
+AST_TEST_DEFINE(astobj2_test_3)
+{
+	int res = AST_TEST_PASS;
+	int destructor_count = 0;
+	int num_objects = 0;
+	struct test_obj *obj = NULL;
+	struct test_obj *obj2 = NULL;
+	struct test_obj *obj3 = NULL;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "astobj2_test3";
+		info->category = "/main/astobj2/";
+		info->summary = "Test global ao2 array container";
+		info->description =
+			"This test is to see if the global ao2 array container works as intended.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	/* Put an object in index 0 */
+	obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
+	if (!obj) {
+		ast_test_status_update(test, "ao2_alloc failed.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	obj->destructor_count = &destructor_count;
+	obj->i = ++num_objects;
+	obj2 = ao2_t_global_obj_replace(astobj2_array, 0, obj, "Save object in index 0");
+	if (obj2) {
+		ast_test_status_update(test, "Returned object not expected.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	/* Save object for next check. */
+	obj3 = obj;
+
+	/* Replace an object in index 0 */
+	obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
+	if (!obj) {
+		ast_test_status_update(test, "ao2_alloc failed.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	obj->destructor_count = &destructor_count;
+	obj->i = ++num_objects;
+	obj2 = ao2_t_global_obj_replace(astobj2_array, 0, obj, "Replace object in index 0");
+	if (!obj2) {
+		ast_test_status_update(test, "Expected an object.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	if (obj2 != obj3) {
+		ast_test_status_update(test, "Replaced object not expected object.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	ao2_ref(obj3, -1);
+	obj3 = NULL;
+	ao2_ref(obj2, -1);
+	obj2 = NULL;
+	ao2_ref(obj, -1);
+
+	/* Put an object in index 1 */
+	obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
+	if (!obj) {
+		ast_test_status_update(test, "ao2_alloc failed.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	obj->destructor_count = &destructor_count;
+	obj->i = ++num_objects;
+	obj2 = ao2_t_global_obj_replace(astobj2_array, 1, obj, "Save object in index 1");
+	if (obj2) {
+		ao2_ref(obj2, -1);
+		ast_test_status_update(test, "Returned object not expected.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	/* Save object for next check. */
+	obj3 = obj;
+
+	/* Get a reference to the object in index 1. */
+	obj = ao2_t_global_obj_ref(astobj2_array, 1, "Get reference of index 1 object");
+	if (!obj) {
+		ast_test_status_update(test, "Expected an object.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	if (obj != obj3) {
+		ast_test_status_update(test, "Returned object not expected.\n");
+		res = AST_TEST_FAIL;
+		goto cleanup;
+	}
+	ao2_ref(obj3, -1);
+	obj3 = NULL;
+	ao2_ref(obj, -1);
+	obj = NULL;
+
+	/* Release all objects in the global array. */
+	ao2_t_global_obj_release(astobj2_array, "Check release all objects");
+	destructor_count += num_objects;
+	if (0 < destructor_count) {
+		ast_test_status_update(test,
+			"all destructors were not called, destructor count is %d\n",
+			destructor_count);
+		res = AST_TEST_FAIL;
+	} else if (destructor_count < 0) {
+		ast_test_status_update(test,
+			"Destructor was called too many times, destructor count is %d\n",
+			destructor_count);
+		res = AST_TEST_FAIL;
+	}
+
+cleanup:
+	if (obj) {
+		ao2_t_ref(obj, -1, "Test cleanup external object 1");
+	}
+	if (obj2) {
+		ao2_t_ref(obj2, -1, "Test cleanup external object 2");
+	}
+	if (obj3) {
+		ao2_t_ref(obj3, -1, "Test cleanup external object 3");
+	}
+	ao2_t_global_obj_release(astobj2_array, "Test cleanup array");
+
+	return res;
+}
+
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(astobj2_test_1);
 	AST_TEST_UNREGISTER(astobj2_test_2);
+	AST_TEST_UNREGISTER(astobj2_test_3);
 	return 0;
 }
 
@@ -578,6 +712,7 @@
 {
 	AST_TEST_REGISTER(astobj2_test_1);
 	AST_TEST_REGISTER(astobj2_test_2);
+	AST_TEST_REGISTER(astobj2_test_3);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 




More information about the asterisk-commits mailing list