eo: Add "destruct" event

Triggered after (almost) complete destruction of the object.

Not called "deleted" because the other event is already "del".
I don't like "destruct" much but this follows the terminology of
"constructor" / "destructor".

@feature
This commit is contained in:
Jean-Philippe Andre 2018-01-10 15:23:29 +09:00
parent 63bbf0f3e5
commit f285fd5925
3 changed files with 69 additions and 1 deletions

View File

@ -288,6 +288,9 @@ abstract Efl.Object ()
callback,add @hot; [[A callback was added.]]
callback,del @hot; [[A callback was deleted.]]
del @hot; [[Object is being deleted.]]
destruct @hot; [[Object has been fully destroyed. It can not be used
beyond this point. This event should only serve to clean up any
dangling pointer.]]
}
}

View File

@ -59,6 +59,7 @@ typedef struct
Eina_Bool need_cleaning : 1;
Eina_Bool parent_sunk : 1; // If parent ref has already been settled (parent has been set, or we are in add_ref mode
Eina_Bool allow_parent_unref : 1; // Allows unref to zero even with a parent
Eina_Bool has_destroyed_event_cb : 1; // No proper count: minor optimization triggered at destruction only
} Efl_Object_Data;
typedef enum
@ -146,10 +147,12 @@ _eo_generic_data_node_free(Eo_Generic_Data_Node *node)
case DATA_PTR:
break;
case DATA_OBJ:
// FIXME: should this use "destruct" event instead?
efl_event_callback_del(node->d.obj, EFL_EVENT_DEL, _key_generic_cb_del, node);
efl_unref(node->d.obj);
break;
case DATA_OBJ_WEAK:
// FIXME: should this use "destruct" event instead?
efl_event_callback_del(node->d.obj, EFL_EVENT_DEL, _key_generic_cb_del, node);
break;
case DATA_VAL:
@ -1068,6 +1071,8 @@ _special_event_count_inc(Efl_Object_Data *pd, const Efl_Callback_Array_Item *it)
CB_COUNT_INC(pd->event_cb_efl_event_callback_del_count);
else if (it->desc == EFL_EVENT_DEL)
CB_COUNT_INC(pd->event_cb_efl_event_del_count);
else if (it->desc == EFL_EVENT_DESTRUCT)
pd->has_destroyed_event_cb = EINA_TRUE;
}
static inline void
@ -1126,6 +1131,7 @@ _eo_callback_remove_all(Efl_Object_Data *pd)
eina_freeq_ptr_main_add(pd->callbacks, free, 0);
pd->callbacks = NULL;
pd->callbacks_count = 0;
pd->has_destroyed_event_cb = EINA_FALSE;
#ifdef EFL_EVENT_SPECIAL_SKIP
pd->event_cb_efl_event_callback_add_count = 0;
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,
#ifdef EFL_EVENT_SPECIAL_SKIP
_special_event_count_inc(pd, &(cb->items.item));
#endif
if (EINA_UNLIKELY(desc == EFL_EVENT_DESTRUCT))
pd->has_destroyed_event_cb = EINA_TRUE;
efl_event_callback_call(obj, EFL_EVENT_CALLBACK_ADD, (void *)arr);
@ -1389,6 +1397,16 @@ _efl_object_event_callback_array_priority_add(Eo *obj, Efl_Object_Data *pd,
#ifdef EFL_EVENT_SPECIAL_SKIP
for (it = cb->items.item_array; it->func; it++)
_special_event_count_inc(pd, it);
#else
if (!pd->has_destroyed_event_cb)
{
for (it = cb->items.item_array; it->func; it++)
if (it->desc == EFL_EVENT_DESTRUCT)
{
pd->has_destroyed_event_cb = EINA_TRUE;
break;
}
}
#endif
efl_event_callback_call(obj, EFL_EVENT_CALLBACK_ADD, (void *)array);
@ -2044,8 +2062,16 @@ composite_obj_back:
err_parent_back:
_efl_pending_futures_clear(pd);
_eo_generic_data_del_all(obj, pd);
_wref_destruct(pd);
// this isn't 100% correct, as the object is still "slightly" alive at this
// point (so efl_destructed_is() returns false), but triggering the
// "destruct" event here is the simplest, safest solution.
if (EINA_UNLIKELY(pd->has_destroyed_event_cb))
_event_callback_call(obj, pd, EFL_EVENT_DESTRUCT, NULL, EINA_FALSE);
// remove generic data after this final event, in case they are used in a cb
_eo_generic_data_del_all(obj, pd);
_eo_callback_remove_all(pd);
ext = pd->ext;

View File

@ -1719,6 +1719,44 @@ START_TEST(efl_cast_test)
}
END_TEST
static void _destruct_test_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
int *var = data;
*var = 1;
}
static void _destruct_test_destruct_cb(void *data, const Efl_Event *ev)
{
int *var = data;
*var *= 2;
ck_assert_int_eq(efl_ref_count(ev->object), 0);
// test disabled: object isn't yet marked as destructed (we're inside the
// base class destructor here).
//ck_assert_int_ne(efl_destructed_is(ev->object), 0);
}
START_TEST(efl_object_destruct_test)
{
int var = 0;
Eo *obj;
efl_object_init();
obj = efl_add(SIMPLE_CLASS, NULL);
fail_if(efl_ref_count(obj) != 1);
efl_event_callback_add(obj, EFL_EVENT_DEL, _destruct_test_del_cb, &var);
efl_event_callback_add(obj, EFL_EVENT_DESTRUCT, _destruct_test_destruct_cb, &var);
efl_del(obj);
// var should be 2 if del then destruct, 0 otherwise
ck_assert_int_eq(var, 2);
efl_object_shutdown();
}
END_TEST
static void
_auto_unref_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
@ -1797,5 +1835,6 @@ void eo_test_general(TCase *tc)
tcase_add_test(tc, eo_rec_interface);
tcase_add_test(tc, eo_domain);
tcase_add_test(tc, efl_cast_test);
tcase_add_test(tc, efl_object_destruct_test);
tcase_add_test(tc, efl_object_auto_unref_test);
}