efl/src/lib/eo/eo_private.h

427 lines
12 KiB
C
Raw Normal View History

#ifndef _EO_PRIVATE_H
#define _EO_PRIVATE_H
#include <Eo.h>
#include <Eina.h>
#define EO_EINA_MAGIC 0xa186bc32
#define EO_EINA_MAGIC_STR "Eo"
#define EO_FREED_EINA_MAGIC 0xa186bb32
#define EO_FREED_EINA_MAGIC_STR "Eo - Freed object"
#define EO_CLASS_EINA_MAGIC 0xa186ba32
#define EO_CLASS_EINA_MAGIC_STR "Efl Class"
#define EO_MAGIC_RETURN_VAL(d, magic, ret) \
do { \
if (!EINA_MAGIC_CHECK(d, magic)) \
{ \
EINA_MAGIC_FAIL(d, magic); \
return ret; \
} \
} while (0)
#define EO_MAGIC_RETURN(d, magic) \
do { \
if (!EINA_MAGIC_CHECK(d, magic)) \
{ \
EINA_MAGIC_FAIL(d, magic); \
return; \
} \
} while (0)
extern int _eo_log_dom;
#ifdef CRI
#undef CRI
#endif
#define CRI(...) EINA_LOG_DOM_CRIT(_eo_log_dom, __VA_ARGS__)
#ifdef ERR
#undef ERR
#endif
#define ERR(...) EINA_LOG_DOM_ERR(_eo_log_dom, __VA_ARGS__)
#ifdef WRN
#undef WRN
#endif
#define WRN(...) EINA_LOG_DOM_WARN(_eo_log_dom, __VA_ARGS__)
#ifdef INF
#undef INF
#endif
#define INF(...) EINA_LOG_DOM_INFO(_eo_log_dom, __VA_ARGS__)
#ifdef DBG
#undef DBG
#endif
#define DBG(...) EINA_LOG_DOM_DBG(_eo_log_dom, __VA_ARGS__)
typedef uintptr_t Eo_Id;
typedef struct _Efl_Class _Efl_Class;
typedef struct _Eo_Header Eo_Header;
typedef struct _Efl_Object_Optional Efl_Object_Optional;
/* Allocates an entry for the given object */
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
static inline Eo_Id _eo_id_allocate(const _Eo_Object *obj, const Eo *parent_id);
/* Releases an entry by the object id */
static inline void _eo_id_release(const Eo_Id obj_id);
void _eo_condtor_done(Eo *obj);
typedef struct _Dich_Chain1 Dich_Chain1;
typedef struct _Eo_Vtable
{
Dich_Chain1 *chain;
unsigned int size;
} Eo_Vtable;
/* Clean the vtable. */
void _vtable_func_clean_all(Eo_Vtable *vtable);
struct _Eo_Header
{
Eo_Id id;
};
struct _Efl_Object_Optional
{
Eo_Vtable *vtable;
Eina_List *composite_objects;
Efl_Del_Intercept del_intercept;
};
struct _Eo_Object
{
Eo_Header header;
EINA_INLIST;
const _Efl_Class *klass;
#ifdef EO_DEBUG
Eina_Inlist *xrefs;
Eina_Inlist *data_xrefs;
#endif
const Efl_Object_Optional *opt; // eina cow
_Efl_Class *cur_klass;
short refcount;
short user_refcount;
#ifdef EO_DEBUG
short datarefcount;
#endif
Eina_Bool condtor_done:1;
Eina_Bool finalized:1;
Eina_Bool super:1;
Eina_Bool invalidate:1;
Eina_Bool is_invalidating:1;
Eina_Bool parent : 1;
Eina_Bool unref_compensate : 1;
Eina_Bool allow_parent_unref : 1;
Eina_Bool noref_event : 1;
Eina_Bool del_triggered:1;
Eina_Bool destructed:1;
Eina_Bool manual_free:1;
unsigned char auto_unref : 1; // unref after 1 call - hack for parts
};
2016-08-15 09:11:13 -07:00
/* How we search and store the implementations in classes. */
#define DICH_CHAIN_LAST_BITS 5
#define DICH_CHAIN_LAST_SIZE (1 << DICH_CHAIN_LAST_BITS)
#define DICH_CHAIN1(x) ((x) >> DICH_CHAIN_LAST_BITS)
#define DICH_CHAIN_LAST(x) ((x) & ((1 << DICH_CHAIN_LAST_BITS) - 1))
extern Eina_Cow *efl_object_optional_cow;
#define EO_OPTIONAL_COW_WRITE(_obj) ({ Efl_Object_Optional *_cow = eina_cow_write(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt)); _cow; })
#define EO_OPTIONAL_COW_END(_cow, _obj) eina_cow_done(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt), _cow, EINA_TRUE)
#define EO_OPTIONAL_COW_SET(_obj, _field, _value) do { \
typeof (_value) _val = _value; \
if (_obj->opt->_field != _val) {\
Efl_Object_Optional *_obj##_cow = EO_OPTIONAL_COW_WRITE(_obj); \
_obj##_cow->_field = _val; \
EO_OPTIONAL_COW_END(_obj##_cow, _obj); \
}} while (0)
#define EO_VTABLE(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable))
typedef void (*Eo_Op_Func_Type)(Eo *, void *class_data);
2014-04-02 01:26:45 -07:00
typedef struct
{
Eo_Op_Func_Type func;
const _Efl_Class *src;
} op_type_funcs;
2016-08-15 09:11:13 -07:00
typedef struct _Dich_Chain2
{
op_type_funcs funcs[DICH_CHAIN_LAST_SIZE];
unsigned short refcount;
} Dich_Chain2;
struct _Dich_Chain1
{
2016-08-15 09:11:13 -07:00
Dich_Chain2 *chain2;
};
typedef struct
{
const _Efl_Class *klass;
size_t offset;
} Eo_Extension_Data_Offset;
struct _Efl_Class
{
Eo_Header header;
const _Efl_Class *parent;
const Efl_Class_Description *desc;
Eo_Vtable vtable;
const _Efl_Class **extensions;
Eo_Extension_Data_Offset *extn_data_off;
const _Efl_Class **mro;
const Efl_Object_Property_Reflection_Ops *reflection;
/* cached object for faster allocation */
struct {
Eina_Trash *trash;
Eina_Spinlock trash_lock;
unsigned int trash_count;
} objects;
/* cached iterator for faster allocation cycle */
struct {
Eina_Trash *trash;
Eina_Spinlock trash_lock;
unsigned int trash_count;
} iterators;
unsigned int obj_size; /**< size of an object of this class */
unsigned int base_id;
unsigned int data_offset; /* < Offset of the data within object data. */
Efl object: change the way we set class's functions. This is another follow up to the investigations of T4227. As stated there, in any PIE (a shared library is one), structures, even const ones end up being written to because of dynamic relocation. This means that using static const structures has actually lead to no savings, only waste. Since we never really needed them, using them made things even worse than just having a different API that doesn't save them. Thus, this commit changes the way we set the functions. Instead of passing a pre-populated struct, we now just have an initialiser function where you set the functions. This on its own doesn't significantly reduce the amount of dirty memory pages for a reason I have yet to uncover, though I believe it's done as a misguided compiler optimisation. However, this design is flexible enough so we can change to another one that is quite ugly, but I have already tested and proven that does that. This patch series doesn't include the better improvement (passing everything on the stack as va_args) because the API was too ugly for me to bear, and I would rather first make sure there is no way to force the compiler to do the right thing here. Unfortunately this commit gives up on useless stricter validation. Before this commit we would make sure that we are only overriding functions correctly defined in our hierarchy. With this one, we don't anymore. This is not a big problem though because this is a check that is also enforced by Eolian. So as long as you are using Eolian, you should be fine. Breaks API and ABI! @feature
2016-08-24 07:59:28 -07:00
unsigned int ops_count; /* < Offset of the data within object data. */
Eina_Thread construction_thread; /** < the thread which called the class constructor */
Eina_Bool constructed : 1;
Efl object: change the way we set class's functions. This is another follow up to the investigations of T4227. As stated there, in any PIE (a shared library is one), structures, even const ones end up being written to because of dynamic relocation. This means that using static const structures has actually lead to no savings, only waste. Since we never really needed them, using them made things even worse than just having a different API that doesn't save them. Thus, this commit changes the way we set the functions. Instead of passing a pre-populated struct, we now just have an initialiser function where you set the functions. This on its own doesn't significantly reduce the amount of dirty memory pages for a reason I have yet to uncover, though I believe it's done as a misguided compiler optimisation. However, this design is flexible enough so we can change to another one that is quite ugly, but I have already tested and proven that does that. This patch series doesn't include the better improvement (passing everything on the stack as va_args) because the API was too ugly for me to bear, and I would rather first make sure there is no way to force the compiler to do the right thing here. Unfortunately this commit gives up on useless stricter validation. Before this commit we would make sure that we are only overriding functions correctly defined in our hierarchy. With this one, we don't anymore. This is not a big problem though because this is a check that is also enforced by Eolian. So as long as you are using Eolian, you should be fine. Breaks API and ABI! @feature
2016-08-24 07:59:28 -07:00
Eina_Bool functions_set : 1;
/* [extensions*] + NULL */
/* [mro*] + NULL */
/* [extensions data offset] + NULL */
};
2013-09-23 02:07:07 -07:00
typedef struct
{
EINA_INLIST;
const Eo *ref_obj;
const char *data_klass;
2013-09-23 02:07:07 -07:00
const char *file;
int line;
} Eo_Xref_Node;
eo: allow valgrind-like tracking of object lifecycle. Eo pointer indirection is super nice as it avoids you to access invalid memory, but this extra checks inhibits valgrind's own tracking of memory lifecycle, usually it would report when the object was created and when the object is deleted, both as stack traces. This commits introduces logging of object creation and destruction under its own eina_log_domain and controlled by EO_LIFECYCLE_DEBUG and EO_LIFECYCLE_NO_DEBUG envvars. These will only be available if compiled with EO_DEBUG, thus shouldn't cause any performance hits on production code. Running a bogus app with invalid efl_class_name_get() and double efl_del() will report as below: ```sh $ export EO_LIFECYCLE_NO_DEBUG=Efl_Loop_Timer,Efl_Promise,Efl_Future $ export EO_LIFECYCLE_DEBUG=1 $ export EINA_LOG_LEVELS=eo_lifecycle:4 $ /tmp/bogus_app DBG:eo_lifecycle lib/eo/eo.c:2712 _eo_log_obj_init() will log all object allocation and free DBG:eo_lifecycle lib/eo/eo.c:2788 _eo_log_obj_init() will NOT log class 'Efl_Future' DBG:eo_lifecycle lib/eo/eo.c:2788 _eo_log_obj_init() will NOT log class 'Efl_Promise' DBG:eo_lifecycle lib/eo/eo.c:2788 _eo_log_obj_init() will NOT log class 'Efl_Loop_Timer' DBG:eo_lifecycle lib/eo/eo.c:2665 _eo_log_obj_new() new obj=0x563fa35a1aa0 obj_id=0x4000000002cf38ef class=0x563fa35a1450 (Efl_Vpath_Core) [0.0004] DBG:eo_lifecycle lib/eo/eo.c:2665 _eo_log_obj_new() new obj=0x563fa35af8d0 obj_id=0x4000000006cf38f0 class=0x563fa35aecf0 (Efl_Loop) [0.0005] DBG:eo_lifecycle lib/eo/eo.c:2665 _eo_log_obj_new() new obj=0x563fa35d61a0 obj_id=0x400000007ecf390e class=0x563fa35d48f0 (Efl_Net_Dialer_Simple) [0.0054] DBG:eo_lifecycle lib/eo/eo.c:2665 _eo_log_obj_new() new obj=0x563fa35d6470 obj_id=0x4000000082cf390f class=0x563fa35d0d60 (Efl_Net_Dialer_Tcp) [0.0055] DBG:eo_lifecycle lib/eo/eo.c:2665 _eo_log_obj_new() new obj=0x563fa35d75b0 obj_id=0x4000000086cf3910 class=0x563fa35d66b0 (Efl_Io_Queue) [0.0056] DBG:eo_lifecycle lib/eo/eo.c:2665 _eo_log_obj_new() new obj=0x563fa35d8f70 obj_id=0x400000008acf3911 class=0x563fa35d7860 (Efl_Io_Copier) [0.0057] DBG:eo_lifecycle lib/eo/eo.c:2665 _eo_log_obj_new() new obj=0x563fa35df980 obj_id=0x40000000a6cf3918 class=0x563fa35d66b0 (Efl_Io_Queue) [0.0058] DBG:eo_lifecycle lib/eo/eo.c:2665 _eo_log_obj_new() new obj=0x563fa35dfc30 obj_id=0x40000000aacf3919 class=0x563fa35d7860 (Efl_Io_Copier) [0.0058] will efl_class_name_get() with invalid handle: ERR:eo lib/eo/eo.c:1013 efl_class_name_get() Class (0x2000000000000029) is an invalid ref. ERR:eo_lifecycle lib/eo/eo.c:1013 efl_class_name_get() obj_id=0x2000000000000029 was neither created or deleted (EO_LIFECYCLE_NO_DEBUG='Efl_Loop_Timer,Efl_Promise,Efl_Future'). DBG:eo_lifecycle lib/eo/eo.c:2688 _eo_log_obj_free() free obj=0x563fa35df980 obj_id=0x40000000a6cf3918 class=0x563fa35d66b0 (Efl_Io_Queue) [0.0061] DBG:eo_lifecycle lib/eo/eo.c:2688 _eo_log_obj_free() free obj=0x563fa35dfc30 obj_id=0x40000000aacf3919 class=0x563fa35d7860 (Efl_Io_Copier) [0.0061] DBG:eo_lifecycle lib/eo/eo.c:2688 _eo_log_obj_free() free obj=0x563fa35d75b0 obj_id=0x4000000086cf3910 class=0x563fa35d66b0 (Efl_Io_Queue) [0.0061] DBG:eo_lifecycle lib/eo/eo.c:2688 _eo_log_obj_free() free obj=0x563fa35d8f70 obj_id=0x400000008acf3911 class=0x563fa35d7860 (Efl_Io_Copier) [0.0061] DBG:eo_lifecycle lib/eo/eo.c:2688 _eo_log_obj_free() free obj=0x563fa35d6470 obj_id=0x4000000082cf390f class=0x563fa35d0d60 (Efl_Net_Dialer_Tcp) [0.0063] DBG:eo_lifecycle lib/eo/eo.c:2688 _eo_log_obj_free() free obj=0x563fa35d61a0 obj_id=0x400000007ecf390e class=0x563fa35d48f0 (Efl_Net_Dialer_Simple) [0.0063] will double free: ERR:eo ../src/lib/eo/efl_object.eo.c:78 efl_del() EOID 0x400000007ecf390e is not a valid object. EOID domain=0, current_domain=0, local_domain=0. EOID generation=2cf390e, id=1f, ref=1, super=0. Thread self=main. Available domains [0 1 ]. Maybe it has been deleted or does not belong to your thread? ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() obj_id=0x400000007ecf390e created obj=0x563fa35d61a0, class=0x563fa35d48f0 (Efl_Net_Dialer_Simple) [0.0054s, 0.0009 ago]: ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x007f2c0bc6d0ea: libeo_dbg.so+0x90ea (in src/lib/eo/.libs/libeo_dbg.so 0x7f2c0bc64000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x007f2c0bc6ca62: _efl_add_internal_start+0x1c2 (in src/lib/eo/.libs/libeo_dbg.so 0x7f2c0bc64000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x00563fa15dc95f: bogus_app+0x295f (in /tmp/bogus_app 0x563fa15da000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x007f2c0ace7291: __libc_start_main+0xf1 (in /usr/lib/libc.so.6 0x7f2c0acc7000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x00563fa15dc48a: _start+0x2a (in /tmp/bogus_app 0x563fa15da000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() obj_id=0x400000007ecf390e deleted obj=0x563fa35d61a0, class=0x563fa35d48f0 (Efl_Net_Dialer_Simple) [0.0063s, 0.0000 ago]: ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x007f2c0bc6d8ba: libeo_dbg.so+0x98ba (in src/lib/eo/.libs/libeo_dbg.so 0x7f2c0bc64000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x007f2c0bc6d711: libeo_dbg.so+0x9711 (in src/lib/eo/.libs/libeo_dbg.so 0x7f2c0bc64000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x007f2c0bc6beb8: libeo_dbg.so+0x7eb8 (in src/lib/eo/.libs/libeo_dbg.so 0x7f2c0bc64000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x007f2c0bc6c06e: _efl_object_call_end+0x4e (in src/lib/eo/.libs/libeo_dbg.so 0x7f2c0bc64000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x007f2c0bc75725: efl_del+0x105 (in src/lib/eo/.libs/libeo_dbg.so 0x7f2c0bc64000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x00563fa15dcd54: lt-efl_net_dialer_simple_example+0x2d54 (in /tmp/bogus_app 0x563fa15da000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x007f2c0ace7291: __libc_start_main+0xf1 (in /usr/lib/libc.so.6 0x7f2c0acc7000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() 0x00563fa15dc48a: _start+0x2a (in /tmp/bogus_app 0x563fa15da000) ERR:eo_lifecycle ../src/lib/eo/efl_object.eo.c:78 efl_del() obj_id=0x400000007ecf390e was already deleted 0.0000 seconds ago! ```
2016-12-02 14:48:37 -08:00
/* provide valgrind-like tracking of object allocationg and deletion */
void _eo_log_obj_report(const Eo_Id id, int log_level, const char *func_name, const char *file, int line);
void _efl_object_reuse(_Eo_Object *obj);
2016-10-21 07:02:27 -07:00
static inline
Eo *_eo_header_id_get(const Eo_Header *header)
{
return (Eo *) header->id;
}
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
/* Retrieves the pointer to the object from the id */
_Eo_Object *_eo_obj_pointer_get(const Eo_Id obj_id, const char *func_name, const char *file, int line);
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
static inline
Efl_Class *_eo_class_id_get(const _Efl_Class *klass)
{
return _eo_header_id_get((Eo_Header *) klass);
}
static inline
Eo *_eo_obj_id_get(const _Eo_Object *obj)
{
return _eo_header_id_get((Eo_Header *) obj);
}
static inline void
_eo_condtor_reset(_Eo_Object *obj)
{
obj->condtor_done = EINA_FALSE;
}
typedef struct _Efl_Object_Data Efl_Object_Data;
EOLIAN void _efl_object_parent_set(Eo *obj, Efl_Object_Data *pd, Eo *parent_id);
void _efl_invalidate(_Eo_Object *obj);
static inline void
_efl_del_internal(_Eo_Object *obj, const char *func_name, const char *file, int line)
{
/* We need that for the event callbacks that may ref/unref. */
obj->refcount++;
const _Efl_Class *klass = obj->klass;
// If the object hasn't been invalidated yet, time to do it
// before any destructor kick in. This can happen when
// the object has no parent and get deleted by efl_unref.
if (obj->parent)
{
Eo *parent = efl_parent_get(_eo_obj_id_get(obj));
ERR("Destructor path being taken while object [%s] still has a parent [%s] in state %i:%i.",
efl_debug_name_get(_eo_obj_id_get(obj)),
efl_debug_name_get(parent),
obj->is_invalidating, obj->invalidate);
efl_parent_set(_eo_obj_id_get(obj), NULL);
if (obj->parent)
{
CRI("Something is preventing [%s] from disconnecting from its parent, bypassing.",
efl_debug_name_get(_eo_obj_id_get(obj)));
_efl_object_parent_set(_eo_obj_id_get(obj), efl_data_scope_get(_eo_obj_id_get(obj), EFL_OBJECT_CLASS), NULL);
}
}
else if (!obj->invalidate || !obj->is_invalidating)
{
_efl_invalidate(obj);
}
efl_event_callback_call(_eo_obj_id_get(obj), EFL_EVENT_DEL, NULL);
_eo_condtor_reset(obj);
efl_destructor(_eo_obj_id_get(obj));
if (!obj->condtor_done)
{
ERR("in %s:%d: func '%s' Object of class '%s' - Not all of the object destructors have been executed.",
file, line, func_name, klass->desc->name);
}
/*FIXME: add eo_class_unref(klass) ? - just to clear the caches. */
{
Eina_List *itr, *itr_n;
Eo *emb_obj;
EINA_LIST_FOREACH_SAFE(obj->opt->composite_objects, itr, itr_n, emb_obj)
{
efl_composite_detach(_eo_obj_id_get(obj), emb_obj);
}
}
obj->destructed = EINA_TRUE;
obj->refcount--;
}
static inline Eina_Bool
_obj_is_override(_Eo_Object *obj)
{
return obj->opt->vtable != NULL;
}
void _eo_free(_Eo_Object *obj, Eina_Bool manual_free);
static inline _Eo_Object *
_efl_ref(_Eo_Object *obj)
{
obj->refcount++;
return obj;
}
#define _efl_unref(obj) _efl_unref_internal(obj, __FUNCTION__, __FILE__, __LINE__)
static inline void
_efl_unref_internal(_Eo_Object *obj, const char *func_name, const char *file, int line)
{
--(obj->refcount);
if (EINA_UNLIKELY(obj->refcount <= 0))
{
if (obj->user_refcount > 0)
{
ERR("Object %p is still refcounted %i by users, but internal refcount reached 0. This should never happen. Please report a bug and send a backtrace to EFL developer.", (Eo*) obj->header.id, obj->user_refcount);
_eo_log_obj_report((Eo_Id)_eo_obj_id_get(obj), EINA_LOG_LEVEL_ERR, __FUNCTION__, __FILE__, __LINE__);
return;
}
if (obj->refcount < 0)
{
ERR("in %s:%d: func '%s' Obj:%p. Refcount (%d) < 0. Too many unrefs.", file, line, func_name, obj, obj->refcount);
_eo_log_obj_report((Eo_Id)_eo_obj_id_get(obj), EINA_LOG_LEVEL_ERR, __FUNCTION__, __FILE__, __LINE__);
return;
}
if (obj->destructed)
{
ERR("in %s:%d: func '%s' Object %p already destructed.", file, line, func_name, _eo_obj_id_get(obj));
_eo_log_obj_report((Eo_Id)_eo_obj_id_get(obj), EINA_LOG_LEVEL_ERR, __FUNCTION__, __FILE__, __LINE__);
return;
}
if (obj->del_triggered)
{
ERR("in %s:%d: func '%s' Object %p deletion already triggered. You wrongly call efl_unref() within a destructor.", file, line, func_name, _eo_obj_id_get(obj));
_eo_log_obj_report((Eo_Id)_eo_obj_id_get(obj), EINA_LOG_LEVEL_ERR, __FUNCTION__, __FILE__, __LINE__);
return;
}
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
2016-03-07 23:57:22 -08:00
if (obj->opt->del_intercept)
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
2016-03-07 23:57:22 -08:00
{
Eo *obj_id = _eo_obj_id_get(obj);
efl_ref(obj_id);
obj->opt->del_intercept(obj_id);
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
2016-03-07 23:57:22 -08:00
return;
}
obj->del_triggered = EINA_TRUE;
_efl_del_internal(obj, func_name, file, line);
if (EINA_LIKELY(!obj->manual_free))
{
#ifdef EO_DEBUG
/* If for some reason it's not empty, clear it. */
Eo *obj_id = _eo_obj_id_get(obj);
while (obj->xrefs)
{
Eina_Inlist *nitr = obj->xrefs->next;
Eo_Xref_Node *xref = EINA_INLIST_CONTAINER_GET(obj->xrefs, Eo_Xref_Node);
ERR("in %s:%d: func '%s' Object %p is still referenced by object %p. Origin: %s:%d",
file, line, func_name, obj_id, xref->ref_obj, xref->file, xref->line);
eina_freeq_ptr_main_add(xref, free, sizeof(*xref));
obj->xrefs = nitr;
}
while (obj->data_xrefs)
{
Eina_Inlist *nitr = obj->data_xrefs->next;
Eo_Xref_Node *xref = EINA_INLIST_CONTAINER_GET(obj->data_xrefs, Eo_Xref_Node);
if (obj_id == xref->ref_obj)
{
WRN("in %s:%d: func '%s' Object %p still has a reference to its own data (subclass: %s). Origin: %s:%d",
file, line, func_name, obj_id, xref->data_klass, xref->file, xref->line);
}
else
{
ERR("in %s:%d: func '%s' Data of object %p (subclass: %s) is still referenced by object %p. Origin: %s:%d",
file, line, func_name, obj_id, xref->data_klass, xref->ref_obj, xref->file, xref->line);
}
eina_freeq_ptr_main_add(xref, free, sizeof(*xref));
obj->data_xrefs = nitr;
}
#endif
_eo_free(obj, EINA_FALSE);
}
else
_efl_ref(obj); /* If we manual free, we keep a phantom ref. */
}
}
#endif