astobj2: Add ao2_weakproxy_find function.
[asterisk/asterisk.git] / main / astobj2_container.c
index bf53d8f..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"
@@ -49,11 +47,7 @@ int __container_unlink_node_debug(struct ao2_container_node *node, uint32_t flag
 
        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");
-               }
+               __ao2_ref(node->obj, -1, tag ?: "Remove obj from container", file, line, func);
        }
 
        node->obj = NULL;
@@ -75,7 +69,8 @@ int __container_unlink_node_debug(struct ao2_container_node *node, uint32_t flag
        }
 
        if (flags & AO2_UNLINK_NODE_UNREF_NODE) {
-               ao2_t_ref(node, -1, "Remove node from container");
+               /* Remove node from container */
+               ao2_t_ref(node, -1, NULL);
        }
 
        return 1;
@@ -96,16 +91,21 @@ int __container_unlink_node_debug(struct ao2_container_node *node, uint32_t flag
  * \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;
        }
 
@@ -146,7 +146,7 @@ static int internal_ao2_link(struct ao2_container *self, void *obj_new, int flag
                        res = 1;
                        break;
                case AO2_CONTAINER_INSERT_NODE_REJECTED:
-                       __ao2_ref(node, -1);
+                       ao2_t_ref(node, -1, NULL);
                        break;
                }
        }
@@ -160,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
  */
@@ -182,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)) {
+       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_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)) {
-               /* 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;
 }
@@ -267,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;
        }
 
@@ -375,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. */
@@ -389,11 +362,7 @@ 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);
                                        }
                                }
                        }
@@ -417,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) {
@@ -438,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 */
@@ -477,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;
 }
 
 /*!
@@ -509,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;
@@ -525,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) {
@@ -561,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;
        }
 
@@ -607,14 +601,10 @@ static void *internal_ao2_iterator_next(struct ao2_iterator *iter, const char *t
                        /* 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. */
@@ -624,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;
 
@@ -637,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);
@@ -658,26 +638,8 @@ 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);
-
-       /* Perform any extra container cleanup. */
-       if (c->v_table && c->v_table->destroy) {
-               c->v_table->destroy(c);
-       }
-
-#if defined(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__);
+       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) {
@@ -705,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)
@@ -717,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)) {
@@ -735,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;
        }
@@ -791,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;
@@ -1210,7 +1142,7 @@ int container_init(void)
        }
 
        ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
-       ast_register_atexit(container_cleanup);
+       ast_register_cleanup(container_cleanup);
 #endif /* defined(AO2_DEBUG) */
 
        return 0;