astobj2: Add ao2_weakproxy_find function.
[asterisk/asterisk.git] / main / astobj2_container.c
index 987116e..c75dff9 100644 (file)
@@ -22,8 +22,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include "asterisk/_private.h"
 #include "asterisk/astobj2.h"
 #include "astobj2_private.h"
@@ -38,6 +36,46 @@ 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)) {
+               __ao2_ref(node->obj, -1, tag ?: "Remove obj from container", file, line, func);
+       }
+
+       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) {
+               /* Remove node from container */
+               ao2_t_ref(node, -1, NULL);
+       }
+
+       return 1;
+}
+
 /*!
  * \internal
  * \brief Link an object into this container.  (internal)
@@ -53,16 +91,21 @@ int ao2_container_count(struct ao2_container *c)
  * \retval 0 on errors.
  * \retval 1 on success.
  */
-static int internal_ao2_link(struct ao2_container *self, void *obj_new, int flags, const char *tag, const char *file, int line, const char *func)
+int __ao2_link(struct ao2_container *self, void *obj_new, int flags,
+       const char *tag, const char *file, int line, const char *func)
 {
        int res;
        enum ao2_lock_req orig_lock;
        struct ao2_container_node *node;
 
-       if (!is_ao2_object(obj_new) || !is_ao2_object(self)
-               || !self->v_table || !self->v_table->new_node || !self->v_table->insert) {
+       if (!__is_ao2_object(obj_new, file, line, func)
+               || !__is_ao2_object(self, file, line, func)) {
+               return 0;
+       }
+
+       if (!self->v_table || !self->v_table->new_node || !self->v_table->insert) {
                /* Sanity checks. */
-               ast_assert(0);
+               __ast_assert_failed(0, "invalid container v_table", file, line, func);
                return 0;
        }
 
@@ -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);
+                       ao2_t_ref(node, -1, NULL);
                        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) {
@@ -152,16 +160,6 @@ static int internal_ao2_link(struct ao2_container *self, void *obj_new, int flag
        return res;
 }
 
-int __ao2_link_debug(struct ao2_container *c, void *obj_new, int flags, const char *tag, const char *file, int line, const char *func)
-{
-       return internal_ao2_link(c, obj_new, flags, tag, file, line, func);
-}
-
-int __ao2_link(struct ao2_container *c, void *obj_new, int flags)
-{
-       return internal_ao2_link(c, obj_new, flags, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
-}
-
 /*!
  * \brief another convenience function is a callback that matches on address
  */
@@ -174,33 +172,17 @@ int ao2_match_by_addr(void *user_data, void *arg, int flags)
  * Unlink an object from the container
  * and destroy the associated * bucket_entry structure.
  */
-void *__ao2_unlink_debug(struct ao2_container *c, void *user_data, int flags,
+void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags,
        const char *tag, const char *file, int line, const char *func)
 {
-       if (!is_ao2_object(user_data)) {
-               /* Sanity checks. */
-               ast_assert(0);
-               return NULL;
-       }
-
-       flags &= ~OBJ_SEARCH_MASK;
-       flags |= (OBJ_UNLINK | OBJ_SEARCH_OBJECT | OBJ_NODATA);
-       __ao2_callback_debug(c, flags, ao2_match_by_addr, user_data, tag, file, line, func);
-
-       return NULL;
-}
-
-void *__ao2_unlink(struct ao2_container *c, void *user_data, int flags)
-{
-       if (!is_ao2_object(user_data)) {
+       if (!__is_ao2_object(user_data, file, line, func)) {
                /* Sanity checks. */
-               ast_assert(0);
                return NULL;
        }
 
        flags &= ~OBJ_SEARCH_MASK;
        flags |= (OBJ_UNLINK | OBJ_SEARCH_OBJECT | OBJ_NODATA);
-       __ao2_callback(c, flags, ao2_match_by_addr, user_data);
+       __ao2_callback(c, flags, ao2_match_by_addr, user_data, tag, file, line, func);
 
        return NULL;
 }
@@ -259,10 +241,14 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags
        struct ao2_container *multi_container = NULL;
        struct ao2_iterator *multi_iterator = NULL;
 
-       if (!is_ao2_object(self) || !self->v_table || !self->v_table->traverse_first
-               || !self->v_table->traverse_next) {
+       if (!__is_ao2_object(self, file, line, func)) {
+               return NULL;
+       }
+
+       if (!self->v_table
+               || !self->v_table->traverse_first || !self->v_table->traverse_next) {
                /* Sanity checks. */
-               ast_assert(0);
+               __ast_assert_failed(0, "invalid container v_table", file, line, func);
                return NULL;
        }
 
@@ -367,12 +353,7 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags
                                         * Link the object into the container that will hold the
                                         * results.
                                         */
-                                       if (tag) {
-                                               __ao2_link_debug(multi_container, node->obj, flags,
-                                                       tag, file, line, func);
-                                       } else {
-                                               __ao2_link(multi_container, node->obj, flags);
-                                       }
+                                       __ao2_link(multi_container, node->obj, flags, tag, file, line, func);
                                } else {
                                        ret = node->obj;
                                        /* Returning a single object. */
@@ -381,58 +362,17 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags
                                                 * Bump the ref count since we are not going to unlink and
                                                 * transfer the container's object ref to the returned object.
                                                 */
-                                               if (tag) {
-                                                       __ao2_ref_debug(ret, 1, tag, file, line, func);
-                                               } else {
-                                                       ao2_t_ref(ret, 1, "Traversal found object");
-                                               }
+                                               __ao2_ref(ret, 1, tag ?: "Traversal found object", file, line, func);
                                        }
                                }
                        }
 
                        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);
                        }
                }
 
