diff --git a/legacy/eobj/lib/Eo.h b/legacy/eobj/lib/Eo.h index b90bcceaa6..376b0fb6ec 100644 --- a/legacy/eobj/lib/Eo.h +++ b/legacy/eobj/lib/Eo.h @@ -749,7 +749,35 @@ EAPI void eo_composite_object_detach(Eo *obj, Eo *comp_obj); * @see eo_composite_object_attach() * @see eo_composite_object_detach() */ -EAPI Eina_Bool eo_composite_is(Eo *comp_obj); +EAPI Eina_Bool eo_composite_is(const Eo *comp_obj); + +/** + * @brief Enable or disable the manual free feature. + * @param obj the object to work on. + * @param manual_free indicates if the free is manual (EINA_TRUE) or automatic (EINA_FALSE). + * + * The developer is in charge to call the function eo_manual_free to free the memory allocated for this object. + * + * Do not use, unless you really know what you are doing. It's used by Evas + * because evas wants to keep its private data available even after the object + * is deleted. Setting this to true makes Eo destruct the object but not free + * the private data or the object itself. + * + * @see eo_manual_free() + */ +EAPI void eo_manual_free_set(Eo *obj, Eina_Bool manual_free); + +/** + * @brief Frees the object. + * @param obj the object to work on. + * This function must be called by the developer if the function + * eo_manual_free_set has been called before with the parameter EINA_TRUE. + * An error will be printed if this function is called when the manual + * free option is not set to EINA_TRUE or the number of refs is not 0. + * + * @see eo_manual_free_set() + */ +EAPI void eo_manual_free(Eo *obj); /** * @} diff --git a/legacy/eobj/lib/eo.c b/legacy/eobj/lib/eo.c index 0f99638b72..e79b95375b 100644 --- a/legacy/eobj/lib/eo.c +++ b/legacy/eobj/lib/eo.c @@ -44,6 +44,7 @@ struct _Eo { Eina_Bool del:1; Eina_Bool construct_error:1; + Eina_Bool manual_free:1; }; /* Start of Dich */ @@ -1193,6 +1194,13 @@ _eo_del_internal(Eo *obj) obj->refcount--; } +static inline void +_eo_free(Eo *obj) +{ + EINA_MAGIC_SET(obj, EO_DELETED_EINA_MAGIC); + free(obj); +} + static inline void _eo_unref(Eo *obj) { @@ -1201,18 +1209,17 @@ _eo_unref(Eo *obj) _eo_del_internal(obj); #ifndef NDEBUG - /* If for some reason it's not empty, clear it. */ - while (obj->xrefs) - { - WRN("obj->xrefs is not empty, possibly a bug, please report. - An error will be reported for each xref in the stack."); - Eina_Inlist *nitr = obj->xrefs->next; - free(EINA_INLIST_CONTAINER_GET(obj->xrefs, Eo_Xref_Node)); - obj->xrefs = nitr; - } + /* If for some reason it's not empty, clear it. */ + while (obj->xrefs) + { + WRN("obj->xrefs is not empty, possibly a bug, please report. - An error will be reported for each xref in the stack."); + Eina_Inlist *nitr = obj->xrefs->next; + free(EINA_INLIST_CONTAINER_GET(obj->xrefs, Eo_Xref_Node)); + obj->xrefs = nitr; + } #endif - EINA_MAGIC_SET(obj, EO_DELETED_EINA_MAGIC); - free(obj); + if (!obj->manual_free) _eo_free(obj); } } @@ -1466,7 +1473,7 @@ eo_composite_object_detach(Eo *obj, Eo *emb_obj) } EAPI Eina_Bool -eo_composite_is(Eo *emb_obj) +eo_composite_is(const Eo *emb_obj) { if (!EINA_MAGIC_CHECK(emb_obj, EO_EINA_MAGIC)) { @@ -1490,4 +1497,30 @@ eo_composite_is(Eo *emb_obj) return EINA_FALSE; } +EAPI void +eo_manual_free_set(Eo *obj, Eina_Bool manual_free) +{ + EO_MAGIC_RETURN(obj, EO_EINA_MAGIC); + obj->manual_free = manual_free; +} + +EAPI void +eo_manual_free(Eo *obj) +{ + EO_MAGIC_RETURN(obj, EO_EINA_MAGIC); + + if (EINA_FALSE == obj->manual_free) + { + ERR("Tried to free manually the object %p while the option has not been set; see eo_manual_free_set for more information.", obj); + return; + } + + if (0 != obj->refcount) + { + ERR("Tried deleting the object %p while still referenced(%d).", obj, eo_ref_get(obj)); + return; + } + + _eo_free(obj); +} diff --git a/legacy/eobj/tests/eo_test_general.c b/legacy/eobj/tests/eo_test_general.c index e8caea41db..1bf5530989 100644 --- a/legacy/eobj/tests/eo_test_general.c +++ b/legacy/eobj/tests/eo_test_general.c @@ -59,6 +59,76 @@ START_TEST(eo_data_fetch) } END_TEST +static void +_man_con(Eo *obj, void *data EINA_UNUSED) +{ + eo_manual_free_set(obj, EINA_TRUE); + eo_constructor_super(obj); +} + +static void +_man_des(Eo *obj, void *data EINA_UNUSED) +{ + eo_destructor_super(obj); + eo_manual_free_set(obj, EINA_FALSE); +} + +START_TEST(eo_man_free) +{ + eo_init(); + + /* Usually should be const, not const only for the test... */ + static Eo_Class_Description class_desc = { + "Simple2", + EO_CLASS_TYPE_REGULAR, + EO_CLASS_DESCRIPTION_OPS(NULL, NULL, 0), + NULL, + 10, + _man_con, + _man_des, + NULL, + NULL + }; + + const Eo_Class *klass = eo_class_new(&class_desc, EO_BASE_CLASS, NULL); + fail_if(!klass); + + Eo *obj = eo_add(klass, NULL); + fail_if(!obj); + eo_unref(obj); + + obj = eo_add(klass, NULL); + fail_if(!obj); + eo_manual_free(obj); + eo_unref(obj); + + class_desc.destructor = NULL; + klass = eo_class_new(&class_desc, EO_BASE_CLASS, NULL); + fail_if(!klass); + + obj = eo_add(klass, NULL); + fail_if(!obj); + eo_manual_free(obj); + eo_unref(obj); + + obj = eo_add(klass, NULL); + fail_if(!obj); + eo_unref(obj); + eo_manual_free(obj); + + class_desc.constructor = NULL; + klass = eo_class_new(&class_desc, EO_BASE_CLASS, NULL); + fail_if(!klass); + + obj = eo_add(klass, NULL); + fail_if(!obj); + eo_manual_free(obj); + eo_unref(obj); + + eo_shutdown(); +} +END_TEST + START_TEST(eo_refs) { eo_init(); @@ -371,6 +441,11 @@ START_TEST(eo_magic_checks) eo_do(obj, eo_event_callback_forwarder_add(NULL, (Eo *) buf)); eo_do(obj, eo_event_callback_forwarder_del(NULL, (Eo *) buf)); + eo_manual_free_set((Eo *) buf, EINA_TRUE); + eo_manual_free((Eo *) buf); + eo_manual_free_set(NULL, EINA_TRUE); + eo_manual_free(NULL); + eo_unref(obj); eo_shutdown(); @@ -386,4 +461,5 @@ void eo_test_general(TCase *tc) tcase_add_test(tc, eo_refs); tcase_add_test(tc, eo_magic_checks); tcase_add_test(tc, eo_data_fetch); + tcase_add_test(tc, eo_man_free); }