eo: add events to track the ownership status of an Eo object

Some user code may want to track an object ownership in regard to whether it is
kept by just one owner or shared between many owners.

This is specially true for code provided by bindings to other programming
languages, where different kinds of resource management may take place.

The event `ownership,unique` is triggered whenever the object refcount goes
from two to one, as a signal that it has just one owner from now on.

The event `ownership,shared` is triggered whenever the object refcount goes
from one to two, as a signal that it has multiple owners from now on.
It will not trigger when further increasing the refcount to any value beyond
two.

We also add benchmarks for sharing (i.e. increasing the refcount) and them
unsharing objects, in order to evaluate the performance impact of this patch.

Reviewed-by: Cedric BAIL <cedric.bail@free.fr>
Differential Revision: https://phab.enlightenment.org/D8678
This commit is contained in:
Vitor Sousa 2019-05-23 18:41:57 +00:00 committed by Cedric BAIL
parent 343698f7ec
commit a86a0931f1
6 changed files with 291 additions and 0 deletions

View File

@ -38,10 +38,44 @@ bench_efl_add_jump_by_2(int request)
free(objs);
}
static void
bench_efl_add_shared_ownership(int request)
{
int i;
Eo **objs = calloc(request, sizeof(Eo *));
Eo *p = efl_add_ref(SIMPLE_CLASS, NULL);
for (i = 0; i < request; i++)
objs[i] = efl_add_ref(SIMPLE_CLASS, p);
efl_unref(p);
for (i = 0; i < request; i++)
efl_unref(objs[i]);
free(objs);
}
static void
bench_efl_add_shared_ownership_alternative(int request)
{
int i;
Eo **objs = calloc(request, sizeof(Eo *));
Eo *p = efl_add_ref(SIMPLE_CLASS, NULL);
for (i = 0; i < request; i++)
objs[i] = efl_add(SIMPLE_CLASS, p);
for (i = 0; i < request; i++)
efl_ref(objs[i]);
for (i = 0; i < request; i++)
efl_unref(objs[i]);
efl_unref(p);
free(objs);
}
void eo_bench_efl_add(Eina_Benchmark *bench)
{
eina_benchmark_register(bench, "efl_add_linear",
EINA_BENCHMARK(bench_efl_add_linear), _EO_BENCH_TIMES(1000, 10, 50000));
eina_benchmark_register(bench, "efl_add_jump_by_2",
EINA_BENCHMARK(bench_efl_add_jump_by_2), _EO_BENCH_TIMES(1000, 10, 50000));
eina_benchmark_register(bench, "efl_add_shared_ownership",
EINA_BENCHMARK(bench_efl_add_shared_ownership), _EO_BENCH_TIMES(1000, 10, 50000));
eina_benchmark_register(bench, "efl_add_shared_ownership_alternative",
EINA_BENCHMARK(bench_efl_add_shared_ownership_alternative), _EO_BENCH_TIMES(1000, 10, 50000));
}

View File

@ -411,6 +411,11 @@ abstract Efl.Object
del @hot: void; [[Object is being deleted. See @.destructor.]]
invalidate @hot: void; [[Object is being invalidated and losing its parent. See @.invalidate.]]
noref @hot: void; [[Object has lost its last reference, only parent relationship is keeping it alive. Advanced usage.]]
ownership,unique @hot: void; [[Object has lost a reference and only one is left. It has just one owner now.
Triggered whenever the refcount goes from two to one.]]
ownership,shared @hot: void; [[Object has acquired a second reference. It has multiple owners now.
Triggered whenever increasing the refcount from one to two,
it will not trigger by further increasing the refcount beyond two.]]
destruct @hot: void; [[Object has been fully destroyed. It can not be used
beyond this point. This event should only serve to clean up any
reference you keep to the object.]]

View File

@ -1933,6 +1933,9 @@ efl_ref(const Eo *obj_id)
++(obj->user_refcount);
if (EINA_UNLIKELY(obj->user_refcount == 1))
_efl_ref(obj);
else if (EINA_UNLIKELY(obj->ownership_track && obj->user_refcount == 2))
efl_event_callback_call((Eo *) obj_id, EFL_EVENT_OWNERSHIP_SHARED, NULL);
#ifdef EO_DEBUG
_eo_log_obj_ref_op(obj, EO_REF_OP_REF);
#endif
@ -1991,6 +1994,10 @@ efl_unref(const Eo *obj_id)
}
_efl_unref(obj);
}
else if (EINA_UNLIKELY(obj->ownership_track && obj->user_refcount == 1))
{
efl_event_callback_call((Eo *) obj_id, EFL_EVENT_OWNERSHIP_UNIQUE, NULL);
}
_apply_auto_unref(obj, obj_id);

View File

@ -1179,6 +1179,12 @@ _special_event_count_inc(Eo *obj_id, Efl_Object_Data *pd, const Efl_Callback_Arr
}
else if (it->desc == EFL_EVENT_DESTRUCT)
pd->has_destroyed_event_cb = EINA_TRUE;
else if (it->desc == EFL_EVENT_OWNERSHIP_SHARED || it->desc == EFL_EVENT_OWNERSHIP_UNIQUE)
{
EO_OBJ_POINTER_RETURN(obj_id, obj);
obj->ownership_track = EINA_TRUE;
EO_OBJ_DONE(obj_id);
}
}
static inline void

View File

@ -126,6 +126,7 @@ struct _Eo_Object
Eina_Bool destructed:1;
Eina_Bool manual_free:1;
unsigned char auto_unref : 1; // unref after 1 call - hack for parts
Eina_Bool ownership_track:1;
};
/* How we search and store the implementations in classes. */

View File

