mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-03 11:25:35 +00:00
astobj2: Additional refactoring to push impl specific code down into the impls.
Move some implementation specific code from astobj2_container.c into astobj2_hash.c and astobj2_rbtree.c. This completely removes the need for astobj2_container to switch on RTTI and it no longer has any knowledge of the implementation details. Also adds AO2_DEBUG as a new compile option in menuselect which controls astobj2 debugging independently of AST_DEVMODE and REF_DEBUG. Tested by: George Joseph Review: https://reviewboard.asterisk.org/r/3593/ ........ Merged revisions 416806 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@416807 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -8,6 +8,9 @@
|
||||
<member name="REF_DEBUG" displayname="Enable reference count debugging">
|
||||
<support_level>extended</support_level>
|
||||
</member>
|
||||
<member name="AO2_DEBUG" displayname="Enable internal Astobj2 debugging">
|
||||
<support_level>extended</support_level>
|
||||
</member>
|
||||
<member name="STATIC_BUILD" displayname="Build static binaries">
|
||||
<support_level>extended</support_level>
|
||||
</member>
|
||||
|
@@ -38,6 +38,49 @@ int ao2_container_count(struct ao2_container *c)
|
||||
return ast_atomic_fetchadd_int(&c->elements, 0);
|
||||
}
|
||||
|
||||
int __container_unlink_node_debug(struct ao2_container_node *node, uint32_t flags,
|
||||
const char *tag, const char *file, int line, const char *func)
|
||||
{
|
||||
struct ao2_container *container = node->my_container;
|
||||
|
||||
if (container == NULL && (flags & AO2_UNLINK_NODE_DEC_COUNT)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((flags & AO2_UNLINK_NODE_UNLINK_OBJECT)
|
||||
&& !(flags & AO2_UNLINK_NODE_NOUNREF_OBJECT)) {
|
||||
if (tag) {
|
||||
__ao2_ref_debug(node->obj, -1, tag, file, line, func);
|
||||
} else {
|
||||
ao2_t_ref(node->obj, -1, "Remove obj from container");
|
||||
}
|
||||
}
|
||||
|
||||
node->obj = NULL;
|
||||
|
||||
if (flags & AO2_UNLINK_NODE_DEC_COUNT) {
|
||||
ast_atomic_fetchadd_int(&container->elements, -1);
|
||||
#if defined(AO2_DEBUG)
|
||||
{
|
||||
int empty = container->nodes - container->elements;
|
||||
|
||||
if (container->max_empty_nodes < empty) {
|
||||
container->max_empty_nodes = empty;
|
||||
}
|
||||
if (container->v_table->unlink_stat) {
|
||||
container->v_table->unlink_stat(container, node);
|
||||
}
|
||||
}
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
}
|
||||
|
||||
if (flags & AO2_UNLINK_NODE_UNREF_NODE) {
|
||||
ao2_t_ref(node, -1, "Remove node from container");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Link an object into this container. (internal)
|
||||
@@ -76,71 +119,36 @@ static int internal_ao2_link(struct ao2_container *self, void *obj_new, int flag
|
||||
res = 0;
|
||||
node = self->v_table->new_node(self, obj_new, tag, file, line, func);
|
||||
if (node) {
|
||||
#if defined(AO2_DEBUG) && defined(AST_DEVMODE)
|
||||
switch (self->v_table->type) {
|
||||
case AO2_CONTAINER_RTTI_HASH:
|
||||
if (!self->sort_fn) {
|
||||
/*
|
||||
* XXX chan_iax2 plays games with the hash function so we cannot
|
||||
* routinely do an integrity check on this type of container.
|
||||
* chan_iax2 should be changed to not abuse the hash function.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
/* Fall through. */
|
||||
case AO2_CONTAINER_RTTI_RBTREE:
|
||||
if (ao2_container_check(self, OBJ_NOLOCK)) {
|
||||
ast_log(LOG_ERROR, "Container integrity failed before insert.\n");
|
||||
}
|
||||
break;
|
||||
#if defined(AO2_DEBUG)
|
||||
if (ao2_container_check(self, OBJ_NOLOCK)) {
|
||||
ast_log(LOG_ERROR, "Container integrity failed before insert.\n");
|
||||
}
|
||||
#endif /* defined(AO2_DEBUG) && defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
/* Insert the new node. */
|
||||
switch (self->v_table->insert(self, node)) {
|
||||
case AO2_CONTAINER_INSERT_NODE_INSERTED:
|
||||
node->is_linked = 1;
|
||||
ast_atomic_fetchadd_int(&self->elements, 1);
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
AO2_DEVMODE_STAT(++self->nodes);
|
||||
switch (self->v_table->type) {
|
||||
case AO2_CONTAINER_RTTI_HASH:
|
||||
hash_ao2_link_node_stat(self, node);
|
||||
break;
|
||||
case AO2_CONTAINER_RTTI_RBTREE:
|
||||
break;
|
||||
if (self->v_table->link_stat) {
|
||||
self->v_table->link_stat(self, node);
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
/* Fall through */
|
||||
case AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED:
|
||||
#if defined(AO2_DEBUG)
|
||||
if (ao2_container_check(self, OBJ_NOLOCK)) {
|
||||
ast_log(LOG_ERROR, "Container integrity failed after insert or replace.\n");
|
||||
}
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
res = 1;
|
||||
break;
|
||||
case AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED:
|
||||
res = 1;
|
||||
/* Fall through */
|
||||
case AO2_CONTAINER_INSERT_NODE_REJECTED:
|
||||
__ao2_ref(node, -1);
|
||||
break;
|
||||
}
|
||||
#if defined(AO2_DEBUG) && defined(AST_DEVMODE)
|
||||
if (res) {
|
||||
switch (self->v_table->type) {
|
||||
case AO2_CONTAINER_RTTI_HASH:
|
||||
if (!self->sort_fn) {
|
||||
/*
|
||||
* XXX chan_iax2 plays games with the hash function so we cannot
|
||||
* routinely do an integrity check on this type of container.
|
||||
* chan_iax2 should be changed to not abuse the hash function.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
/* Fall through. */
|
||||
case AO2_CONTAINER_RTTI_RBTREE:
|
||||
if (ao2_container_check(self, OBJ_NOLOCK)) {
|
||||
ast_log(LOG_ERROR, "Container integrity failed after insert.\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* defined(AO2_DEBUG) && defined(AST_DEVMODE) */
|
||||
}
|
||||
|
||||
if (flags & OBJ_NOLOCK) {
|
||||
@@ -391,48 +399,11 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags
|
||||
}
|
||||
|
||||
if (flags & OBJ_UNLINK) {
|
||||
/* update number of elements */
|
||||
ast_atomic_fetchadd_int(&self->elements, -1);
|
||||
#if defined(AST_DEVMODE)
|
||||
{
|
||||
int empty = self->nodes - self->elements;
|
||||
|
||||
if (self->max_empty_nodes < empty) {
|
||||
self->max_empty_nodes = empty;
|
||||
}
|
||||
}
|
||||
switch (self->v_table->type) {
|
||||
case AO2_CONTAINER_RTTI_HASH:
|
||||
hash_ao2_unlink_node_stat(self, node);
|
||||
break;
|
||||
case AO2_CONTAINER_RTTI_RBTREE:
|
||||
break;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
|
||||
/*
|
||||
* - When unlinking and not returning the result, OBJ_NODATA is
|
||||
* set, the ref from the container must be decremented.
|
||||
*
|
||||
* - When unlinking with a multi_container the ref from the
|
||||
* original container must be decremented. This is because the
|
||||
* result is 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.
|
||||
*/
|
||||
int ulflag = AO2_UNLINK_NODE_UNREF_NODE | AO2_UNLINK_NODE_DEC_COUNT;
|
||||
if (multi_container || (flags & OBJ_NODATA)) {
|
||||
if (tag) {
|
||||
__ao2_ref_debug(node->obj, -1, tag, file, line, func);
|
||||
} else {
|
||||
ao2_t_ref(node->obj, -1, "Unlink container obj reference.");
|
||||
}
|
||||
ulflag |= AO2_UNLINK_NODE_UNLINK_OBJECT;
|
||||
}
|
||||
node->obj = NULL;
|
||||
|
||||
/* Unref the node from the container. */
|
||||
__ao2_ref(node, -1);
|
||||
__container_unlink_node_debug(node, ulflag, tag, file, line, func);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,27 +601,8 @@ static void *internal_ao2_iterator_next(struct ao2_iterator *iter, const char *t
|
||||
ret = node->obj;
|
||||
|
||||
if (iter->flags & AO2_ITERATOR_UNLINK) {
|
||||
/* update number of elements */
|
||||
ast_atomic_fetchadd_int(&iter->c->elements, -1);
|
||||
#if defined(AST_DEVMODE)
|
||||
{
|
||||
int empty = iter->c->nodes - iter->c->elements;
|
||||
|
||||
if (iter->c->max_empty_nodes < empty) {
|
||||
iter->c->max_empty_nodes = empty;
|
||||
}
|
||||
}
|
||||
switch (iter->c->v_table->type) {
|
||||
case AO2_CONTAINER_RTTI_HASH:
|
||||
hash_ao2_unlink_node_stat(iter->c, node);
|
||||
break;
|
||||
case AO2_CONTAINER_RTTI_RBTREE:
|
||||
break;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
|
||||
/* Transfer the object ref from the container to the returned object. */
|
||||
node->obj = NULL;
|
||||
__container_unlink_node_debug(node, AO2_UNLINK_NODE_DEC_COUNT, tag, file, line, func);
|
||||
|
||||
/* Transfer the container's node ref to the iterator. */
|
||||
} else {
|
||||
@@ -713,7 +665,7 @@ void container_destruct(void *_c)
|
||||
c->v_table->destroy(c);
|
||||
}
|
||||
|
||||
#ifdef AO2_DEBUG
|
||||
#if defined(AO2_DEBUG)
|
||||
ast_atomic_fetchadd_int(&ao2.total_containers, -1);
|
||||
#endif
|
||||
}
|
||||
@@ -732,7 +684,7 @@ void container_destruct_debug(void *_c)
|
||||
c->v_table->destroy(c);
|
||||
}
|
||||
|
||||
#ifdef AO2_DEBUG
|
||||
#if defined(AO2_DEBUG)
|
||||
ast_atomic_fetchadd_int(&ao2.total_containers, -1);
|
||||
#endif
|
||||
}
|
||||
@@ -863,11 +815,11 @@ void ao2_container_dump(struct ao2_container *self, enum search_flags flags, con
|
||||
if (name) {
|
||||
prnt(where, "Container name: %s\n", name);
|
||||
}
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
if (self->v_table->dump) {
|
||||
self->v_table->dump(self, where, prnt, prnt_obj);
|
||||
} else
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
{
|
||||
prnt(where, "Container dump not available.\n");
|
||||
}
|
||||
@@ -891,7 +843,7 @@ void ao2_container_stats(struct ao2_container *self, enum search_flags flags, co
|
||||
prnt(where, "Container name: %s\n", name);
|
||||
}
|
||||
prnt(where, "Number of objects: %d\n", self->elements);
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
prnt(where, "Number of nodes: %d\n", self->nodes);
|
||||
prnt(where, "Number of empty nodes: %d\n", self->nodes - self->elements);
|
||||
/*
|
||||
@@ -907,7 +859,7 @@ void ao2_container_stats(struct ao2_container *self, enum search_flags flags, co
|
||||
if (self->v_table->stats) {
|
||||
self->v_table->stats(self, where, prnt);
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
if (!(flags & OBJ_NOLOCK)) {
|
||||
ao2_unlock(self);
|
||||
}
|
||||
@@ -922,7 +874,7 @@ int ao2_container_check(struct ao2_container *self, enum search_flags flags)
|
||||
ast_assert(0);
|
||||
return -1;
|
||||
}
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
if (!self->v_table->integrity) {
|
||||
/* No ingetrigy check available. Assume container is ok. */
|
||||
return 0;
|
||||
@@ -935,11 +887,11 @@ int ao2_container_check(struct ao2_container *self, enum search_flags flags)
|
||||
if (!(flags & OBJ_NOLOCK)) {
|
||||
ao2_unlock(self);
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
return res;
|
||||
}
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
static struct ao2_container *reg_containers;
|
||||
|
||||
struct ao2_reg_container {
|
||||
@@ -964,9 +916,9 @@ struct ao2_reg_match {
|
||||
/*! Count of the matches already found. */
|
||||
int count;
|
||||
};
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
static int ao2_reg_sort_cb(const void *obj_left, const void *obj_right, int flags)
|
||||
{
|
||||
const struct ao2_reg_container *reg_left = obj_left;
|
||||
@@ -1002,9 +954,9 @@ static int ao2_reg_sort_cb(const void *obj_left, const void *obj_right, int flag
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
static void ao2_reg_destructor(void *v_doomed)
|
||||
{
|
||||
struct ao2_reg_container *doomed = v_doomed;
|
||||
@@ -1013,12 +965,12 @@ static void ao2_reg_destructor(void *v_doomed)
|
||||
ao2_t_ref(doomed->registered, -1, "Releasing registered container.");
|
||||
}
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
int ao2_container_register(const char *name, struct ao2_container *self, ao2_prnt_obj_fn *prnt_obj)
|
||||
{
|
||||
int res = 0;
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
struct ao2_reg_container *reg;
|
||||
|
||||
reg = ao2_t_alloc_options(sizeof(*reg) + strlen(name), ao2_reg_destructor,
|
||||
@@ -1038,19 +990,19 @@ int ao2_container_register(const char *name, struct ao2_container *self, ao2_prn
|
||||
}
|
||||
|
||||
ao2_t_ref(reg, -1, "Done registering container.");
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
return res;
|
||||
}
|
||||
|
||||
void ao2_container_unregister(const char *name)
|
||||
{
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
ao2_t_find(reg_containers, name, OBJ_UNLINK | OBJ_NODATA | OBJ_SEARCH_KEY,
|
||||
"Unregister container");
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
}
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
static int ao2_complete_reg_cb(void *obj, void *arg, void *data, int flags)
|
||||
{
|
||||
struct ao2_reg_match *which = data;
|
||||
@@ -1058,9 +1010,9 @@ static int ao2_complete_reg_cb(void *obj, void *arg, void *data, int flags)
|
||||
/* ao2_reg_sort_cb() has already filtered the search to matching keys */
|
||||
return (which->find_nth < ++which->count) ? (CMP_MATCH | CMP_STOP) : 0;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
static char *complete_container_names(struct ast_cli_args *a)
|
||||
{
|
||||
struct ao2_reg_partial_key partial_key;
|
||||
@@ -1086,9 +1038,9 @@ static char *complete_container_names(struct ast_cli_args *a)
|
||||
}
|
||||
return name;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
AST_THREADSTORAGE(ao2_out_buf);
|
||||
|
||||
/*!
|
||||
@@ -1120,9 +1072,9 @@ static void cli_output(void *where, const char *fmt, ...)
|
||||
ast_cli(*(int *) where, "%s", ast_str_buffer(buf));
|
||||
}
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*! \brief Show container contents - CLI command */
|
||||
static char *handle_cli_astobj2_container_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
@@ -1156,9 +1108,9 @@ static char *handle_cli_astobj2_container_dump(struct ast_cli_entry *e, int cmd,
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*! \brief Show container statistics - CLI command */
|
||||
static char *handle_cli_astobj2_container_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
@@ -1191,9 +1143,9 @@ static char *handle_cli_astobj2_container_stats(struct ast_cli_entry *e, int cmd
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*! \brief Show container check results - CLI command */
|
||||
static char *handle_cli_astobj2_container_check(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
@@ -1227,17 +1179,17 @@ static char *handle_cli_astobj2_container_check(struct ast_cli_entry *e, int cmd
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
static struct ast_cli_entry cli_astobj2[] = {
|
||||
AST_CLI_DEFINE(handle_cli_astobj2_container_dump, "Show container contents"),
|
||||
AST_CLI_DEFINE(handle_cli_astobj2_container_stats, "Show container statistics"),
|
||||
AST_CLI_DEFINE(handle_cli_astobj2_container_check, "Perform a container integrity check"),
|
||||
};
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
static void container_cleanup(void)
|
||||
{
|
||||
ao2_t_ref(reg_containers, -1, "Releasing container registration container");
|
||||
@@ -1245,11 +1197,11 @@ static void container_cleanup(void)
|
||||
|
||||
ast_cli_unregister_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
int container_init(void)
|
||||
{
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
reg_containers = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK,
|
||||
AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, ao2_reg_sort_cb, NULL,
|
||||
"Container registration container.");
|
||||
@@ -1259,7 +1211,7 @@ int container_init(void)
|
||||
|
||||
ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
|
||||
ast_register_atexit(container_cleanup);
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -26,6 +26,22 @@
|
||||
|
||||
#include "asterisk/astobj2.h"
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Enum for internal_ao2_unlink_node.
|
||||
*/
|
||||
enum ao2_unlink_node_flags {
|
||||
/*! Remove the node from the object's weak link list
|
||||
* OR unref the object if it's a strong reference. */
|
||||
AO2_UNLINK_NODE_UNLINK_OBJECT = (1 << 0),
|
||||
/*! Modified unlink_object to skip the unref of the object. */
|
||||
AO2_UNLINK_NODE_NOUNREF_OBJECT = (1 << 1),
|
||||
/*! Unref the node. */
|
||||
AO2_UNLINK_NODE_UNREF_NODE = (1 << 2),
|
||||
/*! Decrement the container's element count. */
|
||||
AO2_UNLINK_NODE_DEC_COUNT = (1 << 3),
|
||||
};
|
||||
|
||||
enum ao2_callback_type {
|
||||
AO2_CALLBACK_DEFAULT,
|
||||
AO2_CALLBACK_WITH_DATA,
|
||||
@@ -40,13 +56,6 @@ enum ao2_container_insert {
|
||||
AO2_CONTAINER_INSERT_NODE_REJECTED,
|
||||
};
|
||||
|
||||
enum ao2_container_rtti {
|
||||
/*! This is a hash container */
|
||||
AO2_CONTAINER_RTTI_HASH,
|
||||
/*! This is a red-black tree container */
|
||||
AO2_CONTAINER_RTTI_RBTREE,
|
||||
};
|
||||
|
||||
/*! Allow enough room for container specific traversal state structs */
|
||||
#define AO2_TRAVERSAL_STATE_SIZE 100
|
||||
|
||||
@@ -211,10 +220,32 @@ typedef void (*ao2_container_statistics)(struct ao2_container *self, void *where
|
||||
*/
|
||||
typedef int (*ao2_container_integrity)(struct ao2_container *self);
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Increment the container linked object statistic.
|
||||
* \since 12.4.0
|
||||
*
|
||||
* \param container Container to operate upon.
|
||||
* \param node Container node linking object to.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
typedef void (*ao2_link_node_stat_fn)(struct ao2_container *container, struct ao2_container_node *node);
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Decrement the container linked object statistic.
|
||||
* \since 12.4.0
|
||||
*
|
||||
* \param container Container to operate upon.
|
||||
* \param node Container node unlinking object from.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
typedef void (*ao2_unlink_node_stat_fn)(struct ao2_container *container, struct ao2_container_node *node);
|
||||
|
||||
/*! Container virtual methods template. */
|
||||
struct ao2_container_methods {
|
||||
/*! Run Time Type Identification */
|
||||
enum ao2_container_rtti type;
|
||||
/*! Destroy this container. */
|
||||
ao2_container_destroy_fn destroy;
|
||||
/*! \brief Create an empty copy of this container. */
|
||||
@@ -233,14 +264,18 @@ struct ao2_container_methods {
|
||||
ao2_container_find_cleanup_fn traverse_cleanup;
|
||||
/*! Find the next iteration element in the container. */
|
||||
ao2_iterator_next_fn iterator_next;
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*! Increment the container linked object statistic. */
|
||||
ao2_link_node_stat_fn link_stat;
|
||||
/*! Deccrement the container linked object statistic. */
|
||||
ao2_unlink_node_stat_fn unlink_stat;
|
||||
/*! Display container contents. (Method for debug purposes) */
|
||||
ao2_container_display dump;
|
||||
/*! Display container debug statistics. (Method for debug purposes) */
|
||||
ao2_container_statistics stats;
|
||||
/*! Perform an integrity check on the container. (Method for debug purposes) */
|
||||
ao2_container_integrity integrity;
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -268,12 +303,12 @@ struct ao2_container {
|
||||
uint32_t options;
|
||||
/*! Number of elements in the container. */
|
||||
int elements;
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*! Number of nodes in the container. */
|
||||
int nodes;
|
||||
/*! Maximum number of empty nodes in the container. (nodes - elements) */
|
||||
int max_empty_nodes;
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
/*!
|
||||
* \brief TRUE if the container is being destroyed.
|
||||
*
|
||||
@@ -287,10 +322,21 @@ struct ao2_container {
|
||||
unsigned int destroying:1;
|
||||
};
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
void hash_ao2_link_node_stat(struct ao2_container *hash, struct ao2_container_node *hash_node);
|
||||
void hash_ao2_unlink_node_stat(struct ao2_container *hash, struct ao2_container_node *hash_node);
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Unlink a node from this container.
|
||||
*
|
||||
* \param node Node to operate upon.
|
||||
* \param flags ao2_unlink_node_flags governing behavior.
|
||||
*
|
||||
* \retval 0 on errors.
|
||||
* \retval 1 on success.
|
||||
*/
|
||||
int __container_unlink_node_debug(struct ao2_container_node *node, uint32_t flags,
|
||||
const char *tag, const char *file, int line, const char *func);
|
||||
|
||||
#define __container_unlink_node(node, flags) \
|
||||
__container_unlink_node_debug(node, flags, NULL, NULL, 0, NULL)
|
||||
|
||||
void container_destruct(void *_c);
|
||||
void container_destruct_debug(void *_c);
|
||||
|
@@ -51,12 +51,12 @@ struct hash_bucket_node {
|
||||
struct hash_bucket {
|
||||
/*! List of objects held in the bucket. */
|
||||
AST_DLLIST_HEAD_NOLOCK(, hash_bucket_node) list;
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*! Number of elements currently in the bucket. */
|
||||
int elements;
|
||||
/*! Maximum number of elements in the bucket. */
|
||||
int max_elements;
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -188,18 +188,12 @@ static void hash_ao2_node_destructor(void *v_doomed)
|
||||
my_container = (struct ao2_container_hash *) doomed->common.my_container;
|
||||
__adjust_lock(my_container, AO2_LOCK_REQ_WRLOCK, 1);
|
||||
|
||||
#if defined(AO2_DEBUG) && defined(AST_DEVMODE)
|
||||
/*
|
||||
* XXX chan_iax2 plays games with the hash function so we cannot
|
||||
* routinely do an integrity check on this type of container.
|
||||
* chan_iax2 should be changed to not abuse the hash function.
|
||||
*/
|
||||
#if defined(AO2_DEBUG)
|
||||
if (!my_container->common.destroying
|
||||
&& my_container->common.sort_fn
|
||||
&& ao2_container_check(doomed->common.my_container, OBJ_NOLOCK)) {
|
||||
ast_log(LOG_ERROR, "Container integrity failed before node deletion.\n");
|
||||
}
|
||||
#endif /* defined(AO2_DEBUG) && defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
bucket = &my_container->buckets[doomed->my_bucket];
|
||||
AST_DLLIST_REMOVE(&bucket->list, doomed, links);
|
||||
AO2_DEVMODE_STAT(--my_container->common.nodes);
|
||||
@@ -210,8 +204,7 @@ static void hash_ao2_node_destructor(void *v_doomed)
|
||||
* destroyed or the node had not been linked in yet.
|
||||
*/
|
||||
if (doomed->common.obj) {
|
||||
ao2_t_ref(doomed->common.obj, -1, "Container node destruction");
|
||||
doomed->common.obj = NULL;
|
||||
__container_unlink_node(&doomed->common, AO2_UNLINK_NODE_UNLINK_OBJECT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +258,8 @@ static struct hash_bucket_node *hash_ao2_new_node(struct ao2_container_hash *sel
|
||||
*
|
||||
* \return enum ao2_container_insert value.
|
||||
*/
|
||||
static enum ao2_container_insert hash_ao2_insert_node(struct ao2_container_hash *self, struct hash_bucket_node *node)
|
||||
static enum ao2_container_insert hash_ao2_insert_node(struct ao2_container_hash *self,
|
||||
struct hash_bucket_node *node)
|
||||
{
|
||||
int cmp;
|
||||
struct hash_bucket *bucket;
|
||||
@@ -303,6 +297,7 @@ static enum ao2_container_insert hash_ao2_insert_node(struct ao2_container_hash
|
||||
break;
|
||||
case AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE:
|
||||
SWAP(cur->common.obj, node->common.obj);
|
||||
ao2_t_ref(node, -1, "Discard the new node.");
|
||||
return AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED;
|
||||
}
|
||||
}
|
||||
@@ -335,6 +330,7 @@ static enum ao2_container_insert hash_ao2_insert_node(struct ao2_container_hash
|
||||
break;
|
||||
case AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE:
|
||||
SWAP(cur->common.obj, node->common.obj);
|
||||
ao2_t_ref(node, -1, "Discard the new node.");
|
||||
return AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED;
|
||||
}
|
||||
}
|
||||
@@ -708,7 +704,7 @@ static struct hash_bucket_node *hash_ao2_iterator_next(struct ao2_container_hash
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Increment the hash container linked object statistic.
|
||||
@@ -719,7 +715,7 @@ static struct hash_bucket_node *hash_ao2_iterator_next(struct ao2_container_hash
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void hash_ao2_link_node_stat(struct ao2_container *hash, struct ao2_container_node *hash_node)
|
||||
static void hash_ao2_link_node_stat(struct ao2_container *hash, struct ao2_container_node *hash_node)
|
||||
{
|
||||
struct ao2_container_hash *self = (struct ao2_container_hash *) hash;
|
||||
struct hash_bucket_node *node = (struct hash_bucket_node *) hash_node;
|
||||
@@ -730,9 +726,9 @@ void hash_ao2_link_node_stat(struct ao2_container *hash, struct ao2_container_no
|
||||
self->buckets[i].max_elements = self->buckets[i].elements;
|
||||
}
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Decrement the hash container linked object statistic.
|
||||
@@ -743,14 +739,14 @@ void hash_ao2_link_node_stat(struct ao2_container *hash, struct ao2_container_no
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void hash_ao2_unlink_node_stat(struct ao2_container *hash, struct ao2_container_node *hash_node)
|
||||
static void hash_ao2_unlink_node_stat(struct ao2_container *hash, struct ao2_container_node *hash_node)
|
||||
{
|
||||
struct ao2_container_hash *self = (struct ao2_container_hash *) hash;
|
||||
struct hash_bucket_node *node = (struct hash_bucket_node *) hash_node;
|
||||
|
||||
--self->buckets[node->my_bucket].elements;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
@@ -776,7 +772,7 @@ static void hash_ao2_destroy(struct ao2_container_hash *self)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Display contents of the specified container.
|
||||
@@ -828,9 +824,9 @@ static void hash_ao2_dump(struct ao2_container_hash *self, void *where, ao2_prnt
|
||||
#undef FORMAT
|
||||
#undef FORMAT2
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Display statistics of the specified container.
|
||||
@@ -869,9 +865,9 @@ static void hash_ao2_stats(struct ao2_container_hash *self, void *where, ao2_prn
|
||||
#undef FORMAT
|
||||
#undef FORMAT2
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Perform an integrity check on the specified container.
|
||||
@@ -1034,11 +1030,10 @@ static int hash_ao2_integrity(struct ao2_container_hash *self)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
/*! Hash container virtual method table. */
|
||||
static const struct ao2_container_methods v_table_hash = {
|
||||
.type = AO2_CONTAINER_RTTI_HASH,
|
||||
.alloc_empty_clone = (ao2_container_alloc_empty_clone_fn) hash_ao2_alloc_empty_clone,
|
||||
.alloc_empty_clone_debug =
|
||||
(ao2_container_alloc_empty_clone_debug_fn) hash_ao2_alloc_empty_clone_debug,
|
||||
@@ -1048,11 +1043,13 @@ static const struct ao2_container_methods v_table_hash = {
|
||||
.traverse_next = (ao2_container_find_next_fn) hash_ao2_find_next,
|
||||
.iterator_next = (ao2_iterator_next_fn) hash_ao2_iterator_next,
|
||||
.destroy = (ao2_container_destroy_fn) hash_ao2_destroy,
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
.link_stat = hash_ao2_link_node_stat,
|
||||
.unlink_stat = hash_ao2_unlink_node_stat,
|
||||
.dump = (ao2_container_display) hash_ao2_dump,
|
||||
.stats = (ao2_container_statistics) hash_ao2_stats,
|
||||
.integrity = (ao2_container_integrity) hash_ao2_integrity,
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -1098,7 +1095,7 @@ static struct ao2_container *hash_ao2_container_init(
|
||||
|
||||
#ifdef AO2_DEBUG
|
||||
ast_atomic_fetchadd_int(&ao2.total_containers, 1);
|
||||
#endif
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
return (struct ao2_container *) self;
|
||||
}
|
||||
|
@@ -26,16 +26,11 @@
|
||||
|
||||
#include "asterisk/astobj2.h"
|
||||
|
||||
#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) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
#define AO2_DEVMODE_STAT(stat) stat
|
||||
#else
|
||||
#define AO2_DEVMODE_STAT(stat)
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#ifdef AO2_DEBUG
|
||||
struct ao2_stats {
|
||||
@@ -46,7 +41,7 @@ struct ao2_stats {
|
||||
volatile int total_locked;
|
||||
};
|
||||
extern struct ao2_stats ao2;
|
||||
#endif
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
int is_ao2_object(void *user_data);
|
||||
enum ao2_lock_req __adjust_lock(void *user_data, enum ao2_lock_req lock_how, int keep_stronger);
|
||||
|
@@ -79,7 +79,7 @@ struct ao2_container_rbtree {
|
||||
struct ao2_container common;
|
||||
/*! Root node of the tree. NULL if the tree is empty. */
|
||||
struct rbtree_node *root;
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
struct {
|
||||
/*! Fixup insert left cases 1-3 */
|
||||
int fixup_insert_left[3];
|
||||
@@ -92,7 +92,7 @@ struct ao2_container_rbtree {
|
||||
/*! Deletion of node with number of children (0-2). */
|
||||
int delete_children[3];
|
||||
} stats;
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
};
|
||||
|
||||
enum equal_node_bias {
|
||||
@@ -880,19 +880,19 @@ static void rb_ao2_node_destructor(void *v_doomed)
|
||||
my_container = (struct ao2_container_rbtree *) doomed->common.my_container;
|
||||
__adjust_lock(my_container, AO2_LOCK_REQ_WRLOCK, 1);
|
||||
|
||||
#if defined(AO2_DEBUG) && defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
if (!my_container->common.destroying
|
||||
&& ao2_container_check(doomed->common.my_container, OBJ_NOLOCK)) {
|
||||
ast_log(LOG_ERROR, "Container integrity failed before node deletion.\n");
|
||||
}
|
||||
#endif /* defined(AO2_DEBUG) && defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
rb_delete_node(my_container, doomed);
|
||||
#if defined(AO2_DEBUG) && defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
if (!my_container->common.destroying
|
||||
&& ao2_container_check(doomed->common.my_container, OBJ_NOLOCK)) {
|
||||
ast_log(LOG_ERROR, "Container integrity failed after node deletion.\n");
|
||||
}
|
||||
#endif /* defined(AO2_DEBUG) && defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -900,8 +900,7 @@ static void rb_ao2_node_destructor(void *v_doomed)
|
||||
* destroyed or the node had not been linked in yet.
|
||||
*/
|
||||
if (doomed->common.obj) {
|
||||
ao2_t_ref(doomed->common.obj, -1, "Container node destruction");
|
||||
doomed->common.obj = NULL;
|
||||
__container_unlink_node(&doomed->common, AO2_UNLINK_NODE_UNLINK_OBJECT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1266,6 +1265,7 @@ static enum ao2_container_insert rb_ao2_insert_node(struct ao2_container_rbtree
|
||||
break;
|
||||
case AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE:
|
||||
SWAP(cur->common.obj, node->common.obj);
|
||||
ao2_t_ref(node, -1, "Don't need the new node.");
|
||||
return AO2_CONTAINER_INSERT_NODE_OBJ_REPLACED;
|
||||
}
|
||||
|
||||
@@ -1707,7 +1707,7 @@ static void rb_ao2_destroy(struct ao2_container_rbtree *self)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Display contents of the specified container.
|
||||
@@ -1745,9 +1745,9 @@ static void rb_ao2_dump(struct ao2_container_rbtree *self, void *where, ao2_prnt
|
||||
#undef FORMAT
|
||||
#undef FORMAT2
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Display statistics of the specified container.
|
||||
@@ -1787,9 +1787,9 @@ static void rb_ao2_stats(struct ao2_container_rbtree *self, void *where, ao2_prn
|
||||
self->stats.fixup_delete_right[idx]);
|
||||
}
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Check the black height of the given node.
|
||||
@@ -1831,9 +1831,9 @@ static int rb_check_black_height(struct rbtree_node *node)
|
||||
return height_left;
|
||||
}
|
||||
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Perform an integrity check on the specified container.
|
||||
@@ -2011,11 +2011,10 @@ static int rb_ao2_integrity(struct ao2_container_rbtree *self)
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
/*! rbtree container virtual method table. */
|
||||
static const struct ao2_container_methods v_table_rbtree = {
|
||||
.type = AO2_CONTAINER_RTTI_RBTREE,
|
||||
.alloc_empty_clone = (ao2_container_alloc_empty_clone_fn) rb_ao2_alloc_empty_clone,
|
||||
.alloc_empty_clone_debug =
|
||||
(ao2_container_alloc_empty_clone_debug_fn) rb_ao2_alloc_empty_clone_debug,
|
||||
@@ -2025,11 +2024,11 @@ static const struct ao2_container_methods v_table_rbtree = {
|
||||
.traverse_next = (ao2_container_find_next_fn) rb_ao2_find_next,
|
||||
.iterator_next = (ao2_iterator_next_fn) rb_ao2_iterator_next,
|
||||
.destroy = (ao2_container_destroy_fn) rb_ao2_destroy,
|
||||
#if defined(AST_DEVMODE)
|
||||
#if defined(AO2_DEBUG)
|
||||
.dump = (ao2_container_display) rb_ao2_dump,
|
||||
.stats = (ao2_container_statistics) rb_ao2_stats,
|
||||
.integrity = (ao2_container_integrity) rb_ao2_integrity,
|
||||
#endif /* defined(AST_DEVMODE) */
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -2056,7 +2055,7 @@ static struct ao2_container *rb_ao2_container_init(struct ao2_container_rbtree *
|
||||
|
||||
#ifdef AO2_DEBUG
|
||||
ast_atomic_fetchadd_int(&ao2.total_containers, 1);
|
||||
#endif
|
||||
#endif /* defined(AO2_DEBUG) */
|
||||
|
||||
return (struct ao2_container *) self;
|
||||
}
|
||||
|
@@ -1927,7 +1927,10 @@ AST_TEST_DEFINE(astobj2_test_4)
|
||||
static enum ast_test_result_state test_performance(struct ast_test *test,
|
||||
enum test_container_type type, unsigned int copt)
|
||||
{
|
||||
#define OBJS 256
|
||||
/*!
|
||||
* \brief The number of objects inserted and searched for in the container under test.
|
||||
*/
|
||||
#define OBJS 73
|
||||
int res = AST_TEST_PASS;
|
||||
struct ao2_container *c1 = NULL;
|
||||
struct test_obj *tobj[OBJS];
|
||||
@@ -1989,47 +1992,58 @@ test_cleanup:
|
||||
}
|
||||
|
||||
static enum ast_test_result_state testloop(struct ast_test *test,
|
||||
enum test_container_type type, int copt)
|
||||
enum test_container_type type, int copt, int iterations)
|
||||
{
|
||||
#define ITERATIONS 2500
|
||||
int res = AST_TEST_PASS;
|
||||
int i;
|
||||
struct timeval start;
|
||||
|
||||
start = ast_tvnow();
|
||||
for (i = 1 ; i <= ITERATIONS && res == AST_TEST_PASS ; i++) {
|
||||
for (i = 1 ; i <= iterations && res == AST_TEST_PASS ; i++) {
|
||||
res = test_performance(test, type, copt);
|
||||
}
|
||||
ast_test_status_update(test, "%dK traversals, %9s : %5lu ms\n",
|
||||
ITERATIONS / 1000, test_container2str(type), (unsigned long)ast_tvdiff_ms(ast_tvnow(), start));
|
||||
ast_test_status_update(test, "%5.2fK traversals, %9s : %5lu ms\n",
|
||||
iterations / 1000.0, test_container2str(type), (unsigned long)ast_tvdiff_ms(ast_tvnow(), start));
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(astobj2_test_perf)
|
||||
{
|
||||
/*!
|
||||
* \brief The number of iteration of testloop to be performed.
|
||||
* \note
|
||||
* In order to keep the elapsed time sane, if AO2_DEBUG is defined in menuselect,
|
||||
* only 25000 iterations are performed. Otherwise 100000.
|
||||
*/
|
||||
#ifdef AO2_DEBUG
|
||||
#define ITERATIONS 25000
|
||||
#else
|
||||
#define ITERATIONS 100000
|
||||
#endif
|
||||
|
||||
int res = AST_TEST_PASS;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "astobj2_test_perf";
|
||||
info->category = "/main/astobj2/";
|
||||
info->category = "/main/astobj2/perf";
|
||||
info->summary = "Test container performance";
|
||||
info->description =
|
||||
"Runs 100000 container traversal tests.";
|
||||
"Runs container traversal tests.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
res = testloop(test, TEST_CONTAINER_LIST, 0);
|
||||
res = testloop(test, TEST_CONTAINER_LIST, 0, ITERATIONS);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
res = testloop(test, TEST_CONTAINER_HASH, 0);
|
||||
res = testloop(test, TEST_CONTAINER_HASH, 0, ITERATIONS);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
res = testloop(test, TEST_CONTAINER_RBTREE, 0);
|
||||
res = testloop(test, TEST_CONTAINER_RBTREE, 0, ITERATIONS);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
Reference in New Issue
Block a user