cedric pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=ae8004033112f0f4f94ad6d0eac2f010b5c009cd

commit ae8004033112f0f4f94ad6d0eac2f010b5c009cd
Author: Marcel Hollerbach <marcel-hollerb...@t-online.de>
Date:   Thu Feb 16 13:37:16 2017 -0800

    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 <ced...@osg.samsung.com>
---
 src/lib/eo/eo_base_class.c         | 32 ++++++++++++++---
 src/tests/eo/suite/eo_test_event.c | 71 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 98 insertions(+), 5 deletions(-)

diff --git a/src/lib/eo/eo_base_class.c b/src/lib/eo/eo_base_class.c
index 2f3803a..c6b4775 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 ebbeaa8..66902db 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",

-- 


Reply via email to