eo: ensure the generation is correctly clamped

Summary:
when a few recursive event emissions are happening, and in some deep
recursive level a subscription to the same object is happening, the
subscription would just be executed when the complete recursion is done.
that is wrong. The subscription needs to be executed when the event is
called after the subscription is added, undepended from any recursive
level. That fixes that and adds a regression test for it.

This was discovered in e, since e gives a lot of error messages about a eo object
that is already freed. It turned out this object is returned from evas, and exactly
the above happened to the EFL_EVENT_DEL subscription of that object.

Test Plan: make check

Reviewers: tasn, cedric, stefan_schmidt

Subscribers: stefan_schmidt, netstar, zmike, raster, jpeg

Differential Revision: https://phab.enlightenment.org/D4656

Signed-off-by: Cedric BAIL <cedric@osg.samsung.com>
This commit is contained in:
Marcel Hollerbach 2017-02-16 13:37:16 -08:00 committed by Cedric BAIL
parent 7760613107
commit ae80040331
2 changed files with 98 additions and 5 deletions

View File

@ -1062,20 +1062,42 @@ _eo_callbacks_clear(Efl_Object_Data *pd)
{
Eo_Callback_Description **itr;
unsigned int i = 0;
Eina_Bool remove_callbacks;
unsigned int generation_clamp;
/* If there are no deletions waiting. */
if (!pd->need_cleaning) return;
/* Abort if we are currently walking the list. */
if (pd->event_frame) return;
pd->need_cleaning = EINA_FALSE;
if (pd->event_frame)
{
/* there is still a event emission going on ... do not delete anything! */
remove_callbacks = EINA_FALSE;
/* if we are in event subscription we need to clamp the generations at the current frame otherwise we are possiblity not executing that later */
generation_clamp = pd->event_frame->generation;
}
else
{
/* no event emission running */
/* remove deleted callbacks */
remove_callbacks = EINA_TRUE;
/* clap to 0 generation */
generation_clamp = 0;
/* we dont need to clean later */
pd->need_cleaning = EINA_FALSE;
}
while (i < pd->callbacks_count)
{
itr = pd->callbacks + i;
if ((*itr)->delete_me) _eo_callback_remove(pd, itr);
if (remove_callbacks && (*itr)->delete_me)
{
_eo_callback_remove(pd, itr);
}
else
{
(*itr)->generation = 0;
if ((*itr)->generation > generation_clamp)
(*itr)->generation = generation_clamp;
i++;
}
}

View File

@ -14,8 +14,12 @@
EWAPI const Efl_Class *efl_test_event_class_get(void);
EWAPI extern const Efl_Event_Description _EFL_TEST_EVENT_EVENT_TESTER;
EWAPI extern const Efl_Event_Description _EFL_TEST_EVENT_EVENT_TESTER_SUBSCRIBE;
EWAPI extern const Efl_Event_Description _EFL_TEST_EVENT_EVENT_TESTER_CLAMP_TEST;
#define EFL_TEST_EVENT_EVENT_TESTER (&(_EFL_TEST_EVENT_EVENT_TESTER))
#define EFL_TEST_EVENT_EVENT_TESTER_SUBSCRIBE (&(_EFL_TEST_EVENT_EVENT_TESTER_SUBSCRIBE))
#define EFL_TEST_EVENT_EVENT_TESTER_CLAMP_TEST (&(_EFL_TEST_EVENT_EVENT_TESTER_CLAMP_TEST))
typedef struct {
Eina_Bool event1;
@ -131,17 +135,84 @@ START_TEST(eo_event_call_in_call)
efl_object_shutdown();
}
END_TEST
static Eina_Bool emitted = 0;
static void
_generation_clamp_step3(void *data EINA_UNUSED, const Efl_Event *e EINA_UNUSED)
{
emitted = 1;
}
static void
_generation_clamp_subscribe(void *data EINA_UNUSED, const Efl_Event *e)
{
//generation is 2
efl_event_callback_add(e->object, EFL_TEST_EVENT_EVENT_TESTER_CLAMP_TEST, _generation_clamp_step3, NULL);
}
static void
_generation_clamp_step1(void *data EINA_UNUSED, const Efl_Event *e)
{
//generation is 1
efl_event_callback_call(e->object, EFL_TEST_EVENT_EVENT_TESTER_SUBSCRIBE, NULL);
efl_event_callback_call(e->object, EFL_TEST_EVENT_EVENT_TESTER_CLAMP_TEST, NULL);
efl_event_callback_call(e->object, EFL_TEST_EVENT_EVENT_TESTER_CLAMP_TEST, NULL);
}
START_TEST(eo_event_generation_bug)
{
efl_object_init();
/*
* The idea is:
*
* #1 a event gets emitted (generation is 1)
* #2 a event gets emitted as a result of #1 (generation is 2)
* in a callback from #2 a new subscription for E is added (S) (generation of it is 2)
* in a callback of #1 event E is emitted (generation is 2)
* S now MUST get executed (Here is the bug generation of S is 2 and of emission is 2, event gets skipped)
* subscription adds a callback to a event
*/
Eo *obj;
obj = efl_add(efl_test_event_class_get(), NULL);
emitted = 0;
efl_event_callback_priority_add(obj, EFL_TEST_EVENT_EVENT_TESTER, EFL_CALLBACK_PRIORITY_BEFORE, _generation_clamp_step1, NULL);
efl_event_callback_priority_add(obj, EFL_TEST_EVENT_EVENT_TESTER_SUBSCRIBE, EFL_CALLBACK_PRIORITY_BEFORE, _generation_clamp_subscribe, NULL);
efl_event_callback_call(obj, EFL_TEST_EVENT_EVENT_TESTER, NULL);
ck_assert_int_ne(emitted, 0);
efl_object_shutdown();
}
END_TEST
void eo_test_event(TCase *tc)
{
tcase_add_test(tc, eo_event);
tcase_add_test(tc, eo_event_call_in_call);
tcase_add_test(tc, eo_event_generation_bug);
}
//class implementation
EWAPI const Efl_Event_Description _EFL_TEST_EVENT_EVENT_TESTER =
EFL_EVENT_DESCRIPTION("tester");
EWAPI const Efl_Event_Description _EFL_TEST_EVENT_EVENT_TESTER_SUBSCRIBE =
EFL_EVENT_DESCRIPTION("tester");
EWAPI const Efl_Event_Description _EFL_TEST_EVENT_EVENT_TESTER_CLAMP_TEST =
EFL_EVENT_DESCRIPTION("tester");
static const Efl_Class_Description _efl_test_event_class_desc = {
EO_VERSION,
"Efl_Test_Event",