diff --git a/src/benchmarks/eo/eo_bench_eo_add.c b/src/benchmarks/eo/eo_bench_eo_add.c index 963cd3abb9..77f2eadb7f 100644 --- a/src/benchmarks/eo/eo_bench_eo_add.c +++ b/src/benchmarks/eo/eo_bench_eo_add.c @@ -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)); } diff --git a/src/lib/eo/efl_object.eo b/src/lib/eo/efl_object.eo index 1ced422b5c..bea9a0e0f5 100644 --- a/src/lib/eo/efl_object.eo +++ b/src/lib/eo/efl_object.eo @@ -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.]] diff --git a/src/lib/eo/eo.c b/src/lib/eo/eo.c index 0113434a8c..db96b24ef7 100644 --- a/src/lib/eo/eo.c +++ b/src/lib/eo/eo.c @@ -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); diff --git a/src/lib/eo/eo_base_class.c b/src/lib/eo/eo_base_class.c index 2dc5efcec5..62a1caf9a2 100644 --- a/src/lib/eo/eo_base_class.c +++ b/src/lib/eo/eo_base_class.c @@ -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 diff --git a/src/lib/eo/eo_private.h b/src/lib/eo/eo_private.h index 3b046bb302..c64dee5f5e 100644 --- a/src/lib/eo/eo_private.h +++ b/src/lib/eo/eo_private.h @@ -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. */ diff --git a/src/tests/eo/suite/eo_test_lifecycle.c b/src/tests/eo/suite/eo_test_lifecycle.c index 224c9c738c..381f153755 100644 --- a/src/tests/eo/suite/eo_test_lifecycle.c +++ b/src/tests/eo/suite/eo_test_lifecycle.c @@ -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); }