summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/eo/efl_object.eo3
-rw-r--r--src/lib/eo/eo_base_class.c28
-rw-r--r--src/tests/eo/suite/eo_test_general.c39
3 files changed, 69 insertions, 1 deletions
diff --git a/src/lib/eo/efl_object.eo b/src/lib/eo/efl_object.eo
index 7de713e5be..f41b6ee3eb 100644
--- a/src/lib/eo/efl_object.eo
+++ b/src/lib/eo/efl_object.eo
@@ -288,6 +288,9 @@ abstract Efl.Object ()
288 callback,add @hot; [[A callback was added.]] 288 callback,add @hot; [[A callback was added.]]
289 callback,del @hot; [[A callback was deleted.]] 289 callback,del @hot; [[A callback was deleted.]]
290 del @hot; [[Object is being deleted.]] 290 del @hot; [[Object is being deleted.]]
291 destruct @hot; [[Object has been fully destroyed. It can not be used
292 beyond this point. This event should only serve to clean up any
293 dangling pointer.]]
291 } 294 }
292} 295}
293 296
diff --git a/src/lib/eo/eo_base_class.c b/src/lib/eo/eo_base_class.c
index e5d932addc..a5295fa373 100644
--- a/src/lib/eo/eo_base_class.c
+++ b/src/lib/eo/eo_base_class.c
@@ -59,6 +59,7 @@ typedef struct
59 Eina_Bool need_cleaning : 1; 59 Eina_Bool need_cleaning : 1;
60 Eina_Bool parent_sunk : 1; // If parent ref has already been settled (parent has been set, or we are in add_ref mode 60 Eina_Bool parent_sunk : 1; // If parent ref has already been settled (parent has been set, or we are in add_ref mode
61 Eina_Bool allow_parent_unref : 1; // Allows unref to zero even with a parent 61 Eina_Bool allow_parent_unref : 1; // Allows unref to zero even with a parent
62 Eina_Bool has_destroyed_event_cb : 1; // No proper count: minor optimization triggered at destruction only
62} Efl_Object_Data; 63} Efl_Object_Data;
63 64
64typedef enum 65typedef enum
@@ -146,10 +147,12 @@ _eo_generic_data_node_free(Eo_Generic_Data_Node *node)
146 case DATA_PTR: 147 case DATA_PTR:
147 break; 148 break;
148 case DATA_OBJ: 149 case DATA_OBJ:
150 // FIXME: should this use "destruct" event instead?
149 efl_event_callback_del(node->d.obj, EFL_EVENT_DEL, _key_generic_cb_del, node); 151 efl_event_callback_del(node->d.obj, EFL_EVENT_DEL, _key_generic_cb_del, node);
150 efl_unref(node->d.obj); 152 efl_unref(node->d.obj);
151 break; 153 break;
152 case DATA_OBJ_WEAK: 154 case DATA_OBJ_WEAK:
155 // FIXME: should this use "destruct" event instead?
153 efl_event_callback_del(node->d.obj, EFL_EVENT_DEL, _key_generic_cb_del, node); 156 efl_event_callback_del(node->d.obj, EFL_EVENT_DEL, _key_generic_cb_del, node);
154 break; 157 break;
155 case DATA_VAL: 158 case DATA_VAL:
@@ -1068,6 +1071,8 @@ _special_event_count_inc(Efl_Object_Data *pd, const Efl_Callback_Array_Item *it)
1068 CB_COUNT_INC(pd->event_cb_efl_event_callback_del_count); 1071 CB_COUNT_INC(pd->event_cb_efl_event_callback_del_count);
1069 else if (it->desc == EFL_EVENT_DEL) 1072 else if (it->desc == EFL_EVENT_DEL)
1070 CB_COUNT_INC(pd->event_cb_efl_event_del_count); 1073 CB_COUNT_INC(pd->event_cb_efl_event_del_count);
1074 else if (it->desc == EFL_EVENT_DESTRUCT)
1075 pd->has_destroyed_event_cb = EINA_TRUE;
1071} 1076}
1072 1077
1073static inline void 1078static inline void
@@ -1126,6 +1131,7 @@ _eo_callback_remove_all(Efl_Object_Data *pd)
1126 eina_freeq_ptr_main_add(pd->callbacks, free, 0); 1131 eina_freeq_ptr_main_add(pd->callbacks, free, 0);
1127 pd->callbacks = NULL; 1132 pd->callbacks = NULL;
1128 pd->callbacks_count = 0; 1133 pd->callbacks_count = 0;
1134 pd->has_destroyed_event_cb = EINA_FALSE;
1129#ifdef EFL_EVENT_SPECIAL_SKIP 1135#ifdef EFL_EVENT_SPECIAL_SKIP
1130 pd->event_cb_efl_event_callback_add_count = 0; 1136 pd->event_cb_efl_event_callback_add_count = 0;
1131 pd->event_cb_efl_event_callback_del_count = 0; 1137 pd->event_cb_efl_event_callback_del_count = 0;
@@ -1284,6 +1290,8 @@ _efl_object_event_callback_priority_add(Eo *obj, Efl_Object_Data *pd,
1284#ifdef EFL_EVENT_SPECIAL_SKIP 1290#ifdef EFL_EVENT_SPECIAL_SKIP
1285 _special_event_count_inc(pd, &(cb->items.item)); 1291 _special_event_count_inc(pd, &(cb->items.item));
1286#endif 1292#endif
1293 if (EINA_UNLIKELY(desc == EFL_EVENT_DESTRUCT))
1294 pd->has_destroyed_event_cb = EINA_TRUE;
1287 1295
1288 efl_event_callback_call(obj, EFL_EVENT_CALLBACK_ADD, (void *)arr); 1296 efl_event_callback_call(obj, EFL_EVENT_CALLBACK_ADD, (void *)arr);
1289 1297
@@ -1389,6 +1397,16 @@ _efl_object_event_callback_array_priority_add(Eo *obj, Efl_Object_Data *pd,
1389#ifdef EFL_EVENT_SPECIAL_SKIP 1397#ifdef EFL_EVENT_SPECIAL_SKIP
1390 for (it = cb->items.item_array; it->func; it++) 1398 for (it = cb->items.item_array; it->func; it++)
1391 _special_event_count_inc(pd, it); 1399 _special_event_count_inc(pd, it);
1400#else
1401 if (!pd->has_destroyed_event_cb)
1402 {
1403 for (it = cb->items.item_array; it->func; it++)
1404 if (it->desc == EFL_EVENT_DESTRUCT)
1405 {
1406 pd->has_destroyed_event_cb = EINA_TRUE;
1407 break;
1408 }
1409 }
1392#endif 1410#endif
1393 1411
1394 efl_event_callback_call(obj, EFL_EVENT_CALLBACK_ADD, (void *)array); 1412 efl_event_callback_call(obj, EFL_EVENT_CALLBACK_ADD, (void *)array);
@@ -2044,8 +2062,16 @@ composite_obj_back:
2044err_parent_back: 2062err_parent_back:
2045 2063
2046 _efl_pending_futures_clear(pd); 2064 _efl_pending_futures_clear(pd);
2047 _eo_generic_data_del_all(obj, pd);
2048 _wref_destruct(pd); 2065 _wref_destruct(pd);
2066
2067 // this isn't 100% correct, as the object is still "slightly" alive at this
2068 // point (so efl_destructed_is() returns false), but triggering the
2069 // "destruct" event here is the simplest, safest solution.
2070 if (EINA_UNLIKELY(pd->has_destroyed_event_cb))
2071 _event_callback_call(obj, pd, EFL_EVENT_DESTRUCT, NULL, EINA_FALSE);
2072
2073 // remove generic data after this final event, in case they are used in a cb
2074 _eo_generic_data_del_all(obj, pd);
2049 _eo_callback_remove_all(pd); 2075 _eo_callback_remove_all(pd);
2050 2076
2051 ext = pd->ext; 2077 ext = pd->ext;
diff --git a/src/tests/eo/suite/eo_test_general.c b/src/tests/eo/suite/eo_test_general.c
index 46c351fdd9..e475889e95 100644
--- a/src/tests/eo/suite/eo_test_general.c
+++ b/src/tests/eo/suite/eo_test_general.c
@@ -1719,6 +1719,44 @@ START_TEST(efl_cast_test)
1719} 1719}
1720END_TEST 1720END_TEST
1721 1721
1722static void _destruct_test_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
1723{
1724 int *var = data;
1725 *var = 1;
1726}
1727
1728static void _destruct_test_destruct_cb(void *data, const Efl_Event *ev)
1729{
1730 int *var = data;
1731 *var *= 2;
1732
1733 ck_assert_int_eq(efl_ref_count(ev->object), 0);
1734
1735 // test disabled: object isn't yet marked as destructed (we're inside the
1736 // base class destructor here).
1737 //ck_assert_int_ne(efl_destructed_is(ev->object), 0);
1738}
1739
1740START_TEST(efl_object_destruct_test)
1741{
1742 int var = 0;
1743 Eo *obj;
1744
1745 efl_object_init();
1746
1747 obj = efl_add(SIMPLE_CLASS, NULL);
1748 fail_if(efl_ref_count(obj) != 1);
1749 efl_event_callback_add(obj, EFL_EVENT_DEL, _destruct_test_del_cb, &var);
1750 efl_event_callback_add(obj, EFL_EVENT_DESTRUCT, _destruct_test_destruct_cb, &var);
1751 efl_del(obj);
1752
1753 // var should be 2 if del then destruct, 0 otherwise
1754 ck_assert_int_eq(var, 2);
1755
1756 efl_object_shutdown();
1757}
1758END_TEST
1759
1722static void 1760static void
1723_auto_unref_del_cb(void *data, const Efl_Event *ev EINA_UNUSED) 1761_auto_unref_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
1724{ 1762{
@@ -1797,5 +1835,6 @@ void eo_test_general(TCase *tc)
1797 tcase_add_test(tc, eo_rec_interface); 1835 tcase_add_test(tc, eo_rec_interface);
1798 tcase_add_test(tc, eo_domain); 1836 tcase_add_test(tc, eo_domain);
1799 tcase_add_test(tc, efl_cast_test); 1837 tcase_add_test(tc, efl_cast_test);
1838 tcase_add_test(tc, efl_object_destruct_test);
1800 tcase_add_test(tc, efl_object_auto_unref_test); 1839 tcase_add_test(tc, efl_object_auto_unref_test);
1801} 1840}