diff --git a/legacy/eobj/lib/Eobj.h b/legacy/eobj/lib/Eobj.h index 255dae57ff..6303c23e12 100644 --- a/legacy/eobj/lib/Eobj.h +++ b/legacy/eobj/lib/Eobj.h @@ -399,6 +399,25 @@ EAPI void eobj_unref(Eobj *obj); */ EAPI int eobj_ref_get(const Eobj *obj); +#define eobj_xref(obj, ref_obj) eobj_xref_internal(obj, ref_obj, __FILE__, __LINE__) + +/** + * @brief Increment the object's reference count by 1. + * @param obj the object to work on. + * @return The object passed. + * + * @see eobj_xunref() + */ +EAPI Eobj *eobj_xref_internal(Eobj *obj, const Eobj *ref_obj, const char *file, int line); + +/** + * @brief Decrement the object's reference count by 1 and free it if needed. + * @param obj the object to work on. + * + * @see eobj_xref_internal() + */ +EAPI void eobj_xunref(Eobj *obj, const Eobj *ref_obj); + /** * @brief Delete the object passed (disregarding ref count). * @param obj the object to work on. diff --git a/legacy/eobj/lib/eobj.c b/legacy/eobj/lib/eobj.c index e449575844..36a8674b85 100644 --- a/legacy/eobj/lib/eobj.c +++ b/legacy/eobj/lib/eobj.c @@ -26,6 +26,10 @@ struct _Eobj { const Eobj_Class *klass; void *data_blob; int refcount; +#ifndef NDEBUG + Eina_Inlist *xrefs; +#endif + Eina_List *composite_objects; Eina_Inlist *callbacks; @@ -755,6 +759,63 @@ fail: return NULL; } +typedef struct +{ + EINA_INLIST; + const Eobj *ref_obj; + const char *file; + int line; +} Eobj_Xref_Node; + +EAPI Eobj * +eobj_xref_internal(Eobj *obj, const Eobj *ref_obj, const char *file, int line) +{ + eobj_ref(obj); + +#ifndef NDEBUG + Eobj_Xref_Node *xref = calloc(1, sizeof(*xref)); + xref->ref_obj = ref_obj; + xref->file = file; + xref->line = line; + + /* FIXME: Make it sorted. */ + obj->xrefs = eina_inlist_prepend(obj->xrefs, EINA_INLIST_GET(xref)); +#else + (void) ref_obj; + (void) file; + (void) line; +#endif + + return obj; +} + +EAPI void +eobj_xunref(Eobj *obj, const Eobj *ref_obj) +{ +#ifndef NDEBUG + Eobj_Xref_Node *xref = NULL; + EINA_INLIST_FOREACH(obj->xrefs, xref) + { + if (xref->ref_obj == ref_obj) + break; + } + + if (xref) + { + obj->xrefs = eina_inlist_remove(obj->xrefs, EINA_INLIST_GET(xref)); + free(xref); + } + else + { + ERR("ref_obj (%p) does not reference obj (%p). Aborting unref.", ref_obj, obj); + return; + } +#else + (void) ref_obj; +#endif + eobj_unref(obj); +} + EAPI Eobj * eobj_ref(Eobj *obj) { @@ -804,6 +865,17 @@ eobj_unref(Eobj *obj) obj->kls_itr = nitr; } +#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 = nitr->next; + free(EINA_INLIST_CONTAINER_GET(obj->xrefs, Eobj_Kls_Itr_Node)); + obj->xrefs = nitr; + } +#endif + Eina_List *itr, *itr_n; Eobj *emb_obj; EINA_LIST_FOREACH_SAFE(obj->composite_objects, itr, itr_n, emb_obj) diff --git a/legacy/eobj/tests/eobj_test_general.c b/legacy/eobj/tests/eobj_test_general.c index f553d50e2c..6809f7de00 100644 --- a/legacy/eobj/tests/eobj_test_general.c +++ b/legacy/eobj/tests/eobj_test_general.c @@ -19,6 +19,45 @@ START_TEST(eobj_simple) } END_TEST +START_TEST(eobj_xrefs) +{ + eobj_init(); + Eobj *obj = eobj_add(SIMPLE_CLASS, NULL); + Eobj *obj2 = eobj_add(SIMPLE_CLASS, NULL); + Eobj *obj3 = eobj_add(SIMPLE_CLASS, NULL); + + eobj_xref(obj, obj2); + fail_if(eobj_ref_get(obj) != 2); + eobj_xref(obj, obj3); + fail_if(eobj_ref_get(obj) != 3); + + eobj_xunref(obj, obj2); + fail_if(eobj_ref_get(obj) != 2); + eobj_xunref(obj, obj3); + fail_if(eobj_ref_get(obj) != 1); + +#ifndef NDEBUG + eobj_xunref(obj, obj3); + fail_if(eobj_ref_get(obj) != 1); + + eobj_xref(obj, obj2); + fail_if(eobj_ref_get(obj) != 2); + + eobj_xunref(obj, obj3); + fail_if(eobj_ref_get(obj) != 2); + + eobj_xunref(obj, obj2); + fail_if(eobj_ref_get(obj) != 1); +#endif + + eobj_unref(obj); + eobj_unref(obj2); + eobj_unref(obj3); + + eobj_shutdown(); +} +END_TEST + START_TEST(eobj_weak_reference) { eobj_init(); @@ -161,4 +200,5 @@ void eobj_test_general(TCase *tc) tcase_add_test(tc, eobj_op_errors); tcase_add_test(tc, eobj_simple); tcase_add_test(tc, eobj_weak_reference); + tcase_add_test(tc, eobj_xrefs); }