diff --git a/build_tools/cflags.xml b/build_tools/cflags.xml index 3702192d6773a8440355716f3f11693203d149b6..082e8e63fd8d05a989507c00ab443219273ea984 100644 --- a/build_tools/cflags.xml +++ b/build_tools/cflags.xml @@ -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> diff --git a/main/astobj2_container.c b/main/astobj2_container.c index 987116e0d1e9a1e21f33ba59ac26d114b75a7963..bf53d8f2a29fdb486e6a4536ed6facc17ee8dfe1 100644 --- a/main/astobj2_container.c +++ b/main/astobj2_container.c @@ -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) */ - - res = 1; - break; +#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; - /* Fall through */ + break; 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; } diff --git a/main/astobj2_container_private.h b/main/astobj2_container_private.h index 9730cf41f23e461d55317feeb31cb522f5d8367a..07779a5f960373d8fb55c3af2a274f4db4497e30 100644 --- a/main/astobj2_container_private.h +++ b/main/astobj2_container_private.h @@ -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); diff --git a/main/astobj2_hash.c b/main/astobj2_hash.c index f56aa72c75bb32b3104540c3186079cb55c31a12..37d4b60178ea4efa0e3ff304fc170a30ecf55d97 100644 --- a/main/astobj2_hash.c +++ b/main/astobj2_hash.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; } diff --git a/main/astobj2_private.h b/main/astobj2_private.h index 79cb06cf6ea2407d23298c9ba330f473453de664..0583faf7fa19ba1faf21220589c79a024241604e 100644 --- a/main/astobj2_private.h +++ b/main/astobj2_private.h @@ -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); diff --git a/main/astobj2_rbtree.c b/main/astobj2_rbtree.c index 3b69ce347b4dc931070df454afcf5ab749c8ec0e..2e3a73eaae9dfe4f62495dcf8204f636fa58ecaf 100644 --- a/main/astobj2_rbtree.c +++ b/main/astobj2_rbtree.c @@ -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; } diff --git a/tests/test_astobj2.c b/tests/test_astobj2.c index 67e8e45bddb1976a98045c98fb2f93006494252f..13ce7f994172d5c490dd84325b326b5815e213df 100644 --- a/tests/test_astobj2.c +++ b/tests/test_astobj2.c @@ -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; }