astobj2: Create function to copy weak proxied objects from container.
Create ao2_container_dup_weakproxy_objs to perform a similar function to ao2_container_dup. This function expects the source container to have weakproxy objects, inserts the associated non-weak objects into the destination container. Orphaned weakproxy objects are ignored. Create test for this new function and for ao2_weakproxy_find. Change-Id: I898387f058057e08696fe9070f8cd94ef3a27482
This commit is contained in:
parent
b7af9c8b19
commit
bc7f4f4db3
|
@ -1419,6 +1419,28 @@ int ao2_container_count(struct ao2_container *c);
|
|||
*/
|
||||
int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags);
|
||||
|
||||
/*!
|
||||
* \brief Copy object references associated with src container weakproxies into the dest container.
|
||||
*
|
||||
* \param dest Container to copy src strong object references into.
|
||||
* \param src Container to copy all weak object references from.
|
||||
* \param flags OBJ_NOLOCK if a lock is already held on both containers.
|
||||
* Otherwise, the src container is locked first.
|
||||
*
|
||||
* \pre The dest container must be empty. If the duplication fails, the
|
||||
* dest container will be returned empty.
|
||||
*
|
||||
* \note This can potentially be expensive because a malloc is
|
||||
* needed for every object in the src container.
|
||||
*
|
||||
* \note Every object inside the container is locked by \ref ao2_weakproxy_get_object.
|
||||
* Any weakproxy in \ref src with no associated object is ignored.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
int ao2_container_dup_weakproxy_objs(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags);
|
||||
|
||||
/*!
|
||||
* \brief Create a clone/copy of the given container.
|
||||
* \since 11.0
|
||||
|
|
|
@ -697,6 +697,59 @@ int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enu
|
|||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Copy obj associated with a weakproxy into the arg container.
|
||||
*
|
||||
* \param proxy pointer to the weakproxy.
|
||||
* \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_weakproxy_cb(void *proxy, void *arg, int flags)
|
||||
{
|
||||
void *obj = ao2_weakproxy_get_object(proxy, 0);
|
||||
struct ao2_container *dest = arg;
|
||||
int ret;
|
||||
|
||||
if (!obj) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ao2_t_link_flags(dest, obj, OBJ_NOLOCK, NULL) ? 0 : (CMP_MATCH | CMP_STOP);
|
||||
ao2_ref(obj, -1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ao2_container_dup_weakproxy_objs(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
|
||||
{
|
||||
void *obj;
|
||||
int res = 0;
|
||||
|
||||
if (!(flags & OBJ_NOLOCK)) {
|
||||
ao2_rdlock(src);
|
||||
ao2_wrlock(dest);
|
||||
}
|
||||
obj = ao2_callback(src, OBJ_NOLOCK, dup_weakproxy_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_t_callback(dest, OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL,
|
||||
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, const char *tag, const char *file, int line, const char *func)
|
||||
{
|
||||
struct ao2_container *clone;
|
||||
|
|
|
@ -262,9 +262,166 @@ fail_cleanup:
|
|||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
struct strong_str {
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct weakproxy_str {
|
||||
AO2_WEAKPROXY();
|
||||
char value[0];
|
||||
};
|
||||
|
||||
static struct strong_str *alloc_str(struct ao2_container *weakcontainer, const char *value)
|
||||
{
|
||||
struct strong_str *strong = ao2_t_alloc(sizeof(*strong), NULL, value);
|
||||
struct weakproxy_str *weak = ao2_weakproxy_alloc(sizeof(*weak) + strlen(value) + 1, NULL);
|
||||
|
||||
if (!weak || !strong) {
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
strcpy(weak->value, value); /*SAFE*/
|
||||
strong->value = weak->value;
|
||||
|
||||
if (ao2_weakproxy_set_object(weak, strong, 0)) {
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
if (!ao2_link(weakcontainer, weak)) {
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
ao2_ref(weak, -1);
|
||||
return strong;
|
||||
|
||||
error_return:
|
||||
ao2_cleanup(weak);
|
||||
ao2_cleanup(strong);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AO2_STRING_FIELD_HASH_FN(weakproxy_str, value);
|
||||
AO2_STRING_FIELD_CMP_FN(weakproxy_str, value);
|
||||
AO2_STRING_FIELD_SORT_FN(strong_str, value);
|
||||
|
||||
#define ITERATOR_CHECK_NEXT(iter, var, expected) \
|
||||
do { \
|
||||
var = ao2_iterator_next(iter); \
|
||||
ast_test_validate_cleanup(test, var == expected, ret, cleanup); \
|
||||
ao2_cleanup(var); \
|
||||
} while (0)
|
||||
|
||||
#define WEAKFIND_CHECK(c, key, var, expected) \
|
||||
do { \
|
||||
var = ao2_weakproxy_find(c, key, OBJ_SEARCH_KEY, ""); \
|
||||
ast_test_validate_cleanup(test, var == expected, ret, cleanup); \
|
||||
ao2_cleanup(var); \
|
||||
} while (0)
|
||||
|
||||
AST_TEST_DEFINE(astobj2_weak_container)
|
||||
{
|
||||
int ret = AST_TEST_FAIL;
|
||||
|
||||
struct strong_str *strong1 = NULL;
|
||||
struct strong_str *strong2 = NULL;
|
||||
struct strong_str *strong3 = NULL;
|
||||
|
||||
struct strong_str *strong = NULL;
|
||||
|
||||
struct ao2_container *weakcontainer = NULL;
|
||||
struct ao2_container *dupcontainer = NULL;
|
||||
|
||||
struct ao2_iterator iter;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "astobj2_weak_container";
|
||||
info->category = "/main/astobj2/";
|
||||
info->summary = "Test ao2 weak containers";
|
||||
info->description = "Test ao2 weak containers.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
weakcontainer = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 7,
|
||||
weakproxy_str_hash_fn, NULL, weakproxy_str_cmp_fn);
|
||||
dupcontainer = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
|
||||
strong_str_sort_fn, NULL);
|
||||
|
||||
if (!weakcontainer || !dupcontainer) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
strong1 = alloc_str(weakcontainer, "obj1");
|
||||
strong2 = alloc_str(weakcontainer, "obj2");
|
||||
strong3 = alloc_str(weakcontainer, "obj3");
|
||||
|
||||
if (!strong1 || !strong2 || !strong3) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (ao2_container_dup_weakproxy_objs(dupcontainer, weakcontainer, 0)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
iter = ao2_iterator_init(dupcontainer, 0);
|
||||
ITERATOR_CHECK_NEXT(&iter, strong, strong1);
|
||||
ITERATOR_CHECK_NEXT(&iter, strong, strong2);
|
||||
ITERATOR_CHECK_NEXT(&iter, strong, strong3);
|
||||
ITERATOR_CHECK_NEXT(&iter, strong, NULL);
|
||||
ao2_iterator_cleanup(&iter);
|
||||
|
||||
ao2_callback(dupcontainer, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, NULL, NULL);
|
||||
|
||||
WEAKFIND_CHECK(weakcontainer, "obj1", strong, strong1);
|
||||
WEAKFIND_CHECK(weakcontainer, "obj2", strong, strong2);
|
||||
WEAKFIND_CHECK(weakcontainer, "obj3", strong, strong3);
|
||||
WEAKFIND_CHECK(weakcontainer, "unknown", strong, NULL);
|
||||
|
||||
/* This will orphan "obj2" in weakcontainer. */
|
||||
ao2_replace(strong2, NULL);
|
||||
|
||||
if (ao2_container_dup_weakproxy_objs(dupcontainer, weakcontainer, 0)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ast_test_validate_cleanup(test,
|
||||
ao2_container_count(weakcontainer) == ao2_container_count(dupcontainer) + 1,
|
||||
ret,
|
||||
cleanup);
|
||||
|
||||
iter = ao2_iterator_init(dupcontainer, 0);
|
||||
ITERATOR_CHECK_NEXT(&iter, strong, strong1);
|
||||
ITERATOR_CHECK_NEXT(&iter, strong, strong3);
|
||||
ITERATOR_CHECK_NEXT(&iter, strong, NULL);
|
||||
ao2_iterator_cleanup(&iter);
|
||||
|
||||
WEAKFIND_CHECK(weakcontainer, "obj1", strong, strong1);
|
||||
WEAKFIND_CHECK(weakcontainer, "obj2", strong, NULL);
|
||||
WEAKFIND_CHECK(weakcontainer, "obj3", strong, strong3);
|
||||
WEAKFIND_CHECK(weakcontainer, "unknown", strong, NULL);
|
||||
|
||||
ret = AST_TEST_PASS;
|
||||
|
||||
cleanup:
|
||||
ao2_cleanup(strong1);
|
||||
ao2_cleanup(strong2);
|
||||
ao2_cleanup(strong3);
|
||||
|
||||
ao2_cleanup(weakcontainer);
|
||||
ao2_cleanup(dupcontainer);
|
||||
|
||||
ao2_cleanup(strong);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(astobj2_weak1);
|
||||
AST_TEST_UNREGISTER(astobj2_weak_container);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -272,6 +429,7 @@ static int unload_module(void)
|
|||
static int load_module(void)
|
||||
{
|
||||
AST_TEST_REGISTER(astobj2_weak1);
|
||||
AST_TEST_REGISTER(astobj2_weak_container);
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue