diff --git a/src/lib/eo/eo_base_class.c b/src/lib/eo/eo_base_class.c index 2f3803aab9..c6b4775b0a 100644 --- a/src/lib/eo/eo_base_class.c +++ b/src/lib/eo/eo_base_class.c @@ -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++; } } diff --git a/src/tests/eo/suite/eo_test_event.c b/src/tests/eo/suite/eo_test_event.c index ebbeaa859c..66902db233 100644 --- a/src/tests/eo/suite/eo_test_event.c +++ b/src/tests/eo/suite/eo_test_event.c @@ -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",