Change ao2 global array to ao2 global object holder.

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


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@366663 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Richard Mudgett
2012-05-16 16:34:42 +00:00
parent 5629d66257
commit d4fa095a64
3 changed files with 117 additions and 109 deletions

View File

@@ -565,130 +565,133 @@ int __ao2_trylock(void *a, enum ao2_lock_req lock_how, const char *file, const c
void *ao2_object_get_lockaddr(void *obj); void *ao2_object_get_lockaddr(void *obj);
/*! Global ao2 array container base structure. */ /*! Global ao2 object holder structure. */
struct ao2_global_obj { struct ao2_global_obj {
/*! Access lock to the global ao2 array container. */ /*! Access lock to the held ao2 object. */
ast_rwlock_t lock; ast_rwlock_t lock;
/*! Number of elements in the global ao2 array container. */ /*! Global ao2 object. */
unsigned int num_elements; void *obj;
/*! 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. * \brief Define a global object holder to be used to hold an ao2 object, statically initialized.
* \since 11.0 * \since 11.0
* *
* \param name This will be the name of the defined structure. * \param name This will be the name of the object holder.
* \param num_objects Number of ao2 objects to contain.
* *
* \details * \details
* This macro creates a structure definition that can be used to * This macro creates a global object holder that can be used to
* hold an array of ao2 objects accessible using an API. The * hold an ao2 object accessible using an API. The structure is
* structure is allocated and initialized to be empty. * allocated and initialized to be empty.
* *
* Example usage: * Example usage:
* \code * \code
* static AO2_GLOBAL_OBJ_STATIC(global_cfg, 10); * static AO2_GLOBAL_OBJ_STATIC(global_cfg);
* \endcode * \endcode
* *
* This would define \c struct \c global_cfg, intended to hold * This defines global_cfg, intended to hold an ao2 object
* an array of ao2 objects accessible using an API. * accessible using an API.
*/ */
#ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER #ifndef HAVE_PTHREAD_RWLOCK_INITIALIZER
#define AO2_GLOBAL_OBJ_STATIC(name, num_objects) \ #define AO2_GLOBAL_OBJ_STATIC(name) \
struct name { \ struct ao2_global_obj name; \
struct ao2_global_obj global; \
void *objs[num_objects]; \
} name; \
static void __attribute__((constructor)) __init_##name(void) \ static void __attribute__((constructor)) __init_##name(void) \
{ \ { \
unsigned int idx = (num_objects); \ ast_rwlock_init(&name.lock); \
ast_rwlock_init(&name.global.lock); \ name.obj = NULL; \
name.global.num_elements = idx; \
while (idx--) { \
name.global.obj[idx] = NULL; \
} \
} \ } \
static void __attribute__((destructor)) __fini_##name(void) \ static void __attribute__((destructor)) __fini_##name(void) \
{ \ { \
unsigned int idx = (num_objects); \ if (name.obj) { \
while (idx--) { \ ao2_ref(name.obj, -1); \
if (name.global.obj[idx]) { \ name.obj = NULL; \
ao2_ref(name.global.obj[idx], -1); \
name.global.obj[idx] = NULL; \
} \
} \ } \
ast_rwlock_destroy(&name.global.lock); \ ast_rwlock_destroy(&name.lock); \
} \ } \
struct __dummy_##name struct __dummy_##name
#else #else
#define AO2_GLOBAL_OBJ_STATIC(name, num_objects) \ #define AO2_GLOBAL_OBJ_STATIC(name) \
struct name { \ struct ao2_global_obj name = { \
struct ao2_global_obj global; \ .lock = AST_RWLOCK_INIT_VALUE, \
void *objs[num_objects]; \
} name = { \
.global.lock = AST_RWLOCK_INIT_VALUE, \
.global.num_elements = (num_objects), \
} }
#endif #endif
/*! /*!
* \brief Release all global ao2 objects in the global array. * \brief Release the ao2 object held in the global holder.
* \since 11.0 * \since 11.0
* *
* \param array Global ao2 object array container. * \param holder Global ao2 object holder.
* \param tag used for debugging * \param tag used for debugging
* *
* \return Nothing * \return Nothing
*/ */
#define ao2_t_global_obj_release(array, tag) \ #define ao2_t_global_obj_release(holder, tag) \
__ao2_global_obj_release(&array.global, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #array) __ao2_global_obj_release(&holder, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder)
#define ao2_global_obj_release(array) \ #define ao2_global_obj_release(holder) \
__ao2_global_obj_release(&array.global, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #array) __ao2_global_obj_release(&holder, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder)
void __ao2_global_obj_release(struct ao2_global_obj *array, const char *tag, const char *file, int line, const char *func, const char *name); void __ao2_global_obj_release(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name);
/*! /*!
* \brief Replace a global ao2 object in the global array. * \brief Replace an ao2 object in the global holder.
* \since 11.0 * \since 11.0
* *
* \param array Global ao2 object array container. * \param holder Global ao2 object holder.
* \param idx Index to replace in the array. * \param obj Object to put into the holder. Can be NULL.
* \param obj Object to put into the array. Can be NULL.
* \param tag used for debugging * \param tag used for debugging
* *
* \note This function automatically increases the reference * \note This function automatically increases the reference
* count to account for the reference that the global array now * count to account for the reference that the global holder now
* holds to the object. * holds to the object.
* *
* \retval Reference to previous global ao2 object stored at the index. * \retval Reference to previous global ao2 object stored.
* \retval NULL if no object available. * \retval NULL if no object available.
*/ */
#define ao2_t_global_obj_replace(array, idx, obj, tag) \ #define ao2_t_global_obj_replace(holder, obj, tag) \
__ao2_global_obj_replace(&array.global, (idx), (obj), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #array) __ao2_global_obj_replace(&holder, (obj), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder)
#define ao2_global_obj_replace(array, idx, obj) \ #define ao2_global_obj_replace(holder, idx, obj) \
__ao2_global_obj_replace(&array.global, (idx), (obj), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #array) __ao2_global_obj_replace(&holder, (obj), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder)
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 *__ao2_global_obj_replace(struct ao2_global_obj *holder, 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. * \brief Replace an ao2 object in the global holder, throwing away any old object.
* \since 11.0 * \since 11.0
* *
* \param array Global ao2 object array container. * \param holder Global ao2 object holder.
* \param idx Index to get an object reference in the array. * \param obj Object to put into the holder. Can be NULL.
* \param tag used for debugging * \param tag used for debugging
* *
* \retval Reference to current global ao2 object stored at the index. * \note This function automatically increases the reference
* count to account for the reference that the global holder now
* holds to the object. It also decreases the reference count
* of any object being replaced.
*
* \retval 0 The global object was previously empty
* \retval 1 The global object was not previously empty
*/
#define ao2_t_global_obj_replace_unref(holder, obj, tag) \
__ao2_global_obj_replace_unref(&holder, (obj), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder)
#define ao2_global_obj_replace_unref(holder, obj) \
__ao2_global_obj_replace_unref(&holder, (obj), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder)
int __ao2_global_obj_replace_unref(struct ao2_global_obj *holder, 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 global holder.
* \since 11.0
*
* \param holder Global ao2 object holder.
* \param tag used for debugging
*
* \retval Reference to current ao2 object stored in the holder.
* \retval NULL if no object available. * \retval NULL if no object available.
*/ */
#define ao2_t_global_obj_ref(array, idx, tag) \ #define ao2_t_global_obj_ref(holder, tag) \
__ao2_global_obj_ref(&array.global, (idx), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #array) __ao2_global_obj_ref(&holder, (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder)
#define ao2_global_obj_ref(array, idx) \ #define ao2_global_obj_ref(holder, idx) \
__ao2_global_obj_ref(&array.global, (idx), "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #array) __ao2_global_obj_ref(&holder, "", __FILE__, __LINE__, __PRETTY_FUNCTION__, #holder)
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 *__ao2_global_obj_ref(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name);
/*! /*!

View File

@@ -634,40 +634,35 @@ void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned in
} }
void __ao2_global_obj_release(struct ao2_global_obj *array, const char *tag, const char *file, int line, const char *func, const char *name) void __ao2_global_obj_release(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name)
{ {
unsigned int idx; if (!holder) {
if (!array) {
/* For sanity */ /* For sanity */
return; return;
} }
if (__ast_rwlock_wrlock(file, line, func, &array->lock, name)) { if (__ast_rwlock_wrlock(file, line, func, &holder->lock, name)) {
/* Could not get the write lock. */ /* Could not get the write lock. */
return; return;
} }
/* Release all contained ao2 objects. */ /* Release the held ao2 object. */
idx = array->num_elements; if (holder->obj) {
while (idx--) { __ao2_ref_debug(holder->obj, -1, tag, file, line, func);
if (array->obj[idx]) { holder->obj = NULL;
__ao2_ref_debug(array->obj[idx], -1, tag, file, line, func);
array->obj[idx] = NULL;
}
} }
__ast_rwlock_unlock(file, line, func, &array->lock, name); __ast_rwlock_unlock(file, line, func, &holder->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 *__ao2_global_obj_replace(struct ao2_global_obj *holder, void *obj, const char *tag, const char *file, int line, const char *func, const char *name)
{ {
void *obj_old; void *obj_old;
if (!array || array->num_elements <= idx) { if (!holder) {
/* For sanity */ /* For sanity */
return NULL; return NULL;
} }
if (__ast_rwlock_wrlock(file, line, func, &array->lock, name)) { if (__ast_rwlock_wrlock(file, line, func, &holder->lock, name)) {
/* Could not get the write lock. */ /* Could not get the write lock. */
return NULL; return NULL;
} }
@@ -675,33 +670,45 @@ void *__ao2_global_obj_replace(struct ao2_global_obj *array, unsigned int idx, v
if (obj) { if (obj) {
__ao2_ref_debug(obj, +1, tag, file, line, func); __ao2_ref_debug(obj, +1, tag, file, line, func);
} }
obj_old = array->obj[idx]; obj_old = holder->obj;
array->obj[idx] = obj; holder->obj = obj;
__ast_rwlock_unlock(file, line, func, &array->lock, name); __ast_rwlock_unlock(file, line, func, &holder->lock, name);
return obj_old; 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) int __ao2_global_obj_replace_unref(struct ao2_global_obj *holder, void *obj, const char *tag, const char *file, int line, const char *func, const char *name)
{
void *obj_old;
obj_old = __ao2_global_obj_replace(holder, obj, tag, file, line, func, name);
if (obj_old) {
__ao2_ref_debug(obj_old, -1, tag, file, line, func);
return 1;
}
return 0;
}
void *__ao2_global_obj_ref(struct ao2_global_obj *holder, const char *tag, const char *file, int line, const char *func, const char *name)
{ {
void *obj; void *obj;
if (!array || array->num_elements <= idx) { if (!holder) {
/* For sanity */ /* For sanity */
return NULL; return NULL;
} }
if (__ast_rwlock_rdlock(file, line, func, &array->lock, name)) { if (__ast_rwlock_rdlock(file, line, func, &holder->lock, name)) {
/* Could not get the read lock. */ /* Could not get the read lock. */
return NULL; return NULL;
} }
obj = array->obj[idx]; obj = holder->obj;
if (obj) { if (obj) {
__ao2_ref_debug(obj, +1, tag, file, line, func); __ao2_ref_debug(obj, +1, tag, file, line, func);
} }
__ast_rwlock_unlock(file, line, func, &array->lock, name); __ast_rwlock_unlock(file, line, func, &holder->lock, name);
return obj; return obj;
} }

View File

@@ -567,7 +567,7 @@ cleanup:
return res; return res;
} }
static AO2_GLOBAL_OBJ_STATIC(astobj2_array, 2); static AO2_GLOBAL_OBJ_STATIC(astobj2_holder);
AST_TEST_DEFINE(astobj2_test_3) AST_TEST_DEFINE(astobj2_test_3)
{ {
@@ -582,15 +582,15 @@ AST_TEST_DEFINE(astobj2_test_3)
case TEST_INIT: case TEST_INIT:
info->name = "astobj2_test3"; info->name = "astobj2_test3";
info->category = "/main/astobj2/"; info->category = "/main/astobj2/";
info->summary = "Test global ao2 array container"; info->summary = "Test global ao2 holder";
info->description = info->description =
"This test is to see if the global ao2 array container works as intended."; "This test is to see if the global ao2 holder works as intended.";
return AST_TEST_NOT_RUN; return AST_TEST_NOT_RUN;
case TEST_EXECUTE: case TEST_EXECUTE:
break; break;
} }
/* Put an object in index 0 */ /* Put an object in the holder */
obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor); obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
if (!obj) { if (!obj) {
ast_test_status_update(test, "ao2_alloc failed.\n"); ast_test_status_update(test, "ao2_alloc failed.\n");
@@ -599,7 +599,7 @@ AST_TEST_DEFINE(astobj2_test_3)
} }
obj->destructor_count = &destructor_count; obj->destructor_count = &destructor_count;
obj->i = ++num_objects; obj->i = ++num_objects;
obj2 = ao2_t_global_obj_replace(astobj2_array, 0, obj, "Save object in index 0"); obj2 = ao2_t_global_obj_replace(astobj2_holder, obj, "Save object in the holder");
if (obj2) { if (obj2) {
ast_test_status_update(test, "Returned object not expected.\n"); ast_test_status_update(test, "Returned object not expected.\n");
res = AST_TEST_FAIL; res = AST_TEST_FAIL;
@@ -608,7 +608,7 @@ AST_TEST_DEFINE(astobj2_test_3)
/* Save object for next check. */ /* Save object for next check. */
obj3 = obj; obj3 = obj;
/* Replace an object in index 0 */ /* Replace an object in the holder */
obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor); obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
if (!obj) { if (!obj) {
ast_test_status_update(test, "ao2_alloc failed.\n"); ast_test_status_update(test, "ao2_alloc failed.\n");
@@ -617,7 +617,7 @@ AST_TEST_DEFINE(astobj2_test_3)
} }
obj->destructor_count = &destructor_count; obj->destructor_count = &destructor_count;
obj->i = ++num_objects; obj->i = ++num_objects;
obj2 = ao2_t_global_obj_replace(astobj2_array, 0, obj, "Replace object in index 0"); obj2 = ao2_t_global_obj_replace(astobj2_holder, obj, "Replace object in the holder");
if (!obj2) { if (!obj2) {
ast_test_status_update(test, "Expected an object.\n"); ast_test_status_update(test, "Expected an object.\n");
res = AST_TEST_FAIL; res = AST_TEST_FAIL;
@@ -634,7 +634,7 @@ AST_TEST_DEFINE(astobj2_test_3)
obj2 = NULL; obj2 = NULL;
ao2_ref(obj, -1); ao2_ref(obj, -1);
/* Put an object in index 1 */ /* Replace with unref of an object in the holder */
obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor); obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor);
if (!obj) { if (!obj) {
ast_test_status_update(test, "ao2_alloc failed.\n"); ast_test_status_update(test, "ao2_alloc failed.\n");
@@ -643,25 +643,23 @@ AST_TEST_DEFINE(astobj2_test_3)
} }
obj->destructor_count = &destructor_count; obj->destructor_count = &destructor_count;
obj->i = ++num_objects; obj->i = ++num_objects;
obj2 = ao2_t_global_obj_replace(astobj2_array, 1, obj, "Save object in index 1"); if (!ao2_t_global_obj_replace_unref(astobj2_holder, obj, "Replace w/ unref object in the holder")) {
if (obj2) { ast_test_status_update(test, "Expected an object to be replaced.\n");
ao2_ref(obj2, -1);
ast_test_status_update(test, "Returned object not expected.\n");
res = AST_TEST_FAIL; res = AST_TEST_FAIL;
goto cleanup; goto cleanup;
} }
/* Save object for next check. */ /* Save object for next check. */
obj3 = obj; obj3 = obj;
/* Get a reference to the object in index 1. */ /* Get reference to held object. */
obj = ao2_t_global_obj_ref(astobj2_array, 1, "Get reference of index 1 object"); obj = ao2_t_global_obj_ref(astobj2_holder, "Get a held object reference");
if (!obj) { if (!obj) {
ast_test_status_update(test, "Expected an object.\n"); ast_test_status_update(test, "Expected an object.\n");
res = AST_TEST_FAIL; res = AST_TEST_FAIL;
goto cleanup; goto cleanup;
} }
if (obj != obj3) { if (obj != obj3) {
ast_test_status_update(test, "Returned object not expected.\n"); ast_test_status_update(test, "Referenced object not expected object.\n");
res = AST_TEST_FAIL; res = AST_TEST_FAIL;
goto cleanup; goto cleanup;
} }
@@ -670,8 +668,8 @@ AST_TEST_DEFINE(astobj2_test_3)
ao2_ref(obj, -1); ao2_ref(obj, -1);
obj = NULL; obj = NULL;
/* Release all objects in the global array. */ /* Release the object in the global holder. */
ao2_t_global_obj_release(astobj2_array, "Check release all objects"); ao2_t_global_obj_release(astobj2_holder, "Check release all objects");
destructor_count += num_objects; destructor_count += num_objects;
if (0 < destructor_count) { if (0 < destructor_count) {
ast_test_status_update(test, ast_test_status_update(test,
@@ -695,7 +693,7 @@ cleanup:
if (obj3) { if (obj3) {
ao2_t_ref(obj3, -1, "Test cleanup external object 3"); ao2_t_ref(obj3, -1, "Test cleanup external object 3");
} }
ao2_t_global_obj_release(astobj2_array, "Test cleanup array"); ao2_t_global_obj_release(astobj2_holder, "Test cleanup holder");
return res; return res;
} }