evas: Avoid infinite loop on evas shutdown

If an object fails to call efl_destructor() on all the parent
classes, then it may never be removed from the layer object
inlist and never would its data be deleted either (eo keeps
it safely alive).

Here's how to test: add a "return;" statement inside an object's
destructor (eg. the window class).

Fixes T5680
This commit is contained in:
Jean-Philippe Andre 2017-07-20 14:04:10 +09:00
parent bbfdf8909b
commit cb19c77558
1 changed files with 40 additions and 16 deletions

View File

@ -277,6 +277,7 @@ _evas_canvas_efl_object_destructor(Eo *eo_e, Evas_Public_Data *e)
Evas_Post_Render_Job *job;
Evas_Layer *lay;
Efl_Canvas_Output *evo;
unsigned int prev_zombie_count = UINT_MAX;
int i;
Eina_Bool del;
@ -295,13 +296,14 @@ _evas_canvas_efl_object_destructor(Eo *eo_e, Evas_Public_Data *e)
e->cleanup = 1;
while (del)
{
Eina_Bool detach_zombies = EINA_FALSE;
Evas_Object_Protected_Data *o;
Eina_List *unrefs = NULL;
Eo *eo_obj;
del = EINA_FALSE;
EINA_INLIST_FOREACH(e->layers, lay)
{
Eo *eo_obj;
Evas_Object_Protected_Data *o;
Eina_List *unrefs = NULL;
evas_layer_pre_free(lay);
EINA_INLIST_FOREACH(lay->objects, o)
@ -313,21 +315,43 @@ _evas_canvas_efl_object_destructor(Eo *eo_e, Evas_Public_Data *e)
ERR("obj(%p, %s) ref count(%d) is bigger than 0. This object couldn't be deleted", o, o->type, efl_ref_get(o->object));
continue;
}
else
{
unrefs = eina_list_append(unrefs, o->object);
}
unrefs = eina_list_append(unrefs, o->object);
del = EINA_TRUE;
}
}
EINA_LIST_FREE(unrefs, eo_obj)
{
ERR("Killing Zombie Object [%p] ref=%i:%i\n", eo_obj, efl_ref_get(eo_obj), ___efl_ref2_get(eo_obj));
___efl_ref2_reset(eo_obj);
while (efl_ref_get(eo_obj) > 1) efl_unref(eo_obj);
while (efl_ref_get(eo_obj) < 1) efl_ref(eo_obj);
efl_del(eo_obj);
}
}
if (eina_list_count(unrefs) >= prev_zombie_count)
detach_zombies = EINA_TRUE;
prev_zombie_count = eina_list_count(unrefs);
EINA_LIST_FREE(unrefs, eo_obj)
{
ERR("Killing Zombie Object [%s@%p]. Refs: %i:%i",
efl_class_name_get(eo_obj), eo_obj, efl_ref_get(eo_obj), ___efl_ref2_get(eo_obj));
___efl_ref2_reset(eo_obj);
while (efl_ref_get(eo_obj) > 1) efl_unref(eo_obj);
while (efl_ref_get(eo_obj) < 1) efl_ref(eo_obj);
efl_del(eo_obj);
if (!detach_zombies) continue;
EINA_INLIST_FOREACH(e->layers, lay)
EINA_INLIST_FOREACH(lay->objects, o)
if (o && (o->object == eo_obj))
{
ERR("Zombie Object [%s@%p] could not be removed "
"from the list of objects. Maybe this object "
"was deleted but the call to efl_destructor() "
"was not propagated to all the parent classes? "
"Forcibly removing it. This may leak! Refs: %i:%i",
efl_class_name_get(eo_obj), eo_obj, efl_ref_get(eo_obj), ___efl_ref2_get(eo_obj));
lay->objects = (Evas_Object_Protected_Data *)
eina_inlist_remove(EINA_INLIST_GET(lay->objects), EINA_INLIST_GET(o));
goto next_zombie;
}
next_zombie:
continue;
}
}
EINA_INLIST_FOREACH(e->layers, lay)