Add ability to clone ao2 containers.
[asterisk/asterisk.git] / main / astobj2.c
index 38f3d1b..a93a342 100644 (file)
@@ -27,6 +27,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/cli.h"
 #define REF_FILE "/tmp/refs"
 
+#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) */
+
 /*!
  * astobj2 objects are always preceded by this data structure,
  * which contains a lock, a reference counter,
@@ -1007,6 +1012,109 @@ static void container_destruct_debug(void *_c)
 #endif
 }
 
+/*!
+ * \internal
+ * \brief Put obj into the arg container.
+ * \since 11.0
+ *
+ * \param obj  pointer to the (user-defined part) of an object.
+ * \param arg callback argument from ao2_callback()
+ * \param flags flags from ao2_callback()
+ *
+ * \retval 0 on success.
+ * \retval CMP_STOP|CMP_MATCH on error.
+ */
+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);
+}
+
+int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
+{
+       void *obj;
+       int res = 0;
+
+       if (!(flags & OBJ_NOLOCK)) {
+               ao2_lock(src);
+               ao2_lock(dest);
+       }
+       obj = __ao2_callback(src, OBJ_NOLOCK, dup_obj_cb, dest);
+       if (obj) {
+               /* Failed to put this obj into the dest container. */
+               __ao2_ref(obj, -1);
+
+               /* Remove all items from the dest container. */
+               __ao2_callback(dest, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL,
+                       NULL);
+               res = -1;
+       }
+       if (!(flags & OBJ_NOLOCK)) {
+               ao2_unlock(dest);
+               ao2_unlock(src);
+       }
+
+       return res;
+}
+
+struct ao2_container *__ao2_container_clone(struct ao2_container *orig, enum search_flags flags)
+{
+       struct ao2_container *clone;
+       int failed;
+
+       /* Create the clone container with the same properties as the original. */
+       clone = __ao2_container_alloc(orig->n_buckets, orig->hash_fn, orig->cmp_fn);
+       if (!clone) {
+               return NULL;
+       }
+
+       if (flags & OBJ_NOLOCK) {
+               ao2_lock(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_ref(clone, -1);
+               clone = NULL;
+       }
+       return clone;
+}
+
+struct ao2_container *__ao2_container_clone_debug(struct ao2_container *orig, enum search_flags flags, const char *tag, char *file, int line, const char *funcname, int ref_debug)
+{
+       struct ao2_container *clone;
+       int failed;
+
+       /* Create the clone container with the same properties as the original. */
+       clone = __ao2_container_alloc_debug(orig->n_buckets, orig->hash_fn, orig->cmp_fn, tag,
+               file, line, funcname, ref_debug);
+       if (!clone) {
+               return NULL;
+       }
+
+       if (flags & OBJ_NOLOCK) {
+               ao2_lock(clone);
+       }
+       failed = ao2_container_dup(clone, orig, flags);
+       if (flags & OBJ_NOLOCK) {
+               ao2_unlock(clone);
+       }
+       if (failed) {
+               /* Object copy into the clone container failed. */
+               if (ref_debug) {
+                       __ao2_ref_debug(clone, -1, tag, file, line, funcname);
+               } else {
+                       __ao2_ref(clone, -1);
+               }
+               clone = NULL;
+       }
+       return clone;
+}
+
 #ifdef AO2_DEBUG
 static int print_cb(void *obj, void *arg, int flag)
 {
@@ -1045,6 +1153,7 @@ static char *handle_astobj2_stats(struct ast_cli_entry *e, int cmd, struct ast_c
 static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        struct ao2_container *c1;
+       struct ao2_container *c2;
        int i, lim;
        char *obj;
        static int prof_id = -1;
@@ -1099,8 +1208,17 @@ static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cl
                 */
                ao2_t_ref(obj, -1, "test");
        }
+
        ast_cli(a->fd, "testing callbacks\n");
        ao2_t_callback(c1, 0, print_cb, a, "test callback");
+
+       ast_cli(a->fd, "testing container cloning\n");
+       c2 = ao2_container_clone(c1, 0);
+       if (ao2_container_count(c1) != ao2_container_count(c2)) {
+               ast_cli(a->fd, "Cloned container does not have the same number of objects!\n");
+       }
+       ao2_t_callback(c2, 0, print_cb, a, "test callback");
+
        ast_cli(a->fd, "testing iterators, remove every second object\n");
        {
                struct ao2_iterator ai;
@@ -1122,6 +1240,7 @@ static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cl
                }
                ao2_iterator_destroy(&ai);
        }
+
        ast_cli(a->fd, "testing callbacks again\n");
        ao2_t_callback(c1, 0, print_cb, a, "test callback");
 
@@ -1130,6 +1249,7 @@ static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cl
 
        ast_cli(a->fd, "destroy container\n");
        ao2_t_ref(c1, -1, "");  /* destroy container */
+       ao2_t_ref(c2, -1, "");  /* destroy container */
        handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
        return CLI_SUCCESS;
 }