@ -162,6 +162,238 @@ EFL_START_TEST(eo_test_unref_noref)
}
EFL_END_TEST
typedef struct {
int shared, unique, invalidate;
} OwnershipEventsCounter;
static void
_ownership_shared_event(void *data, const Efl_Event *ev EINA_UNUSED)
{
OwnershipEventsCounter *counter = data;
++(counter->shared);
}
static void
_ownership_unique_event(void *data, const Efl_Event *ev EINA_UNUSED)
{
OwnershipEventsCounter *counter = data;
++(counter->unique);
}
static void
_invalidate_ownership_event(void *data, const Efl_Event *ev EINA_UNUSED)
{
OwnershipEventsCounter *counter = data;
++(counter->invalidate);
}
EFL_START_TEST(eo_test_ownership_events)
{
OwnershipEventsCounter counter = {0,};
Eo *obj = efl_add_ref(SIMPLE_CLASS, NULL);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_SHARED, _ownership_shared_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_UNIQUE, _ownership_unique_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_INVALIDATE, _invalidate_ownership_event, &counter);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 0);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 1);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 2);
ck_assert_int_eq(counter.invalidate, 0);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 2);
ck_assert_int_eq(counter.invalidate, 1);
}
EFL_END_TEST
EFL_START_TEST(eo_test_ownership_events_with_parent)
{
OwnershipEventsCounter counter = {0,};
Eo *par = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj = efl_add(SIMPLE_CLASS, par);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_SHARED, _ownership_shared_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_UNIQUE, _ownership_unique_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_INVALIDATE, _invalidate_ownership_event, &counter);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 0);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 1);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 2);
ck_assert_int_eq(counter.invalidate, 0);
efl_del(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 2);
ck_assert_int_eq(counter.invalidate, 1);
efl_unref(par);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 2);
}
EFL_END_TEST
EFL_START_TEST(eo_test_ownership_events_with_parent_invalidate)
{
OwnershipEventsCounter counter = {0,};
Eo *par = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj = efl_add(SIMPLE_CLASS, par);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_SHARED, _ownership_shared_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_UNIQUE, _ownership_unique_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_INVALIDATE, _invalidate_ownership_event, &counter);
/* Kill parent */
efl_unref(par);
ck_assert_int_eq(counter.shared, 0);
ck_assert_int_eq(counter.unique, 0);
ck_assert_int_eq(counter.invalidate, 1);
}
EFL_END_TEST
EFL_START_TEST(eo_test_ownership_events_with_parent_invalidate2)
{
OwnershipEventsCounter counter = {0,};
Eo *par = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj = efl_add(SIMPLE_CLASS, par);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_SHARED, _ownership_shared_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_UNIQUE, _ownership_unique_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_INVALIDATE, _invalidate_ownership_event, &counter);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 0);
ck_assert_int_eq(counter.invalidate, 0);
/* Kill parent */
efl_unref(par);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 1);
ck_assert_int_eq(counter.invalidate, 1);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 1);
ck_assert_int_eq(counter.invalidate, 1);
}
EFL_END_TEST
EFL_START_TEST(eo_test_ownership_events_with_parent_invalidate3)
{
OwnershipEventsCounter counter = {0,};
Eo *par = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj = efl_add(SIMPLE_CLASS, par);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_SHARED, _ownership_shared_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_UNIQUE, _ownership_unique_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_INVALIDATE, _invalidate_ownership_event, &counter);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 0);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 1);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
ck_assert_int_eq(counter.invalidate, 0);
/* Kill parent */
efl_unref(par);
ck_assert_int_eq(counter.shared, 2);
ck_assert_int_eq(counter.unique, 1);
ck_assert_int_eq(counter.invalidate, 1);
}
EFL_END_TEST
EFL_START_TEST(eo_test_ownership_events_self_invalidate)
{
OwnershipEventsCounter counter = {0,};
Eo *par = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj = efl_add(SIMPLE_CLASS, par);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_SHARED, _ownership_shared_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_OWNERSHIP_UNIQUE, _ownership_unique_event, &counter);
efl_event_callback_add(obj, EFL_EVENT_INVALIDATE, _invalidate_ownership_event, &counter);
ck_assert_int_eq(counter.shared, 0);
ck_assert_int_eq(counter.unique, 0);
ck_assert_int_eq(counter.invalidate, 0);
efl_ref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 0);
ck_assert_int_eq(counter.invalidate, 0);
efl_del(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 1);
ck_assert_int_eq(counter.invalidate, 1);
/* Kill parent */
efl_unref(par);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 1);
ck_assert_int_eq(counter.invalidate, 1);
efl_unref(obj);
ck_assert_int_eq(counter.shared, 1);
ck_assert_int_eq(counter.unique, 1);
ck_assert_int_eq(counter.invalidate, 1);
}
EFL_END_TEST
typedef struct {
Eo *par;
Eina_Bool called;
@ -216,6 +448,12 @@ void eo_test_lifecycle(TCase *tc)
tcase_add_test(tc, eo_test_shutdown_eventting);
tcase_add_test(tc, eo_test_del_in_noref);
tcase_add_test(tc, eo_test_unref_noref);
tcase_add_test(tc, eo_test_ownership_events);
tcase_add_test(tc, eo_test_ownership_events_with_parent);
tcase_add_test(tc, eo_test_ownership_events_with_parent_invalidate);
tcase_add_test(tc, eo_test_ownership_events_with_parent_invalidate2);
tcase_add_test(tc, eo_test_ownership_events_with_parent_invalidate3);
tcase_add_test(tc, eo_test_ownership_events_self_invalidate);
tcase_add_test(tc, eo_test_invalidating_get);
tcase_add_test(tc, eo_test_alive_get);
}