summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarsten Haitzler (Rasterman) <raster@rasterman.com>2016-03-08 16:57:22 +0900
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>2016-03-08 16:57:22 +0900
commit3df71ab0f668967cf37813cc2322d1993a4d5db1 (patch)
tree5a692df888b9f4978e329284d2ca894157e6aad2
parent96bb964dd599d25778ad2f0378d0192b9855311b (diff)
eo del interceptor: add the ability to intercept deletions of eo objects
Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
-rw-r--r--src/lib/eo/Eo.h58
-rw-r--r--src/lib/eo/eo.c16
-rw-r--r--src/lib/eo/eo_private.h9
-rw-r--r--src/tests/eo/suite/eo_test_general.c55
4 files changed, 138 insertions, 0 deletions
diff --git a/src/lib/eo/Eo.h b/src/lib/eo/Eo.h
index cf10bb3931..4bfae9765e 100644
--- a/src/lib/eo/Eo.h
+++ b/src/lib/eo/Eo.h
@@ -166,6 +166,16 @@ typedef struct _Eo_Event Eo_Event;
166 */ 166 */
167typedef Eina_Bool (*Eo_Event_Cb)(void *data, const Eo_Event *event); 167typedef Eina_Bool (*Eo_Event_Cb)(void *data, const Eo_Event *event);
168 168
169/**
170 * @typedef Eo_Del_Intercept
171 *
172 * A function to be called on object deletion/destruction instead of normal
173 * destruction taking place.
174 *
175 * @param obj_id The object needing destruction
176 */
177typedef void (*Eo_Del_Intercept) (Eo *obj_id);
178
169#include "eo_base.eo.h" 179#include "eo_base.eo.h"
170#define EO_CLASS EO_BASE_CLASS 180#define EO_CLASS EO_BASE_CLASS
171 181
@@ -787,6 +797,54 @@ EAPI void eo_unref(const Eo *obj);
787EAPI int eo_ref_get(const Eo *obj); 797EAPI int eo_ref_get(const Eo *obj);
788 798
789/** 799/**
800 * @brief Set a deletion interceptor function
801 * @param obj The object to set the interceptor on
802 * @param del_intercept_func The interceptor function to call
803 *
804 * This sets the function @p del_intercept_func to be called when an object
805 * is about to go from a reference count of 1 to 0, thus triggering actual
806 * destruction of the object. Instead of going to a reference count of 0 and
807 * being destroyed, the object will stay alive with a reference count of 1
808 * and this intercept function will be called instead. It is the job of
809 * this interceptor function to handle any further deletion of of the object
810 * from here.
811 *
812 * Note that by default objects have no interceptor function set, and thus
813 * will be destroyed as normal. To return an object to this state, simply
814 * set the @p del_intercept_func to NULL which is the default.
815 *
816 * A good use for this feature is to ensure an object is destroyed by its
817 * owning main loop and not in a foreign loop. This makes it possible to
818 * safely unrefor delete objects from any loop as an interceptor can be set
819 * on an object that will abort destruction and instead queue the object
820 * on its owning loop to be destroyed at some time in the future and now
821 * set the intercept function to NULL so it is not called again on the next
822 * "real deletion".
823 *
824 * @see eo_del_intercept_get()
825 * @see eo_unref()
826 * @see eo_del()
827 */
828EAPI void eo_del_intercept_set(Eo *obj, Eo_Del_Intercept del_intercept_func);
829
830/**
831 * @brief Get the deletion interceptor function
832 * @param obj The object to get the interceptor of
833 * @return The intercept function or NULL if none is set.
834 *
835 * This returns the interceptor function set by eo_del_intercept_set(). Note
836 * that objects by default have no interceptor (NULL) set, but certain
837 * classes may set one up in a constructor, so it is important to be able
838 * to get the interceptor function to know if this has happend and
839 * if you want to override this interceptor, be sure to call it after your
840 * own interceptor function has finished. It would generally be a bad idea
841 * though to override these functions.
842 *
843 * @see eo_del_intercept_set()
844 */
845EAPI Eo_Del_Intercept eo_del_intercept_get(const Eo *obj);
846
847/**
790 * @brief Unrefs the object and reparents it to NULL. 848 * @brief Unrefs the object and reparents it to NULL.
791 * @param obj the object to work on. 849 * @param obj the object to work on.
792 * 850 *
diff --git a/src/lib/eo/eo.c b/src/lib/eo/eo.c
index 996b961b64..ba12fbd252 100644
--- a/src/lib/eo/eo.c
+++ b/src/lib/eo/eo.c
@@ -1332,6 +1332,22 @@ eo_ref_get(const Eo *obj_id)
1332 return obj->refcount; 1332 return obj->refcount;
1333} 1333}
1334 1334
1335EAPI void
1336eo_del_intercept_set(Eo *obj_id, Eo_Del_Intercept del_intercept_func)
1337{
1338 EO_OBJ_POINTER_RETURN(obj_id, obj);
1339
1340 obj->del_intercept = del_intercept_func;
1341}
1342
1343EAPI Eo_Del_Intercept
1344eo_del_intercept_get(const Eo *obj_id)
1345{
1346 EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, NULL);
1347
1348 return obj->del_intercept;
1349}
1350
1335void 1351void
1336_eo_condtor_done(Eo *obj_id) 1352_eo_condtor_done(Eo *obj_id)
1337{ 1353{
diff --git a/src/lib/eo/eo_private.h b/src/lib/eo/eo_private.h
index 3ee413477e..323fc02117 100644
--- a/src/lib/eo/eo_private.h
+++ b/src/lib/eo/eo_private.h
@@ -95,6 +95,7 @@ struct _Eo_Object
95#endif 95#endif
96 96
97 Eina_List *composite_objects; 97 Eina_List *composite_objects;
98 Eo_Del_Intercept del_intercept;
98 99
99 int refcount; 100 int refcount;
100 int datarefcount; 101 int datarefcount;
@@ -295,6 +296,14 @@ _eo_unref(_Eo_Object *obj)
295 ERR("Object %p deletion already triggered. You wrongly call eo_unref() within a destructor.", _eo_id_get(obj)); 296 ERR("Object %p deletion already triggered. You wrongly call eo_unref() within a destructor.", _eo_id_get(obj));
296 return; 297 return;
297 } 298 }
299
300 if (obj->del_intercept)
301 {
302 (obj->refcount)++;
303 obj->del_intercept(_eo_id_get(obj));
304 return;
305 }
306
298 obj->del_triggered = EINA_TRUE; 307 obj->del_triggered = EINA_TRUE;
299 308
300 _eo_del_internal(__FILE__, __LINE__, obj); 309 _eo_del_internal(__FILE__, __LINE__, obj);
diff --git a/src/tests/eo/suite/eo_test_general.c b/src/tests/eo/suite/eo_test_general.c
index 9a0db0fbc7..2960902715 100644
--- a/src/tests/eo/suite/eo_test_general.c
+++ b/src/tests/eo/suite/eo_test_general.c
@@ -950,6 +950,60 @@ START_TEST(eo_add_failures)
950} 950}
951END_TEST 951END_TEST
952 952
953static Eina_Bool intercepted = EINA_FALSE;
954
955static void
956_del_intercept(Eo *obj)
957{
958 intercepted = EINA_TRUE;
959 eo_del_intercept_set(obj, NULL);
960 eo_unref(obj);
961}
962
963START_TEST(eo_del_intercept)
964{
965#ifdef HAVE_EO_ID
966 eo_init();
967
968 static const Eo_Class_Description class_desc = {
969 EO_VERSION,
970 "Simple",
971 EO_CLASS_TYPE_REGULAR,
972 EO_CLASS_DESCRIPTION_NOOPS(),
973 NULL,
974 0,
975 NULL,
976 NULL
977 };
978
979 const Eo_Class *klass = eo_class_new(&class_desc, EO_CLASS, NULL);
980 fail_if(!klass);
981
982 /* Check unref interception */
983 intercepted = EINA_FALSE;
984 Eo *obj = eo_add(klass, NULL);
985 fail_if(!obj);
986 fail_if(!eo_isa(obj, klass));
987 eo_del_intercept_set(obj, _del_intercept);
988 eo_unref(obj);
989 fail_if(!intercepted);
990 fail_if(eo_isa(obj, klass));
991
992 /* Check del interception */
993 intercepted = EINA_FALSE;
994 obj = eo_add(klass, NULL);
995 fail_if(!obj);
996 fail_if(!eo_isa(obj, klass));
997 eo_del_intercept_set(obj, _del_intercept);
998 eo_del(obj);
999 fail_if(!intercepted);
1000 fail_if(eo_isa(obj, klass));
1001
1002 eo_shutdown();
1003#endif
1004}
1005END_TEST
1006
953void eo_test_general(TCase *tc) 1007void eo_test_general(TCase *tc)
954{ 1008{
955 tcase_add_test(tc, eo_simple); 1009 tcase_add_test(tc, eo_simple);
@@ -967,4 +1021,5 @@ void eo_test_general(TCase *tc)
967 tcase_add_test(tc, eo_add_do_and_custom); 1021 tcase_add_test(tc, eo_add_do_and_custom);
968 tcase_add_test(tc, eo_pointers_indirection); 1022 tcase_add_test(tc, eo_pointers_indirection);
969 tcase_add_test(tc, eo_add_failures); 1023 tcase_add_test(tc, eo_add_failures);
1024 tcase_add_test(tc, eo_del_intercept);
970} 1025}