forked from enlightenment/efl
eio: enforce proper lifecycle for all Efl.Io_Model and fix discovered lifecycle bugs.
Summary: This make sure that the object returned by children_slice_get are properly destroyed when the refcount drop to only the parent holding a reference on it. This make it clear that the user of the api can rely on efl_ref/efl_unref to actually manage its use of the returned object. Additionnaly we are cleaning up the created object that we are using to build our own request inside the Efl.Io.Model and avoid internal leak. Depends on D7864 Reviewers: felipealmeida, segfaultxavi, SanghyeonLee, zmike, bu5hm4n Reviewed By: zmike Subscribers: #reviewers, #committers Tags: #efl Maniphest Tasks: T7528 Differential Revision: https://phab.enlightenment.org/D7865
This commit is contained in:
parent
790fc1b8c5
commit
301b05502c
|
@ -100,7 +100,8 @@ _efl_model_evt_added_ecore_cb(void *data, int type, void *event)
|
||||||
mi->name_start = eina_stringshare_strlen(pd->path) + 1;
|
mi->name_start = eina_stringshare_strlen(pd->path) + 1;
|
||||||
mi->name_length = mi->path_length - mi->name_start;
|
mi->name_length = mi->path_length - mi->name_start;
|
||||||
mi->type = EINA_FILE_UNKNOWN;
|
mi->type = EINA_FILE_UNKNOWN;
|
||||||
mi->parent_ref = EINA_TRUE;
|
mi->parent_ref = EINA_FALSE;
|
||||||
|
mi->child_ref = EINA_TRUE;
|
||||||
|
|
||||||
// Honor filter on new added file too
|
// Honor filter on new added file too
|
||||||
if (pd->filter.cb)
|
if (pd->filter.cb)
|
||||||
|
@ -115,7 +116,7 @@ _efl_model_evt_added_ecore_cb(void *data, int type, void *event)
|
||||||
|
|
||||||
if (!pd->filter.cb(pd->filter.data, obj, &info))
|
if (!pd->filter.cb(pd->filter.data, obj, &info))
|
||||||
{
|
{
|
||||||
eina_stringshare_del(mi->path);
|
eina_stringshare_replace(&mi->path, NULL);
|
||||||
free(mi);
|
free(mi);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -176,9 +177,7 @@ _efl_model_evt_deleted_ecore_cb(void *data, int type, void *event)
|
||||||
// Remove the entry from the files list
|
// Remove the entry from the files list
|
||||||
pd->files = eina_list_remove_list(pd->files, l);
|
pd->files = eina_list_remove_list(pd->files, l);
|
||||||
|
|
||||||
// This should trigger the object child destruction if it exist
|
// This will only trigger the data destruction if no object is referencing them.
|
||||||
// resulting in the potential destruction of the child, after
|
|
||||||
// this point mi and info might be freed.
|
|
||||||
_efl_io_model_info_free(mi, EINA_FALSE);
|
_efl_io_model_info_free(mi, EINA_FALSE);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
|
@ -225,11 +224,6 @@ _efl_io_model_info_free(Efl_Io_Model_Info *info, Eina_Bool model)
|
||||||
|
|
||||||
if (!model)
|
if (!model)
|
||||||
{
|
{
|
||||||
if (!info->object)
|
|
||||||
{
|
|
||||||
efl_del(info->object);
|
|
||||||
info->object = NULL;
|
|
||||||
}
|
|
||||||
info->child_ref = EINA_FALSE;
|
info->child_ref = EINA_FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -241,7 +235,7 @@ _efl_io_model_info_free(Efl_Io_Model_Info *info, Eina_Bool model)
|
||||||
info->parent_ref)
|
info->parent_ref)
|
||||||
return ;
|
return ;
|
||||||
|
|
||||||
eina_stringshare_del(info->path);
|
eina_stringshare_replace(&info->path, NULL);
|
||||||
free(info);
|
free(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,11 +330,11 @@ _eio_build_st_done_clobber(void *data, Eio_File *handler, const Eina_Stat *stat)
|
||||||
Efl_Io_Model *model = data;
|
Efl_Io_Model *model = data;
|
||||||
Efl_Io_Model *parent;
|
Efl_Io_Model *parent;
|
||||||
|
|
||||||
efl_ref(model);
|
efl_ref(model); // For st_done
|
||||||
_eio_build_st_done(data, handler, stat);
|
_eio_build_st_done(data, handler, stat);
|
||||||
parent = efl_parent_get(model);
|
parent = efl_parent_get(model);
|
||||||
efl_model_child_del(parent, model);
|
efl_model_child_del(parent, model);
|
||||||
efl_unref(model);
|
efl_unref(model); // From the async thread early ref
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -361,13 +355,10 @@ static void
|
||||||
_eio_build_st_error_clobber(void *data, Eio_File *handler, int error)
|
_eio_build_st_error_clobber(void *data, Eio_File *handler, int error)
|
||||||
{
|
{
|
||||||
Efl_Io_Model *model = data;
|
Efl_Io_Model *model = data;
|
||||||
Efl_Io_Model *parent;
|
|
||||||
|
|
||||||
efl_ref(model);
|
efl_ref(model); // For st_error unref
|
||||||
_eio_build_st_error(data, handler, error);
|
_eio_build_st_error(data, handler, error);
|
||||||
parent = efl_parent_get(model);
|
efl_unref(model); // From the async thread early ref
|
||||||
efl_model_child_del(parent, model);
|
|
||||||
efl_unref(model);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -377,7 +368,10 @@ _eio_build_st(const Efl_Io_Model *model, Efl_Io_Model_Data *pd)
|
||||||
if (pd->request.stat) return ;
|
if (pd->request.stat) return ;
|
||||||
if (pd->error) return ;
|
if (pd->error) return ;
|
||||||
|
|
||||||
pd->request.stat = eio_file_direct_stat(pd->path, _eio_build_st_done, _eio_build_st_error, efl_ref(model));
|
pd->request.stat = eio_file_direct_stat(pd->path,
|
||||||
|
_eio_build_st_done,
|
||||||
|
_eio_build_st_error,
|
||||||
|
efl_ref(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -502,7 +496,7 @@ _property_filename_cb(const Eo *obj, Efl_Io_Model_Data *pd)
|
||||||
static Eina_Value *
|
static Eina_Value *
|
||||||
_property_path_cb(const Eo *obj EINA_UNUSED, Efl_Io_Model_Data *pd)
|
_property_path_cb(const Eo *obj EINA_UNUSED, Efl_Io_Model_Data *pd)
|
||||||
{
|
{
|
||||||
return eina_value_string_new(pd->path);
|
return eina_value_stringshare_new(pd->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Eina_Value *
|
static Eina_Value *
|
||||||
|
@ -700,7 +694,7 @@ _efl_io_model_efl_model_property_set(Eo *obj,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
f = efl_loop_future_resolved(obj, eina_value_string_init(pd->path));
|
f = efl_loop_future_resolved(obj, eina_value_stringshare_init(pd->path));
|
||||||
}
|
}
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
|
@ -741,7 +735,8 @@ _efl_io_model_children_list(void *data, Eina_Array *entries)
|
||||||
mi->name_start = info->name_start;
|
mi->name_start = info->name_start;
|
||||||
mi->name_length = info->name_length;
|
mi->name_length = info->name_length;
|
||||||
mi->type = _efl_io_model_info_type_get(info, NULL);
|
mi->type = _efl_io_model_info_type_get(info, NULL);
|
||||||
mi->parent_ref = EINA_TRUE;
|
mi->parent_ref = EINA_FALSE;
|
||||||
|
mi->child_ref = EINA_TRUE;
|
||||||
|
|
||||||
cevt.index = eina_list_count(pd->files);
|
cevt.index = eina_list_count(pd->files);
|
||||||
cevt.child = NULL;
|
cevt.child = NULL;
|
||||||
|
@ -755,14 +750,10 @@ _efl_io_model_children_list(void *data, Eina_Array *entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
static Eina_Value
|
static Eina_Value
|
||||||
_efl_io_model_children_list_on(void *data, const Eina_Value v,
|
_efl_io_model_children_list_on(Eo *o EINA_UNUSED, void *data, const Eina_Value v)
|
||||||
const Eina_Future *dead EINA_UNUSED)
|
|
||||||
{
|
{
|
||||||
Efl_Io_Model_Data *pd = data;
|
Efl_Io_Model_Data *pd = data;
|
||||||
|
|
||||||
pd->request.listing = NULL;
|
|
||||||
pd->listed = EINA_TRUE;
|
|
||||||
|
|
||||||
// Now that we have listed the content of the directory,
|
// Now that we have listed the content of the directory,
|
||||||
// we can whatch over it
|
// we can whatch over it
|
||||||
_efl_io_model_efl_model_monitor_add(pd);
|
_efl_io_model_efl_model_monitor_add(pd);
|
||||||
|
@ -770,6 +761,15 @@ _efl_io_model_children_list_on(void *data, const Eina_Value v,
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_efl_io_model_children_list_cleanup(Eo *o EINA_UNUSED, void *data, const Eina_Future *dead_future EINA_UNUSED)
|
||||||
|
{
|
||||||
|
Efl_Io_Model_Data *pd = data;
|
||||||
|
|
||||||
|
pd->request.listing = NULL;
|
||||||
|
pd->listed = EINA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Children Count Get
|
* Children Count Get
|
||||||
*/
|
*/
|
||||||
|
@ -801,8 +801,11 @@ _efl_io_model_efl_model_children_count_get(const Eo *obj, Efl_Io_Model_Data *pd)
|
||||||
|
|
||||||
f = efl_io_manager_direct_ls(iom, pd->path, EINA_FALSE,
|
f = efl_io_manager_direct_ls(iom, pd->path, EINA_FALSE,
|
||||||
(void*) obj, _efl_io_model_children_list, NULL);
|
(void*) obj, _efl_io_model_children_list, NULL);
|
||||||
f = eina_future_then(f, _efl_io_model_children_list_on, pd, NULL);
|
|
||||||
pd->request.listing = efl_future_then(obj, f);
|
pd->request.listing = efl_future_then(obj, f,
|
||||||
|
.success = _efl_io_model_children_list_on,
|
||||||
|
.free = _efl_io_model_children_list_cleanup,
|
||||||
|
.data = pd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return eina_list_count(pd->files);
|
return eina_list_count(pd->files);
|
||||||
|
@ -878,8 +881,8 @@ _efl_io_model_efl_model_child_del(Eo *obj EINA_UNUSED,
|
||||||
Eina_File_Type type;
|
Eina_File_Type type;
|
||||||
|
|
||||||
child_pd = efl_data_scope_get(child, MY_CLASS);
|
child_pd = efl_data_scope_get(child, MY_CLASS);
|
||||||
if (!child_pd->info) goto on_error;
|
if (!child_pd->info) return;
|
||||||
if (child_pd->error) goto on_error;
|
if (child_pd->error) return;
|
||||||
|
|
||||||
type = child_pd->info->type;
|
type = child_pd->info->type;
|
||||||
|
|
||||||
|
@ -889,6 +892,9 @@ _efl_io_model_efl_model_child_del(Eo *obj EINA_UNUSED,
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Already in progress
|
||||||
|
if (child_pd->request.del) return;
|
||||||
|
|
||||||
efl_ref(child);
|
efl_ref(child);
|
||||||
if (type == EINA_FILE_DIR)
|
if (type == EINA_FILE_DIR)
|
||||||
{
|
{
|
||||||
|
@ -906,11 +912,6 @@ _efl_io_model_efl_model_child_del(Eo *obj EINA_UNUSED,
|
||||||
_eio_error_unlink_cb,
|
_eio_error_unlink_cb,
|
||||||
child);
|
child);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ;
|
|
||||||
|
|
||||||
on_error:
|
|
||||||
efl_del(child);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -947,20 +948,24 @@ _efl_io_model_efl_model_children_slice_get(Eo *obj, Efl_Io_Model_Data *pd,
|
||||||
Efl_Io_Model_Info *info = eina_list_data_get(ls);
|
Efl_Io_Model_Info *info = eina_list_data_get(ls);
|
||||||
Efl_Io_Model_Data *child_data = NULL;
|
Efl_Io_Model_Data *child_data = NULL;
|
||||||
|
|
||||||
info->child_ref = EINA_TRUE;
|
info->parent_ref = EINA_TRUE;
|
||||||
|
|
||||||
if (info->object == NULL)
|
if (info->object == NULL)
|
||||||
// Little trick here, setting internal data before finalize
|
// Little trick here, setting internal data before finalize
|
||||||
info->object = efl_add(EFL_IO_MODEL_CLASS, obj,
|
info->object = efl_add_ref(EFL_IO_MODEL_CLASS, obj,
|
||||||
child_data = efl_data_scope_get(efl_added, EFL_IO_MODEL_CLASS),
|
efl_loop_model_volatile_make(efl_added),
|
||||||
child_data->info = info,
|
child_data = efl_data_scope_get(efl_added, EFL_IO_MODEL_CLASS),
|
||||||
child_data->path = eina_stringshare_ref(info->path),
|
child_data->info = info,
|
||||||
child_data->parent = ls,
|
child_data->path = eina_stringshare_ref(info->path),
|
||||||
// NOTE: We are assuming here that the parent model will outlive all its children
|
child_data->parent = ls,
|
||||||
child_data->filter.cb = pd->filter.cb,
|
// NOTE: We are assuming here that the parent model will outlive all its children
|
||||||
child_data->filter.data = pd->filter.data);
|
child_data->filter.cb = pd->filter.cb,
|
||||||
|
child_data->filter.data = pd->filter.data);
|
||||||
eina_value_array_append(&array, info->object);
|
eina_value_array_append(&array, info->object);
|
||||||
|
|
||||||
|
efl_wref_add(info->object, &info->object);
|
||||||
|
efl_unref(info->object);
|
||||||
|
|
||||||
count--;
|
count--;
|
||||||
ls = eina_list_next(ls);
|
ls = eina_list_next(ls);
|
||||||
}
|
}
|
||||||
|
@ -1017,13 +1022,17 @@ _efl_io_model_efl_object_destructor(Eo *obj , Efl_Io_Model_Data *priv)
|
||||||
{
|
{
|
||||||
Efl_Io_Model_Info *info;
|
Efl_Io_Model_Info *info;
|
||||||
|
|
||||||
|
|
||||||
|
free(priv->st);
|
||||||
|
priv->st = NULL;
|
||||||
|
|
||||||
_efl_io_model_info_free(priv->info, EINA_TRUE);
|
_efl_io_model_info_free(priv->info, EINA_TRUE);
|
||||||
priv->info = NULL;
|
priv->info = NULL;
|
||||||
|
|
||||||
EINA_LIST_FREE(priv->files, info)
|
EINA_LIST_FREE(priv->files, info)
|
||||||
_efl_io_model_info_free(info, EINA_FALSE);
|
_efl_io_model_info_free(info, EINA_FALSE);
|
||||||
|
|
||||||
eina_stringshare_del(priv->path);
|
eina_stringshare_replace(&priv->path, NULL);
|
||||||
|
|
||||||
efl_destructor(efl_super(obj, MY_CLASS));
|
efl_destructor(efl_super(obj, MY_CLASS));
|
||||||
}
|
}
|
||||||
|
@ -1031,10 +1040,20 @@ _efl_io_model_efl_object_destructor(Eo *obj , Efl_Io_Model_Data *priv)
|
||||||
static void
|
static void
|
||||||
_efl_io_model_efl_object_invalidate(Eo *obj , Efl_Io_Model_Data *priv)
|
_efl_io_model_efl_object_invalidate(Eo *obj , Efl_Io_Model_Data *priv)
|
||||||
{
|
{
|
||||||
|
// This has to be done first, to make sure that the automatic
|
||||||
|
// del is not done anymore. Or it would lead to too much
|
||||||
|
// unref when stopping async thread (During invalidate, we
|
||||||
|
// are already in the process of doing an implicit del).
|
||||||
|
efl_invalidate(efl_super(obj, EFL_IO_MODEL_CLASS));
|
||||||
|
|
||||||
_efl_io_model_efl_model_monitor_del(priv);
|
_efl_io_model_efl_model_monitor_del(priv);
|
||||||
|
|
||||||
// Unlink the object from the parent
|
// Unlink the object from the parent
|
||||||
if (priv->info) priv->info->object = NULL;
|
if (priv->info)
|
||||||
|
{
|
||||||
|
efl_wref_del(priv->info->object, &priv->info->object);
|
||||||
|
priv->info->object = NULL;
|
||||||
|
}
|
||||||
if (priv->request.del)
|
if (priv->request.del)
|
||||||
{
|
{
|
||||||
if (!ecore_thread_wait(priv->request.del->thread, 0.1))
|
if (!ecore_thread_wait(priv->request.del->thread, 0.1))
|
||||||
|
@ -1059,8 +1078,6 @@ _efl_io_model_efl_object_invalidate(Eo *obj , Efl_Io_Model_Data *priv)
|
||||||
ecore_thread_wait(priv->request.stat->thread, 0.1);
|
ecore_thread_wait(priv->request.stat->thread, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
efl_invalidate(efl_super(obj, EFL_IO_MODEL_CLASS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "efl_io_model.eo.c"
|
#include "efl_io_model.eo.c"
|
||||||
|
|
|
@ -16,6 +16,52 @@ Eina_Tmpstr* temp_filename = NULL;
|
||||||
const char* tmpdir = NULL;
|
const char* tmpdir = NULL;
|
||||||
Eina_Bool children_deleted = EINA_FALSE;
|
Eina_Bool children_deleted = EINA_FALSE;
|
||||||
|
|
||||||
|
// This will ensure that the child have the time to be automatically destroyed
|
||||||
|
static Eina_Value
|
||||||
|
_delayed_quit(Eo *o EINA_UNUSED, void *data EINA_UNUSED, const Eina_Value v)
|
||||||
|
{
|
||||||
|
ecore_main_loop_quit();
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Value
|
||||||
|
_children_got(Eo *o, void *data EINA_UNUSED, const Eina_Value v)
|
||||||
|
{
|
||||||
|
Eo *child = NULL;
|
||||||
|
unsigned int i, len;
|
||||||
|
Eina_Value r = (Eina_Value) v;
|
||||||
|
|
||||||
|
EINA_VALUE_ARRAY_FOREACH(&v, len, i, child)
|
||||||
|
{
|
||||||
|
Eina_Value *path;
|
||||||
|
char *str;
|
||||||
|
|
||||||
|
path = efl_model_property_get(child, "path");
|
||||||
|
fail_if(path == NULL);
|
||||||
|
str = eina_value_to_string(path);
|
||||||
|
fail_if(str == NULL);
|
||||||
|
|
||||||
|
if (temp_filename && strcmp(str, temp_filename) == 0)
|
||||||
|
r = eina_future_as_value(efl_future_then(efl_loop_get(o),
|
||||||
|
efl_loop_job(efl_loop_get(o)),
|
||||||
|
.success = _delayed_quit));
|
||||||
|
|
||||||
|
|
||||||
|
free(str);
|
||||||
|
eina_value_free(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Value
|
||||||
|
_children_failed(Eo *o EINA_UNUSED, void *data EINA_UNUSED, const Eina_Error err)
|
||||||
|
{
|
||||||
|
ck_abort_msg( "Failed to get the child with '%s'.\n", eina_error_msg_get(err));
|
||||||
|
return eina_value_error_init(err);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_children_removed_cb(void *data EINA_UNUSED, const Efl_Event* event)
|
_children_removed_cb(void *data EINA_UNUSED, const Efl_Event* event)
|
||||||
{
|
{
|
||||||
|
@ -23,6 +69,20 @@ _children_removed_cb(void *data EINA_UNUSED, const Efl_Event* event)
|
||||||
Eina_Value *path;
|
Eina_Value *path;
|
||||||
char *str;
|
char *str;
|
||||||
|
|
||||||
|
if (!evt->child)
|
||||||
|
{
|
||||||
|
Eina_Future *f;
|
||||||
|
|
||||||
|
// Last time we can fetch the object
|
||||||
|
f = efl_future_then(event->object,
|
||||||
|
efl_model_children_slice_get(event->object, evt->index, 1),
|
||||||
|
.success = _children_got,
|
||||||
|
.error = _children_failed,
|
||||||
|
.success_type = EINA_VALUE_TYPE_ARRAY);
|
||||||
|
fail_if(f == NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fail_if(evt->child == NULL);
|
fail_if(evt->child == NULL);
|
||||||
path = efl_model_property_get(evt->child, "path");
|
path = efl_model_property_get(evt->child, "path");
|
||||||
fail_if(path == NULL);
|
fail_if(path == NULL);
|
||||||
|
@ -30,7 +90,9 @@ _children_removed_cb(void *data EINA_UNUSED, const Efl_Event* event)
|
||||||
fail_if(str == NULL);
|
fail_if(str == NULL);
|
||||||
|
|
||||||
if (temp_filename && strcmp(str, temp_filename) == 0)
|
if (temp_filename && strcmp(str, temp_filename) == 0)
|
||||||
ecore_main_loop_quit();
|
efl_future_then(efl_loop_get(evt->child),
|
||||||
|
efl_loop_job(efl_loop_get(evt->child)),
|
||||||
|
.success = _delayed_quit);
|
||||||
|
|
||||||
free(str);
|
free(str);
|
||||||
eina_value_free(path);
|
eina_value_free(path);
|
||||||
|
|
Loading…
Reference in New Issue