@@ -446,7 +386,7 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags
        }
        if (node) {
                /* Unref the node from self->v_table->traverse_first/traverse_next() */
-               __ao2_ref(node, -1);
+               ao2_t_ref(node, -1, NULL);
        }
 
        if (flags & OBJ_NOLOCK) {
@@ -467,36 +407,24 @@ static void *internal_ao2_traverse(struct ao2_container *self, enum search_flags
        }
 }
 
-void *__ao2_callback_debug(struct ao2_container *c, enum search_flags flags,
+void *__ao2_callback(struct ao2_container *c, enum search_flags flags,
        ao2_callback_fn *cb_fn, void *arg, const char *tag, const char *file, int line,
        const char *func)
 {
        return internal_ao2_traverse(c, flags, cb_fn, arg, NULL, AO2_CALLBACK_DEFAULT, tag, file, line, func);
 }
 
-void *__ao2_callback(struct ao2_container *c, enum search_flags flags,
-       ao2_callback_fn *cb_fn, void *arg)
-{
-       return internal_ao2_traverse(c, flags, cb_fn, arg, NULL, AO2_CALLBACK_DEFAULT, NULL, NULL, 0, NULL);
-}
-
-void *__ao2_callback_data_debug(struct ao2_container *c, enum search_flags flags,
+void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
        ao2_callback_data_fn *cb_fn, void *arg, void *data, const char *tag, const char *file,
        int line, const char *func)
 {
        return internal_ao2_traverse(c, flags, cb_fn, arg, data, AO2_CALLBACK_WITH_DATA, tag, file, line, func);
 }
 
-void *__ao2_callback_data(struct ao2_container *c, enum search_flags flags,
-       ao2_callback_data_fn *cb_fn, void *arg, void *data)
-{
-       return internal_ao2_traverse(c, flags, cb_fn, arg, data, AO2_CALLBACK_WITH_DATA, NULL, NULL, 0, NULL);
-}
-
 /*!
  * the find function just invokes the default callback with some reasonable flags.
  */
-void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_flags flags,
+void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags,
        const char *tag, const char *file, int line, const char *func)
 {
        void *arged = (void *) arg;/* Done to avoid compiler const warning */
@@ -506,19 +434,49 @@ void *__ao2_find_debug(struct ao2_container *c, const void *arg, enum search_fla
                ast_assert(0);
                return NULL;
        }
-       return __ao2_callback_debug(c, flags, c->cmp_fn, arged, tag, file, line, func);
+       return __ao2_callback(c, flags, c->cmp_fn, arged, tag, file, line, func);
 }
 
-void *__ao2_find(struct ao2_container *c, const void *arg, enum search_flags flags)
+void *__ao2_weakproxy_find(struct ao2_container *c, const void *arg, enum search_flags flags,
+       const char *tag, const char *file, int line, const char *func)
 {
-       void *arged = (void *) arg;/* Done to avoid compiler const warning */
+       void *proxy;
+       void *obj = NULL;
+       enum ao2_lock_req orig_lock;
 
-       if (!c) {
-               /* Sanity checks. */
-               ast_assert(0);
-               return NULL;
+       ast_assert(!!c);
+       ast_assert(flags & OBJ_SEARCH_MASK);
+       ast_assert(!(flags & ~(OBJ_SEARCH_MASK | OBJ_NOLOCK)));
+
+       if (flags & OBJ_NOLOCK) {
+               orig_lock = __adjust_lock(c, AO2_LOCK_REQ_RDLOCK, 1);
+       } else {
+               orig_lock = AO2_LOCK_REQ_RDLOCK;
+               ao2_rdlock(c);
+       }
+
+       while ((proxy = ao2_find(c, arg, flags | OBJ_NOLOCK))) {
+               obj = __ao2_weakproxy_get_object(proxy, 0, tag ?: __PRETTY_FUNCTION__, file, line, func);
+
+               if (obj) {
+                       ao2_ref(proxy, -1);
+                       break;
+               }
+
+               /* Upgrade to a write lock */
+               __adjust_lock(c, AO2_LOCK_REQ_WRLOCK, 1);
+               ao2_unlink_flags(c, proxy, OBJ_NOLOCK);
+               ao2_ref(proxy, -1);
+       }
+
+       if (flags & OBJ_NOLOCK) {
+               /* We'll keep any upgraded lock */
+               __adjust_lock(c, orig_lock, 1);
+       } else {
+               ao2_unlock(c);
        }
-       return __ao2_callback(c, flags, c->cmp_fn, arged);
+
+       return obj;
 }
 
 /*!
@@ -538,6 +496,11 @@ struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
 
 void ao2_iterator_restart(struct ao2_iterator *iter)
 {
+       if (!is_ao2_object(iter->c)) {
+               /* Sanity check. */
+               return;
+       }
+
        /* Release the last container node reference if we have one. */
        if (iter->last_node) {
                enum ao2_lock_req orig_lock;
@@ -554,7 +517,7 @@ void ao2_iterator_restart(struct ao2_iterator *iter)
                        ao2_rdlock(iter->c);
                }
 
-               __ao2_ref(iter->last_node, -1);
+               ao2_t_ref(iter->last_node, -1, NULL);
                iter->last_node = NULL;
 
                if (iter->flags & AO2_ITERATOR_DONTLOCK) {
@@ -590,18 +553,20 @@ void ao2_iterator_cleanup(struct ao2_iterator *iter)
        }
 }
 
