1 #ifndef _EO_PRIVATE_H
2 #define _EO_PRIVATE_H
3 
4 #include <Eo.h>
5 #include <Eina.h>
6 
7 #define EO_EINA_MAGIC 0xa186bc32
8 #define EO_EINA_MAGIC_STR "Eo"
9 #define EO_FREED_EINA_MAGIC 0xa186bb32
10 #define EO_FREED_EINA_MAGIC_STR "Eo - Freed object"
11 #define EO_CLASS_EINA_MAGIC 0xa186ba32
12 #define EO_CLASS_EINA_MAGIC_STR "Efl Class"
13 
14 #define EO_MAGIC_RETURN_VAL(d, magic, ret) \
15    do { \
16         if (!EINA_MAGIC_CHECK(d, magic)) \
17           { \
18              EINA_MAGIC_FAIL(d, magic); \
19              return ret; \
20           } \
21    } while (0)
22 
23 #define EO_MAGIC_RETURN(d, magic) \
24    do { \
25         if (!EINA_MAGIC_CHECK(d, magic)) \
26           { \
27              EINA_MAGIC_FAIL(d, magic); \
28              return; \
29           } \
30    } while (0)
31 
32 
33 extern int _eo_log_dom;
34 
35 #ifdef CRI
36 #undef CRI
37 #endif
38 #define CRI(...) EINA_LOG_DOM_CRIT(_eo_log_dom, __VA_ARGS__)
39 
40 #ifdef ERR
41 #undef ERR
42 #endif
43 #define ERR(...) EINA_LOG_DOM_ERR(_eo_log_dom, __VA_ARGS__)
44 
45 #ifdef WRN
46 #undef WRN
47 #endif
48 #define WRN(...) EINA_LOG_DOM_WARN(_eo_log_dom, __VA_ARGS__)
49 
50 #ifdef INF
51 #undef INF
52 #endif
53 #define INF(...) EINA_LOG_DOM_INFO(_eo_log_dom, __VA_ARGS__)
54 
55 #ifdef DBG
56 #undef DBG
57 #endif
58 #define DBG(...) EINA_LOG_DOM_DBG(_eo_log_dom, __VA_ARGS__)
59 
60 typedef uintptr_t Eo_Id;
61 typedef struct _Efl_Class _Efl_Class;
62 typedef struct _Eo_Header Eo_Header;
63 typedef struct _Efl_Object_Optional Efl_Object_Optional;
64 
65 /* Allocates an entry for the given object */
66 static inline Eo_Id _eo_id_allocate(const _Eo_Object *obj, const Eo *parent_id);
67 
68 /* Releases an entry by the object id */
69 static inline void _eo_id_release(const Eo_Id obj_id);
70 
71 void _eo_condtor_done(Eo *obj);
72 
73 typedef struct _Eo_Vtable_Node Eo_Vtable_Node;
74 
75 typedef struct _Eo_Vtable
76 {
77    Eo_Vtable_Node *chain;
78    unsigned short size;
79 } Eo_Vtable;
80 
81 struct _Eo_Header
82 {
83      Eo_Id id;
84 };
85 
86 struct _Efl_Object_Optional
87 {
88    Eo_Vtable         *vtable;
89    Eina_List          *composite_objects;
90    Efl_Del_Intercept   del_intercept;
91 };
92 
93 struct _Eo_Object
94 {
95      Eo_Header header;
96      EINA_INLIST;
97      const _Efl_Class *klass;
98 #ifdef EO_DEBUG
99      Eina_Inlist *xrefs;
100      Eina_Inlist *data_xrefs;
101 #endif
102 
103      const Efl_Object_Optional *opt; // eina cow
104      _Efl_Class *cur_klass;
105 
106      short refcount;
107      short user_refcount;
108 #ifdef EO_DEBUG
109      short datarefcount;
110 #endif
111 
112      Eina_Bool condtor_done:1;
113      Eina_Bool finalized:1;
114      Eina_Bool super:1;
115      Eina_Bool invalidate:1;
116      Eina_Bool is_invalidating:1;
117      Eina_Bool parent : 1;
118      Eina_Bool unref_compensate : 1;
119      Eina_Bool allow_parent_unref : 1;
120 
121      Eina_Bool noref_event : 1;
122      Eina_Bool del_triggered:1;
123      Eina_Bool destructed:1;
124      Eina_Bool manual_free:1;
125      unsigned char auto_unref : 1; // unref after 1 call - hack for parts
126      Eina_Bool ownership_track:1;
127 };
128 
129 extern Eina_Cow *efl_object_optional_cow;
130 #define EO_OPTIONAL_COW_WRITE(_obj) ({ Efl_Object_Optional *_cow = eina_cow_write(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt)); _cow; })
131 #define EO_OPTIONAL_COW_END(_cow, _obj) eina_cow_done(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt), _cow, EINA_TRUE)
132 #define EO_OPTIONAL_COW_SET(_obj, _field, _value) do { \
133    typeof (_value) _val = _value; \
134    if (_obj->opt->_field != _val) {\
135       Efl_Object_Optional *_obj##_cow = EO_OPTIONAL_COW_WRITE(_obj); \
136       _obj##_cow->_field = _val; \
137       EO_OPTIONAL_COW_END(_obj##_cow, _obj); \
138    }} while (0)
139 #define EO_VTABLE(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable))
140 #define EO_VTABLE2(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable))
141 
142 typedef void (*Eo_Op_Func_Type)(Eo *, void *class_data);
143 
144 typedef struct
145 {
146    Eo_Op_Func_Type func;
147    const _Efl_Class *src;
148 } op_type_funcs;
149 
150 struct _Eo_Vtable_Node{
151    op_type_funcs *funcs;
152    unsigned short count;
153 };
154 
155 typedef struct
156 {
157    const _Efl_Class *klass;
158    size_t offset;
159 } Eo_Extension_Data_Offset;
160 
161 struct _Efl_Class
162 {
163    Eo_Header header;
164 
165    const _Efl_Class *parent;
166    const Efl_Class_Description *desc;
167    Eo_Vtable vtable;
168 
169    const _Efl_Class **extensions;
170 
171    Eo_Extension_Data_Offset *extn_data_off;
172 
173    const _Efl_Class **mro;
174 
175    const Efl_Object_Property_Reflection_Ops *reflection;
176 
177    /* cached object for faster allocation */
178    struct {
179       Eina_Trash  *trash;
180       Eina_Spinlock    trash_lock;
181       unsigned int trash_count;
182    } objects;
183 
184    /* cached iterator for faster allocation cycle */
185    struct {
186       Eina_Trash   *trash;
187       Eina_Spinlock trash_lock;
188       unsigned int  trash_count;
189    } iterators;
190 
191    unsigned int obj_size; /**< size of an object of this class */
192    unsigned int class_id; /**< the id which can be used to find the slot in _eo_classes and vtables chains */
193    unsigned int data_offset; /* < Offset of the data within object data. */
194    unsigned int ops_count; /* < Offset of the data within object data. */
195 
196    Eina_Thread construction_thread; /** < the thread which called the class constructor */
197 
198    Eina_Bool constructed : 1;
199    Eina_Bool functions_set : 1;
200    /* [extensions*] + NULL */
201    /* [mro*] + NULL */
202    /* [extensions data offset] + NULL */
203 };
204 
205 typedef struct
206 {
207    EINA_INLIST;
208    const Eo *ref_obj;
209    const char *data_klass;
210    const char *file;
211    int line;
212 } Eo_Xref_Node;
213 
214 /* provide valgrind-like tracking of object allocationg and deletion */
215 void _eo_log_obj_report(const Eo_Id id, int log_level, const char *func_name, const char *file, int line);
216 
217 void _efl_object_reuse(_Eo_Object *obj);
218 
219 static inline
_eo_header_id_get(const Eo_Header * header)220 Eo *_eo_header_id_get(const Eo_Header *header)
221 {
222    return (Eo *) header->id;
223 }
224 
225 /* Retrieves the pointer to the object from the id */
226 _Eo_Object *_eo_obj_pointer_get(const Eo_Id obj_id, const char *func_name, const char *file, int line);
227 
228 static inline
_eo_class_id_get(const _Efl_Class * klass)229 Efl_Class *_eo_class_id_get(const _Efl_Class *klass)
230 {
231    return _eo_header_id_get((Eo_Header *) klass);
232 }
233 
234 static inline
_eo_obj_id_get(const _Eo_Object * obj)235 Eo *_eo_obj_id_get(const _Eo_Object *obj)
236 {
237    return _eo_header_id_get((Eo_Header *) obj);
238 }
239 
240 static inline void
_eo_condtor_reset(_Eo_Object * obj)241 _eo_condtor_reset(_Eo_Object *obj)
242 {
243    obj->condtor_done = EINA_FALSE;
244 }
245 
246 typedef struct _Efl_Object_Data Efl_Object_Data;
247 
248 EOLIAN void _efl_object_parent_set(Eo *obj, Efl_Object_Data *pd, Eo *parent_id);
249 void _efl_invalidate(_Eo_Object *obj);
250 
251 static inline void
_efl_del_internal(_Eo_Object * obj,const char * func_name,const char * file,int line)252 _efl_del_internal(_Eo_Object *obj, const char *func_name, const char *file, int line)
253 {
254    /* We need that for the event callbacks that may ref/unref. */
255    obj->refcount++;
256 
257    const _Efl_Class *klass = obj->klass;
258 
259    // If the object hasn't been invalidated yet, time to do it
260    // before any destructor kick in. This can happen when
261    // the object has no parent and get deleted by efl_unref.
262    if (obj->parent)
263      {
264         Eo *parent = efl_parent_get(_eo_obj_id_get(obj));
265 
266         ERR("Destructor path being taken while object [%s] still has a parent [%s] in state %i:%i.",
267             efl_debug_name_get(_eo_obj_id_get(obj)),
268             efl_debug_name_get(parent),
269             obj->is_invalidating, obj->invalidate);
270 
271         efl_parent_set(_eo_obj_id_get(obj), NULL);
272 
273         if (obj->parent)
274           {
275              CRI("Something is preventing [%s] from disconnecting from its parent, bypassing.",
276                  efl_debug_name_get(_eo_obj_id_get(obj)));
277              _efl_object_parent_set(_eo_obj_id_get(obj), efl_data_scope_get(_eo_obj_id_get(obj), EFL_OBJECT_CLASS), NULL);
278           }
279      }
280    else if (!obj->invalidate || !obj->is_invalidating)
281      {
282         _efl_invalidate(obj);
283      }
284 
285    efl_event_callback_call(_eo_obj_id_get(obj), EFL_EVENT_DEL, NULL);
286 
287    _eo_condtor_reset(obj);
288 
289    efl_destructor(_eo_obj_id_get(obj));
290 
291    if (!obj->condtor_done)
292      {
293         ERR("in %s:%d: func '%s' Object of class '%s' - Not all of the object destructors have been executed.",
294             file, line, func_name, klass->desc->name);
295      }
296    /*FIXME: add eo_class_unref(klass) ? - just to clear the caches. */
297 
298      {
299         Eina_List *itr, *itr_n;
300         Eo *emb_obj;
301         EINA_LIST_FOREACH_SAFE(obj->opt->composite_objects, itr, itr_n, emb_obj)
302           {
303              efl_composite_detach(_eo_obj_id_get(obj), emb_obj);
304           }
305      }
306 
307    obj->destructed = EINA_TRUE;
308    obj->refcount--;
309 }
310 
311 static inline Eina_Bool
_obj_is_override(_Eo_Object * obj)312 _obj_is_override(_Eo_Object *obj)
313 {
314    return obj->opt->vtable != NULL;
315 }
316 
317 void _eo_free(_Eo_Object *obj, Eina_Bool manual_free);
318 
319 static inline _Eo_Object *
_efl_ref(_Eo_Object * obj)320 _efl_ref(_Eo_Object *obj)
321 {
322    obj->refcount++;
323    return obj;
324 }
325 
326 #define _efl_unref(obj) _efl_unref_internal(obj, __func__, __FILE__, __LINE__)
327 static inline void
_efl_unref_internal(_Eo_Object * obj,const char * func_name,const char * file,int line)328 _efl_unref_internal(_Eo_Object *obj, const char *func_name, const char *file, int line)
329 {
330    --(obj->refcount);
331    if (EINA_UNLIKELY(obj->refcount <= 0))
332      {
333         if (obj->user_refcount > 0)
334           {
335              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);
336              _eo_log_obj_report((Eo_Id)_eo_obj_id_get(obj), EINA_LOG_LEVEL_ERR, __func__, __FILE__, __LINE__);
337              return;
338           }
339         if (obj->refcount < 0)
340           {
341              ERR("in %s:%d: func '%s' Obj:%p. Refcount (%d) < 0. Too many unrefs.", file, line, func_name, obj, obj->refcount);
342              _eo_log_obj_report((Eo_Id)_eo_obj_id_get(obj), EINA_LOG_LEVEL_ERR, __func__, __FILE__, __LINE__);
343              return;
344           }
345 
346         if (obj->destructed)
347           {
348              ERR("in %s:%d: func '%s' Object %p already destructed.", file, line, func_name, _eo_obj_id_get(obj));
349              _eo_log_obj_report((Eo_Id)_eo_obj_id_get(obj), EINA_LOG_LEVEL_ERR, __func__, __FILE__, __LINE__);
350              return;
351           }
352 
353         if (obj->del_triggered)
354           {
355              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));
356              _eo_log_obj_report((Eo_Id)_eo_obj_id_get(obj), EINA_LOG_LEVEL_ERR, __func__, __FILE__, __LINE__);
357              return;
358           }
359 
360         if (obj->opt->del_intercept)
361           {
362              Eo *obj_id = _eo_obj_id_get(obj);
363              efl_ref(obj_id);
364              obj->opt->del_intercept(obj_id);
365              return;
366           }
367 
368         obj->del_triggered = EINA_TRUE;
369 
370         _efl_del_internal(obj, func_name, file, line);
371 
372         if (EINA_LIKELY(!obj->manual_free))
373           {
374 #ifdef EO_DEBUG
375              /* If for some reason it's not empty, clear it. */
376              Eo *obj_id = _eo_obj_id_get(obj);
377              while (obj->xrefs)
378                {
379                   Eina_Inlist *nitr = obj->xrefs->next;
380                   Eo_Xref_Node *xref = EINA_INLIST_CONTAINER_GET(obj->xrefs, Eo_Xref_Node);
381                   ERR("in %s:%d: func '%s' Object %p is still referenced by object %p. Origin: %s:%d",
382                       file, line, func_name, obj_id, xref->ref_obj, xref->file, xref->line);
383                   eina_freeq_ptr_main_add(xref, free, sizeof(*xref));
384                   obj->xrefs = nitr;
385                }
386              while (obj->data_xrefs)
387                {
388                   Eina_Inlist *nitr = obj->data_xrefs->next;
389                   Eo_Xref_Node *xref = EINA_INLIST_CONTAINER_GET(obj->data_xrefs, Eo_Xref_Node);
390                   if (obj_id == xref->ref_obj)
391                     {
392                        WRN("in %s:%d: func '%s' Object %p still has a reference to its own data (subclass: %s). Origin: %s:%d",
393                            file, line, func_name, obj_id, xref->data_klass, xref->file, xref->line);
394                     }
395                   else
396                     {
397                        ERR("in %s:%d: func '%s' Data of object %p (subclass: %s) is still referenced by object %p. Origin: %s:%d",
398                            file, line, func_name, obj_id, xref->data_klass, xref->ref_obj, xref->file, xref->line);
399                     }
400 
401                   eina_freeq_ptr_main_add(xref, free, sizeof(*xref));
402                   obj->data_xrefs = nitr;
403                }
404 #endif
405 
406              _eo_free(obj, EINA_FALSE);
407           }
408         else
409           _efl_ref(obj); /* If we manual free, we keep a phantom ref. */
410      }
411 }
412 
413 #endif
414