-/*
- * move to the next element in the container.
- */
-static void *internal_ao2_iterator_next(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func)
+void *__ao2_iterator_next(struct ao2_iterator *iter,
+       const char *tag, const char *file, int line, const char *func)
 {
        enum ao2_lock_req orig_lock;
        struct ao2_container_node *node;
        void *ret;
 
-       if (!is_ao2_object(iter->c) || !iter->c->v_table || !iter->c->v_table->iterator_next) {
+       if (!__is_ao2_object(iter->c, file, line, func)) {
+               return NULL;
+       }
+
+       if (!iter->c->v_table || !iter->c->v_table->iterator_next) {
                /* Sanity checks. */
-               ast_assert(0);
+               __ast_assert_failed(0, "invalid iterator container v_table", file, line, func);
                return NULL;
        }
 
@@ -630,39 +595,16 @@ 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 {
                        /* Bump ref of returned object */
-                       if (tag) {
-                               __ao2_ref_debug(ret, +1, tag, file, line, func);
-                       } else {
-                               ao2_t_ref(ret, +1, "Next iterator object.");
-                       }
+                       __ao2_ref(ret, +1, tag ?: "Next iterator object.", file, line, func);
 
                        /* Bump the container's node ref for the iterator. */
-                       __ao2_ref(node, +1);
+                       ao2_t_ref(node, +1, NULL);
                }
        } else {
                /* The iteration has completed. */
@@ -672,7 +614,7 @@ static void *internal_ao2_iterator_next(struct ao2_iterator *iter, const char *t
 
        /* Replace the iterator's node */
        if (iter->last_node) {
-               __ao2_ref(iter->last_node, -1);
+               ao2_t_ref(iter->last_node, -1, NULL);
        }
        iter->last_node = node;
 
@@ -685,16 +627,6 @@ static void *internal_ao2_iterator_next(struct ao2_iterator *iter, const char *t
        return ret;
 }
 
-void *__ao2_iterator_next_debug(struct ao2_iterator *iter, const char *tag, const char *file, int line, const char *func)
-{
-       return internal_ao2_iterator_next(iter, tag, file, line, func);
-}
-
-void *__ao2_iterator_next(struct ao2_iterator *iter)
-{
-       return internal_ao2_iterator_next(iter, NULL, __FILE__, __LINE__, __PRETTY_FUNCTION__);
-}
-
 int ao2_iterator_count(struct ao2_iterator *iter)
 {
        return ao2_container_count(iter->c);
@@ -706,33 +638,15 @@ void container_destruct(void *_c)
 
        /* Unlink any stored objects in the container. */
        c->destroying = 1;
-       __ao2_callback(c, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
+       ao2_t_callback(c, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL,
+               "container_destruct called");
 
        /* Perform any extra container cleanup. */
        if (c->v_table && c->v_table->destroy) {
                c->v_table->destroy(c);
        }
 
-#ifdef AO2_DEBUG
-       ast_atomic_fetchadd_int(&ao2.total_containers, -1);
-#endif
-}
-
-void container_destruct_debug(void *_c)
-{
-       struct ao2_container *c = _c;
-
-       /* Unlink any stored objects in the container. */
-       c->destroying = 1;
-       __ao2_callback_debug(c, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL,
-               "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__);
-
-       /* Perform any extra container cleanup. */
-       if (c->v_table && c->v_table->destroy) {
-               c->v_table->destroy(c);
-       }
-
-#ifdef AO2_DEBUG
+#if defined(AO2_DEBUG)
        ast_atomic_fetchadd_int(&ao2.total_containers, -1);
 #endif
 }
@@ -753,7 +667,7 @@ static int dup_obj_cb(void *obj, void *arg, int flags)
 {
        struct ao2_container *dest = arg;
 
-       return __ao2_link(dest, obj, OBJ_NOLOCK) ? 0 : (CMP_MATCH | CMP_STOP);
+       return ao2_t_link_flags(dest, obj, OBJ_NOLOCK, NULL) ? 0 : (CMP_MATCH | CMP_STOP);
 }
 
 int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
@@ -765,14 +679,14 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu
                ao2_rdlock(src);
                ao2_wrlock(dest);
        }
-       obj = __ao2_callback(src, OBJ_NOLOCK, dup_obj_cb, dest);
+       obj = ao2_callback(src, OBJ_NOLOCK, dup_obj_cb, dest);
        if (obj) {
                /* Failed to put this obj into the dest container. */
                ao2_t_ref(obj, -1, "Failed to put this object into the dest container.");
 
                /* Remove all items from the dest container. */
-               __ao2_callback(dest, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL,
-                       NULL);
+               ao2_t_callback(dest, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL,
+                       NULL, NULL);
                res = -1;
        }
        if (!(flags & OBJ_NOLOCK)) {
@@ -783,49 +697,23 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu
        return res;
 }
 
-struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags)
+struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *func)
 {
        struct ao2_container *clone;
        int failed;
 
        /* Create the clone container with the same properties as the original. */
-       if (!is_ao2_object(orig) || !orig->v_table || !orig->v_table->alloc_empty_clone) {
-               /* Sanity checks. */
-               ast_assert(0);
-               return NULL;
-       }
-       clone = orig->v_table->alloc_empty_clone(orig);
-       if (!clone) {
+       if (!__is_ao2_object(orig, file, line, func)) {
                return NULL;
        }
 
-       if (flags & OBJ_NOLOCK) {
-               ao2_wrlock(clone);
-       }
-       failed = ao2_container_dup(clone, orig, flags);
-       if (flags & OBJ_NOLOCK) {
-               ao2_unlock(clone);
-       }
-       if (failed) {
-               /* Object copy into the clone container failed. */
-               ao2_t_ref(clone, -1, "Clone creation failed.");
-               clone = NULL;
-       }
-       return clone;
-}
-
-struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, const char *file, int line, const char *func, int ref_debug)
-{
-       struct ao2_container *clone;
-       int failed;
-
-       /* Create the clone container with the same properties as the original. */
-       if (!is_ao2_object(orig) || !orig->v_table || !orig->v_table->alloc_empty_clone_debug) {
+       if (!orig->v_table || !orig->v_table->alloc_empty_clone) {
                /* Sanity checks. */
-               ast_assert(0);
+               __ast_assert_failed(0, "invalid container v_table", file, line, func);
                return NULL;
        }
-       clone = orig->v_table->alloc_empty_clone_debug(orig, tag, file, line, func, ref_debug);
+
+       clone = orig->v_table->alloc_empty_clone(orig, tag, file, line, func);
        if (!clone) {
                return NULL;
        }
@@ -839,11 +727,7 @@ struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, en
        }
        if (failed) {
                /* Object copy into the clone container failed. */
-               if (ref_debug) {
-                       __ao2_ref_debug(clone, -1, tag, file, line, func);
-               } else {
-                       ao2_t_ref(clone, -1, "Clone creation failed.");
-               }
+               __ao2_ref(clone, -1, tag ?: "Clone creation failed", file, line, func);
                clone = NULL;
        }
        return clone;
@@ -863,11 +747,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 +775,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 +791,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 +806,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 +819,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 +848,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 +886,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 +897,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 +922,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 +942,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 +970,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 +1004,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 +1040,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 +1075,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 +1111,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 +1129,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.");
@@ -1258,8 +1142,8 @@ int container_init(void)
        }
 
        ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
-       ast_register_atexit(container_cleanup);
-#endif /* defined(AST_DEVMODE) */
+       ast_register_cleanup(container_cleanup);
+#endif /* defined(AO2_DEBUG) */
 
        return 0;
 }